Module:Multilingual/sandbox

local Multilingual = { suite  = "Multilingual", serial = "2020-12-10", item	= 47541920, globals = { ISO15924 = 71584769, WLink	= 19363224 } } --[=[ Utilities for multilingual texts and ISO 639 (BCP47) issues etc. loadData: Multilingual/config Multilingual/names ]=] local Failsafe  = Multilingual local GlobalMod = Multilingual local GlobalData = Multilingual local User	  = { sniffer = "showpreview" } Multilingual.globals.Multilingual = Multilingual.item
 * fair
 * fallback
 * findCode
 * fix
 * format
 * getBase
 * getLang
 * getName
 * i18n
 * int
 * isLang
 * isLangWiki
 * isMinusculable
 * isRTL
 * message
 * sitelink
 * tabData
 * userLang
 * userLangCode
 * wikibase
 * failsafe

Multilingual.exotic = { simple = true, no	 = true } Multilingual.prefer = { cs = true, de = true, en = true, es = true, fr = true, it = true, nl = true, pt = true, ru = true, sv = true }

local foreignModule = function(access, advanced, append, alt, alert) -- Fetch global module -- Precondition: --	 access	-- string, with name of base module --	 advanced -- true, for require; else mw.loadData --	 append	-- string, with subpage part, if any; or false --	 alt	  -- number, of wikidata item of root; or false --	 alert	 -- true, for throwing error on data problem -- Postcondition: --	 Returns whatever, probably table -- 2020-01-01	local storage = access local finer = function if append then storage = string.format("%s/%s", storage, append) end end local fun, lucky, r, suited if advanced then fun = require else fun = mw.loadData end GlobalMod.globalModules = GlobalMod.globalModules or {} suited = GlobalMod.globalModules[access] if not suited then finer lucky, r = pcall(fun, "Module:" .. storage) end if not lucky then if not suited and type(alt) == "number" and alt > 0 then suited = string.format("Q%d", alt) suited = mw.wikibase.getSitelink(suited) GlobalMod.globalModules[access] = suited or true end if type(suited) == "string" then storage = suited finer lucky, r = pcall(fun, storage) end if not lucky and alert then error("Missing or invalid page: " .. storage) end end return r end -- foreignModule

local fetchData = function(access) -- Retrieve translated keyword from commons:Data:****.tab -- Precondition: --	 access -- string, with page identification on Commons --	 Returns table, with data, or string, with error message -- 2019-12-05	local storage = access local r	if type(storage) == "string" then local s		storage = mw.text.trim(storage) s = storage:lower if s:sub(1, 2) == "c:" then storage = mw.text.trim(storage:sub(3)) s = storage:lower elseif s:sub(1, 8) == "commons:" then storage = mw.text.trim(storage:sub(9)) s = storage:lower end if s:sub(1, 5) == "data:" then storage = mw.text.trim(storage:sub(6)) s = storage:lower end if s == "" or s == ".tab" then storage = false elseif s:sub(-4) == ".tab" then storage = storage:sub(1, -5) .. ".tab" else storage = storage .. ".tab" end end if type(storage) == "string" then local data if type(GlobalData.TabDATA) ~= "table" then GlobalData.TabDATA = {} end data = GlobalData.TabDATA[storage] if data then r = data else local lucky lucky, data = pcall(mw.ext.data.get, storage, "_") if type(data) == "table" then data = data.data if type(data) == "table" then GlobalData.TabDATA[storage] = data else r = string.format("%s %s%s",									  "INVALID Data:*.tab",									   "commons:Data:",									   storage) end else r = "BAD PAGE Data:*.tab &#8211; commons:" .. storage end if r then GlobalData.TabDATA[storage] = r				data = false else r = data end end else r = "BAD PAGE commons:Data:*.tab" end return r end -- fetchData

local favorites = function -- Provide fallback codes -- Postcondition: --	 Returns table with sequence of preferred languages --	 * ahead elements --	 * user (not yet accessible) --	 * page content language (not yet accessible) --	 * page name subpage --	 * project --	 * en	local r = Multilingual.polyglott if not r then local self = mw.language.getContentLanguage:getCode:lower local sub = mw.title.getCurrentTitle.subpageText local f	= function(add) local s = add for i = 1, #r do				if r[i] == s then s = false break -- for i				end end -- for i			if s then table.insert(r, s)			end end r = {} if sub:find("/", 2, true) then sub = sub:match("/(%l%l%l?)$") if sub then table.insert(r, sub) end elseif sub:find("^%l%l%l?%-?%a?%a?%a?%a?$") and mw.language.isSupportedLanguage(sub) then table.insert(r, sub) end f(self) f("en") Multilingual.polyglott = r	end return r end -- favorites

local feasible = function(ask, accept) -- Is ask to be supported by application? -- Precondition: --	 ask	 -- lowercase code --	 accept -- sequence table, with offered lowercase codes -- Postcondition: --	 nil, or true local r	for i = 1, #accept do		if accept[i] == ask then r = true break -- for i		end end -- for i	return r end -- feasible

local fetch = function(access, append) -- Attach config or library module -- Precondition: --	 access -- module title --	 append -- string, with subpage part of this; or false -- Postcondition: --	 Returns: table, with library, or false local got, sign if append then sign = string.format("%s/%s", access, append) else sign = access end if type(Multilingual.ext) ~= "table" then Multilingual.ext = {} end got = Multilingual.ext[sign] if got == nil then local global = Multilingual.globals[access] local lib = (not append or append == "config") got = foreignModule(access, lib, append, global) if type(got) == "table" then if lib then local startup = got[access] if type(startup) == "function" then got = startup end end else got = false end Multilingual.ext[sign] = got end return got end -- fetch

local fetchISO639 = function(access) -- Retrieve table from commons:Data:ISO639/***.tab -- Precondition: --	 access -- string, with subpage identification -- Postcondition: --	 Returns table, with data, even empty local r	if type(Multilingual.iso639) ~= "table" then Multilingual.iso639 = {} end r = Multilingual.iso639[access] if type(r) == "nil" then local raw = fetchData("ISO639/" .. access) if type(raw) == "table" then local t			r = {} for i = 1, #raw do				t = raw[i] if type(t) == "table" and type(t[1]) == "string" and type(t[2]) == "string" then r[t[1]] = t[2] else break -- for i				end end -- for i		else r = false end Multilingual.iso639[access] = r	end return r or {} end -- fetchISO639

local fill = function(access, alien, frame) -- Expand language name template -- Precondition: --	 access -- string, with language code --	 alien  -- language code for which to be generated --	 frame  -- frame, if available -- Postcondition: --	 Returns string local template = Multilingual.tmplLang if type(template) ~= "table" then local cnf = fetch("Multilingual", "config") if cnf then template = cnf.tmplLang end end if type(template) == "table" then local source = template.title local f, lucky, s		Multilingual.tmplLang = template if type(source) ~= "string" and type(template.namePat) == "string" and template.namePat:find("%s", 1, true) then source = string.format(template.namePat, access) end if type(source) == "string" then if not Multilingual.frame then Multilingual.frame = frame or mw.getCurrentFrame end f = function(a) return Multilingual.frame:expandTemplate{ title = a } end lucky, s = pcall(f, source) if lucky then return s			end end end return nil end -- fill

local find = function(ask, alien) -- Derive language code from name -- Precondition: --	 ask	-- language name, downcased --	 alien -- language code of ask -- Postcondition: --	 nil, or string local codes = mw.language.fetchLanguageNames(alien, "all") local r	for k, v in pairs(codes) do		if mw.ustring.lower(v) == ask then r = k			break -- for k, v		end end -- for k, v	if not r then r = Multilingual.fair(ask) end return r end -- find

local fold = function(frame) -- Merge template and #invoke arglist -- Precondition: --	 frame  -- template frame -- Postcondition: --	 table, with combined arglist local r = {} local f = function(apply) if type(apply) == "table" and type(apply.args) == "table" then for k, v in pairs(apply.args) do				v = mw.text.trim(v) if v ~= "" then r[tostring(k)] = v				end end -- for k, v		end end -- f f(frame:getParent) f(frame) return r end -- fold

User.favorize = function(accept, frame) -- Guess user language -- Precondition: --	 accept -- sequence table, with offered ISO 639 etc. codes --	 frame  -- frame, if available -- Postcondition: --	 Returns string with best code, or nil if not (User.self or User.langs) then if not User.trials then User.tell = mw.message.new(User.sniffer) if User.tell:exists then User.trials = {} if not Multilingual.frame then if frame then Multilingual.frame = frame else Multilingual.frame = mw.getCurrentFrame end end User.sin = Multilingual.frame:callParserFunction("int",														  User.sniffer) else User.langs = true end end if User.sin then local order = {} local post  = {} local three = {} local unfold = {} local s, sin for i = 1, #accept do				s = accept[i] if not User.trials[s] then if #s > 2 then if s:find("-", 3, true) then table.insert(unfold, s)						else table.insert(three, s)						end elseif Multilingual.prefer[s] then table.insert(order, s)					else table.insert(post, s)					end end end -- for i			for i = 1, #post do				table.insert(order, post[i]) end -- for i			for i = 1, #three do				table.insert(order, three[i]) end -- for i			for i = 1, #unfold do				table.insert(order, unfold[i]) end -- for i			for i = 1, #order do				s = order[i] sin = User.tell:inLanguage(s):plain if sin == User.sin then User.self = s					break -- for i				else User.trials[s] = true end end -- for i		end end return User.self end -- User.favorize

Multilingual.fair = function(ask) -- Format language specification according to RFC 5646 etc.	-- Precondition: --	 ask -- string or table, as created by .getLang -- Postcondition: --	 Returns string, or false local s = type(ask) local q, r	if s == "table" then q = ask elseif s == "string" then q = Multilingual.getLang(ask) end if q and q.legal and mw.language.isKnownLanguageTag(q.base) then r = q.base if q.n > 1 then local order = { "extlang", "script", "region", "other", "extension" } for i = 1, #order do				s = q[order[i]] if s then r = string.format("%s-%s", r, s)				end end -- for i		end end return r or false end -- Multilingual.fair

Multilingual.fallback = function(able, another) -- Is another language suitable as replacement? -- Precondition: --	 able	 -- language version specifier to be supported --	 another -- language specifier of a possible replacement, --				 or not to retrieve a fallback table -- Postcondition: --	 Returns boolean, or table with fallback codes local r	if type(able) == "string" and #able > 0 then if type(another) == "string" and #another > 0 then if able == another then r = true else local s = Multilingual.getBase(able) if s == another then r = true else local others = mw.language.getFallbacksFor(s) r = feasible(another, others) end end else local s = Multilingual.getBase(able) if s then r = mw.language.getFallbacksFor(s) if r[1] == "en" then local d = fetchISO639("fallback") if type(d) == "table" and type(d[s]) == "string" then r = mw.text.split(d[s], "|") table.insert(r, "en") end end end end end return r or false end -- Multilingual.fallback

Multilingual.findCode = function(ask) -- Retrieve code of local (current project or English) language name -- Precondition: --	 ask -- string, with presumable language name --			 A code itself will be identified, too. -- Postcondition: --	 Returns string, or false local seek = mw.text.trim(ask) local r = false if #seek > 1 then if seek:find("[", 1, true) then local wlink = fetch("WLink") if wlink and type(wlink.getPlain) == "function" then seek = wlink.getPlain(seek) end end seek = mw.ustring.lower(seek) if Multilingual.isLang(seek) then r = Multilingual.fair(seek) else local collection = favorites for i = 1, #collection do				r = find(seek, collection[i]) if r then break -- for i				end end -- for i		end end return r end -- Multilingual.findCode

Multilingual.fix = function(attempt) -- Fix frequently mistaken language code -- Precondition: --	 attempt -- string, with presumable language code -- Postcondition: --	 Returns string with correction, or false if no problem known local r = fetchISO639("correction")[attempt:lower] return r or false end -- Multilingual.fix

Multilingual.format = function(apply, alien, alter, active, alert,								 frame, assembly, adjacent, ahead) -- Format one or more languages -- Precondition: --	 apply	 -- string with language list or item --	 alien	 -- language of the answer --				 -- nil, false, "*": native --				 -- "!": current project --				 -- "#": code, downcased, space separated --				 -- "-": code, mixcase, space separated --				 -- any valid code --	 alter	 -- capitalize, if "c"; downcase all, if "d" --				 capitalize first item only, if "f" --				 downcase every first word only, if "m" --	 active	-- link items, if true --	 alert	 -- string with category title in case of error --	 frame	 -- if available --	 assembly -- string with split pattern, if list expected --	 adjacent -- string with list separator, else assembly --	 ahead	 -- string to prepend first element, if any -- Postcondition: --	 Returns string, or false if apply empty local r = false if apply then local slang if assembly then local bucket = mw.text.split(apply, assembly) local shift = alter local separator if adjacent then separator = adjacent elseif alien == "#" or alien == "-" then separator = " " else separator = assembly end for k, v in pairs(bucket) do				slang = Multilingual.format(v, alien, shift, active,											 alert) if slang then if r then r = string.format("%s%s%s",										  r, separator, slang) else r = slang if shift == "f" then shift = "d" end end end end -- for k, v			if r and ahead then r = ahead .. r			end else local single = mw.text.trim(apply) if single == "" then r = false else local lapsus, slot slang = Multilingual.findCode(single) if slang then if alien == "-" then r = slang elseif alien == "#" then r = slang:lower else r = Multilingual.getName(slang, alien) if active then slot = fill(slang, false, frame) if slot then local wlink = fetch("WLink") if wlink and type(wlink.getTarget) == "function" then slot = wlink.getTarget(slot) end else lapsus = alert end end end else r = single if active then local title = mw.title.makeTitle(0, single) if title.exists then slot = single end end lapsus = alert end if not r then r = single elseif alter == "c" or alter == "f" then r = mw.ustring.upper(mw.ustring.sub(r, 1, 1)) .. mw.ustring.sub(r, 2) elseif alter == "d" then if Multilingual.isMinusculable(slang, r) then r = mw.ustring.lower(r) end elseif alter == "m" then if Multilingual.isMinusculable(slang, r) then r = mw.ustring.lower(mw.ustring.sub(r, 1, 1)) .. mw.ustring.sub(r, 2) end end if slot then if r == slot then r = string.format("%s", r)					else r = string.format("%s", slot, r)					end end if lapsus and alert then r = string.format("%s", r, alert) end end end end return r end -- Multilingual.format

Multilingual.getBase = function(ask) -- Retrieve base language from possibly combined ISO language code -- Precondition: --	 ask -- language code -- Postcondition: --	 Returns string, or false local r	if ask then local slang = ask:match("^%s*(%a%a%a?)-?%a*%s*$") if slang then r = slang:lower else r = false end else r = false end return r end -- Multilingual.getBase

Multilingual.getLang = function(ask) -- Retrieve components of a RFC 5646 language code -- Precondition: --	 ask -- language code with subtags -- Postcondition: --	 Returns table with formatted subtags --			 .base --			 .region --			 .script --			 .suggest --			 .year --			 .extension --			 .other --			 .n	local tags = mw.text.split(ask, "-") local s	= tags[1] local r	if s:match("^%a%a%a?$") then r = { base = s:lower, legal = true, n	 = #tags } for i = 2, r.n do			s = tags[i] if #s == 2 then if r.region or not s:match("%a%a") then r.legal = false else r.region = s:upper end elseif #s == 4 then if s:match("%a%a%a%a") then r.legal = (not r.script) r.script = s:sub(1, 1):upper .. s:sub(2):lower elseif s:match("20%d%d") or					  s:match("1%d%d%d") then r.legal = (not r.year) r.year = s				else r.legal = false end elseif #s == 3 then if r.extlang or not s:match("%a%a%a") then r.legal = false else r.extlang = s:lower end elseif #s == 1 then s = s:lower if s:match("[tux]") then r.extension = s					for k = i + 1, r.n do						s = tags[k] if s:match("^%w+$") then r.extension = string.format("%s-%s",														 r.extension, s)						else r.legal = false end end -- for k				else r.legal = false end break -- for i			else r.legal = (not r.other) and s:match("%a%a%a") r.other = s:lower end if not r.legal then break -- for i			end end -- for i		if r.legal then r.suggest = Multilingual.fix(r.base) if r.suggest then r.legal = false end end else r = { legal = false } end if not r.legal then local cnf = fetch("Multilingual", "config") if cnf and type(cnf.scream) == "string" then r.scream = cnf.scream end end return r end -- Multilingual.getLang

Multilingual.getName = function(ask, alien) -- Which name is assigned to this language code? -- Precondition: --	 ask	-- language code --	 alien -- language of the answer --			  -- nil, false, "*": native --			  -- "!": current project --			  -- any valid code -- Postcondition: --	 Returns string, or false local r	if ask then local slang  = alien local tLang if slang then if slang == "*" then slang = Multilingual.fair(ask) elseif slang == "!" then slang = favorites[1] else slang = Multilingual.fair(slang) end else slang = Multilingual.fair(ask) end if not slang then slang = ask or "?????" end slang = slang:lower tLang = fetch("Multilingual", "names") if tLang then tLang = tLang[slang] if tLang then r = tLang[ask] end end if not r then if not Multilingual.ext.tMW then Multilingual.ext.tMW = {} end tLang = Multilingual.ext.tMW[slang] if tLang == nil then tLang = mw.language.fetchLanguageNames(slang) if tLang then Multilingual.ext.tMW[slang] = tLang else Multilingual.ext.tMW[slang] = false end end if tLang then r = tLang[ask] end end if not r then r = mw.language.fetchLanguageName(ask:lower, slang) if r == "" then r = false end end else r = false end return r end -- Multilingual.getName

Multilingual.i18n = function(available, alt, frame) -- Select translatable message -- Precondition: --	 available -- table, with mapping language code ./. text --	 alt		-- string|nil|false, with fallback text --	 frame	 -- frame, if available --	 Returns --		 1. string|nil|false, with selected message --		 2. string|nil|false, with language code local r1, r2	if type(available) == "table" then local codes = {} local trsl = {} local slang for k, v in pairs(available) do			if type(k) == "string" and type(v) == "string" then slang = mw.text.trim(k:lower) table.insert(codes, slang) trsl[slang] = v			end end -- for k, v		slang = Multilingual.userLang(codes, frame) if slang and trsl[slang] then r1 = mw.text.trim(trsl[slang]) if r1 == "" then r1 = false else r2 = slang end end end if not r1 and type(alt) == "string" then r1 = mw.text.trim(alt) if r1 == "" then r1 = false end end return r1, r2 end -- Multilingual.i18n

Multilingual.int = function(access, alien, apply) -- Translated system message -- Precondition: --	 access -- message ID	--	 alien   -- language code --	 apply  -- nil, or sequence table with parameters $1, $2, ... -- Postcondition: --	 Returns string, or false local o = mw.message.new(access) local r	if o:exists then if type(alien) == "string" then o:inLanguage(alien:lower) end if type(apply) == "table" then o:params(apply) end r = o:plain end return r or false end -- Multilingual.int

Multilingual.isLang = function(ask, additional) -- Could this be an ISO language code? -- Precondition: --	 ask		 -- language code --	 additional -- true, if Wiki codes like "simple" permitted -- Postcondition: --	 Returns boolean local r, s	if additional then s = ask else s = Multilingual.getBase(ask) end if s then r = mw.language.isKnownLanguageTag(s) if r then r = not Multilingual.fix(s) elseif additional then r = Multilingual.exotic[s] or false end else r = false end return r end -- Multilingual.isLang

Multilingual.isLangWiki = function(ask) -- Could this be a Wiki language version? -- Precondition: --	 ask -- language version specifier -- Postcondition: --	 Returns boolean local r	local s = Multilingual.getBase(ask) if s then r = mw.language.isSupportedLanguage(s) or			Multilingual.exotic[ask] else r = false end return r end -- Multilingual.isLangWiki

Multilingual.isMinusculable = function(ask, assigned) -- Could this language name become downcased? -- Precondition: --	 ask	  -- language code, or nil --	 assigned -- language name, or nil -- Postcondition: --	 Returns boolean local r = true if ask then local cnf = fetch("Multilingual", "config") if cnf then local s = string.format(" %s ", ask:lower) if type(cnf.stopMinusculization) == "string" and cnf.stopMinusculization:find(s, 1, true) then r = false end if r and assigned and type(cnf.seekMinusculization) == "string" and cnf.seekMinusculization:find(s, 1, true) and type(cnf.scanMinusculization) == "string" then local scan = assigned:gsub("[%(%)]", " ") .. " "				if not scan:find(cnf.scanMinusculization) then r = false end end end end return r end -- Multilingual.isMinusculable

Multilingual.isRTL = function(ask) -- Check whether language is written right-to-left -- Precondition: --	 ask -- string, with language (or script) code -- Returns true, if right-to-left local r	Multilingual.rtl = Multilingual.rtl or {} r = Multilingual.rtl[ask] if type(r) ~= "boolean" then local bib = fetch("ISO15924") if type(bib) == "table" and type(bib.isRTL) == "function" then r = bib.isRTL(ask) else r = mw.language.new(ask):isRTL end Multilingual.rtl[ask] = r	end return r end -- Multilingual.isRTL

Multilingual.message = function(arglist, frame) -- Show text in best match of user language like system message -- Precondition: --	 arglist -- template arguments --	 frame	-- frame, if available -- Postcondition: --	 Returns string with appropriate text local r	if type(arglist) == "table" then local t = {} local m, p, save for k, v in pairs(arglist) do			if type(k) == "string" and type(v) == "string" then v = mw.text.trim(v) if v ~= "" then if k:match("^%l%l") then t[k] = v					elseif k:match("^%$%d$") and k ~= "$0" then p = p or {} k = tonumber(k:match("^%$(%d)$")) p[k] = v						if not m or k > m then m = k						end end end end end -- for k, v		if type(arglist["-"]) == "string" then save = arglist[arglist["-"]] end r = Multilingual.i18n(t, save, frame) if p and r and r:find("$", 1, true) then t = {} for i = 1, m do				t[i] = p[i] or "" end -- for i			r = mw.message.newRawMessage(r, t):plain end end return r or "" end -- Multilingual.message

Multilingual.sitelink = function(all, frame) -- Make link at local or other site with optimal linktext translation -- Precondition: --	 all	-- string or table or number, item ID or entity --	 frame -- frame, if available -- Postcondition: --	 Returns string with any helpful internal link, or plain text local s = type(all) local object, r	if s == "table" then object = all elseif s == "string" then object = mw.wikibase.getEntity(all) elseif s == "number" then object = mw.wikibase.getEntity(string.format("Q%d", all)) end if type(object) == "table" then local collection = object.sitelinks local entry s = false if type(collection) == "table" then Multilingual.site = Multilingual.site or								mw.wikibase.getGlobalSiteId entry = collection[Multilingual.site] if entry then s = ":" .. entry.title elseif collection.enwiki then s = "w:en:" .. collection.enwiki.title end end r = Multilingual.wikibase(object, "labels", frame) if s then if s == ":" .. r then r = string.format("%s", s)			else r = string.format("%s", s, r)			end end end return r or "" end -- Multilingual.sitelink

Multilingual.tabData = function(access, at, alt, frame) -- Retrieve translated keyword from commons:Data:****.tab -- Precondition: --	 access -- string, with page identification on Commons --	 at	 -- string, with keyword --	 alt	 -- string|nil|false, with fallback text --	 frame  -- frame, if available --	 Returns --		 1. string|nil|false, with selected message --		 2. language code, or "error" local data = fetchData(access) local r1, r2	if type(data) == "table" then if type(at) == "string" then local seek = mw.text.trim(at) if seek == "" then r1 = "EMPTY Multilingual.tabData key" else local e, poly for i = 1, #data do					e = data[i] if type(e) == "table" then if e[1] == seek then if type(e[2]) == "table" then poly = e[2] else r1 = "INVALID Multilingual.tabData bad #" .. tostring(i) end break  -- for i						end else break  -- for i					end end  -- for i				if poly then data = poly else r1 = "UNKNOWN Multilingual.tabData key: " .. seek end end else r1 = "INVALID Multilingual.tabData key" end else r1 = data end if r1 then r2 = "error" elseif data then r1, r2 = Multilingual.i18n(data, alt, frame) r2 = r2 or "error" end return r1, r2 end -- Multilingual.tabData

Multilingual.userLang = function(accept, frame) -- Try to support user language by application -- Precondition: --	 accept -- string or table --				space separated list of available ISO 639 codes --				Default: project language, or English --	 frame  -- frame, if available -- Postcondition: --	 Returns string with appropriate code local s = type(accept) local codes, r, slang if s == "string" then codes = mw.text.split(accept:lower, "%s+") elseif s == "table" then codes = {} for i = 1, #accept do			s = accept[i] if type(s) == "string" and s ~= "" then table.insert(codes, s:lower) end end -- for i	end slang = User.favorize(codes, frame) if slang then if feasible(slang, codes) then r = slang elseif slang:find("-", 1, true) then slang = Multilingual.getBase(slang) if feasible(slang, codes) then r = slang end end if not r then local others = mw.language.getFallbacksFor(slang) for i = 1, #others do				slang = others[i] if feasible(slang, codes) then r = slang break -- for i				end end -- for i		end end if not r then local back = favorites for i = 1, #back do			slang = back[i] if feasible(slang, codes) then r = slang break -- for i			end end -- for i		if not r and codes[1] then r = codes[1] end end return r or favorites[1] end -- Multilingual.userLang

Multilingual.userLangCode = function -- Guess a user language code -- Postcondition: --	 Returns code of current best guess return User.self or favorites[1] end -- Multilingual.userLangCode

Multilingual.wikibase = function(all, about, attempt, frame) -- Optimal translation of wikibase component -- Precondition: --	 all	 -- string or table, object ID or entity --	 about	-- boolean, true "descriptions" or false "labels" --	 attempt -- string or not, code of preferred language --	 frame	-- frame, if available -- Postcondition: --	 Returns --		 1. string, with selected message --		 2. string, with language code, or not local s = type(all) local object, r, r2	if s == "table" then object = all elseif s == "string" then object = mw.wikibase.getEntity(all) end if type(object) == "table" then if about and about ~= "labels" then s = "descriptions" else s = "labels" end object = object[s] if type(object) == "table" then if object[attempt] then r = object[attempt].value r2 = attempt else local poly for k, v in pairs(object) do					poly = poly or {} poly[k] = v.value end -- for k, v				if poly then r, r2 = Multilingual.i18n(poly, nil, frame) end end end end return r or "",  r2 end -- Multilingual.wikibase

Failsafe.failsafe = function(atleast) -- Retrieve versioning and check for compliance -- Precondition: --	 atleast -- string, with required version --						 or wikidata|item|~|@ or false -- Postcondition: --	 Returns string  -- with queried version/item, also if problem --			 false   -- if appropriate -- 2020-08-17	local since = atleast local last	= (since == "~") local linked = (since == "@") local link	= (since == "item") local r	if last or link or linked or since == "wikidata" then local item = Failsafe.item since = false if type(item) == "number" and item > 0 then local suited = string.format("Q%d", item) if link then r = suited else local entity = mw.wikibase.getEntity(suited) if type(entity) == "table" then local seek = Failsafe.serialProperty or "P348" local vsn = entity:formatPropertyValues(seek) if type(vsn) == "table" and type(vsn.value) == "string" and vsn.value ~= "" then if last and vsn.value == Failsafe.serial then r = false elseif linked then if mw.title.getCurrentTitle.prefixedText == mw.wikibase.getSitelink(suited) then r = false else r = suited end else r = vsn.value end end end end end end if type(r) == "nil" then if not since or since <= Failsafe.serial then r = Failsafe.serial else r = false end end return r end -- Failsafe.failsafe

-- Export local p = {}

p.fair = function(frame) -- Format language code --	 1 -- language code local s = mw.text.trim(frame.args[1] or "") return Multilingual.fair(s) or "" end -- p.fair

p.fallback = function(frame) -- Is another language suitable as replacement? --	 1 -- language version specifier to be supported --	 2 -- language specifier of a possible replacement local s1 = mw.text.trim(frame.args[1] or "") local s2 = mw.text.trim(frame.args[2] or "") local r = Multilingual.fallback(s1, s2) if type(r) == "table" then r = r[1] else r = r and "1" or "" end return r end -- p.fallback

p.findCode = function(frame) -- Retrieve language code from language name --	 1 -- name in current project language local s = mw.text.trim(frame.args[1] or "") return Multilingual.findCode(s) or "" end -- p.findCode

p.fix = function(frame) local r = frame.args[1] if r then r = Multilingual.fix(mw.text.trim(r)) end return r or "" end -- p.fix

p.format = function(frame) -- Format one or more languages --	 1		 -- language list or item --	 slang	 -- language of the answer, if not native --				  * -- native --				  ! -- current project --				  any valid code --	 shift	 -- capitalize, if "c"; downcase, if "d" --				  capitalize first item only, if "f" --	 link	  -- 1 -- link items --	 scream	 -- category title in case of error --	 split	 -- split pattern, if list expected --	 separator -- list separator, else split --	 start	 -- prepend first element, if any local r	local link if frame.args.link == "1" then link = true end r = Multilingual.format(frame.args[1],							 frame.args.slang,							 frame.args.shift,							 link,							 frame.args.scream,							 frame,							 frame.args.split,							 frame.args.separator,							 frame.args.start) return r or "" end -- p.format

p.getBase = function(frame) -- Retrieve base language from possibly combined ISO language code --	 1 -- code local s = mw.text.trim(frame.args[1] or "") return Multilingual.getBase(s) or "" end -- p.getBase

p.getName = function(frame) -- Retrieve language name from ISO language code --	 1 -- code --	 2 -- language to be used for the answer, if not native --		  ! -- current project --		  * -- native --		  any valid code local s	 = mw.text.trim(frame.args[1] or "") local slang = frame.args[2] local r	Multilingual.frame = frame if slang then slang = mw.text.trim(slang) end r = Multilingual.getName(s, slang) return r or "" end -- p.getName

p.int = function(frame) -- Translated system message --	 1			 -- message ID	--	 lang		 -- language code --	 $1, $2, ...  -- parameters local sysMsg = frame.args[1] local r	if sysMsg then sysMsg = mw.text.trim(sysMsg) if sysMsg ~= "" then local n	 = 0 local slang = frame.args.lang local i, params, s			if slang == "" then slang = false end for k, v in pairs(frame.args) do				if type(k) == "string" then s = k:match("^%$(%d+)$") if s then i = tonumber(s) if i > n then n = i						end end end end -- for k, v			if n > 0 then local s				params = {} for i = 1, n do s = frame.args["$" .. tostring(i)] or "" table.insert(params, s)				end -- for i			end r = Multilingual.int(sysMsg, slang, params) end end return r or "" end -- p.int

p.isLang = function(frame) -- Could this be an ISO language code? --	 1 -- code local s = mw.text.trim(frame.args[1] or "") local lucky, r = pcall(Multilingual.isLang, s)	return r and "1" or "" end -- p.isLang

p.isLangWiki = function(frame) -- Could this be a Wiki language version? --	 1 -- code -- Returns non-empty, if possibly language version local s = mw.text.trim(frame.args[1] or "") local lucky, r = pcall(Multilingual.isLangWiki, s)	return r and "1" or "" end -- p.isLangWiki

p.isRTL = function(frame) -- Check whether language is written right-to-left --	 1 -- string, with language code -- Returns non-empty, if right-to-left local s = mw.text.trim(frame.args[1] or "") return Multilingual.isRTL(s) and "1" or "" end -- p.isRTL

p.message = function(frame) -- Translation of text element return Multilingual.message(fold(frame), frame) end -- p.message

p.sitelink = function(frame) -- Make link at local or other site with optimal linktext translation --	 1 -- item ID	local s = mw.text.trim(frame.args[1] or "") local r	if s:match("^%d+$") then r = tonumber(s) elseif s:match("^Q%d+$") then r = s	end if r then r = Multilingual.sitelink(r, frame) end return r or s end -- p.sitelink

p.tabData = function(frame) -- Retrieve best message text from Commons Data --	 1	-- page identification on Commons --	 2	-- keyword --	 alt -- fallback text local suite = frame.args[1] local seek = frame.args[2] local salt = frame.args.alt local r	 = Multilingual.tabData(suite, seek, salt, frame) return r end -- p.tabData

p.userLang = function(frame) -- Which language does the current user prefer? --	 1 -- space separated list of available ISO 639 codes local s = mw.text.trim(frame.args[1] or "") return Multilingual.userLang(s, frame) end -- p.userLang

p.wikibase = function(frame) -- Optimal translation of wikibase component --	 1 -- object ID --	 2 -- 1 for "descriptions", 0 for "labels". --		 or either "descriptions" or "labels" local r	local s = mw.text.trim(frame.args[1] or "") if s ~= "" then local s2 = mw.text.trim(frame.args[2] or "0") local slang = mw.text.trim(frame.args.lang or "") local large = (s2 ~= "" and s2 ~= "0") if slang == "" then slang = false end r = Multilingual.wikibase(s, large, slang, frame) end return r or "" end -- p.wikibase

p.failsafe = function(frame) -- Versioning interface local s = type(frame) local since if s == "table" then since = frame.args[1] elseif s == "string" then since = frame end if since then since = mw.text.trim(since) if since == "" then since = false end end return Failsafe.failsafe(since) or "" end -- p.failsafe

p.Multilingual = function return Multilingual end -- p.Multilingual

return p