Module:Multilingual Explained

local Multilingual = --[=[ Utilities for multilingual texts and ISO 639 (BCP47) issues etc. * fair * fallback * findCode * fix * format * getBase * getLang * getName * i18n * int * isLang * isLangWiki * isMinusculable * isRTL * message * sitelink * tabData * userLang * userLangCode * wikibase * failsafe loadData: Multilingual/config Multilingual/names ]=]local Failsafe = Multilinguallocal GlobalMod = Multilinguallocal GlobalData = Multilinguallocal User = Multilingual.globals.Multilingual = Multilingual.item

Multilingual.exotic = Multilingual.prefer =

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 rend -- 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 – 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 rend -- 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 rend -- 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 rend -- 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 not got and got ~= false 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 gotend -- 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 local r 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 if frame then Multilingual.frame = frame else Multilingual.frame = mw.getCurrentFrame end end f = function (a) return Multilingual.frame:expandTemplate end lucky, s = pcall(f, source) if lucky then r = s end end end return rend -- 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 rend -- 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 rend -- 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 else if Multilingual.prefer[s ] then table.insert(order, s) else table.insert(post, s) end 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.selfend -- 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 = 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 falseend -- 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 falseend -- 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 rend -- 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 falseend -- 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 rend -- 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 rend -- 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 = 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 = 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 rend -- 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 rend -- 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, r2end -- 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 falseend -- 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 rend -- 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 rend -- 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 rend -- 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 rend -- 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, r2end -- 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 "", r2end -- 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 rend -- Failsafe.failsafe

-- Exportlocal 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 rend -- 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 send -- 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 rend -- 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 Multilingualend -- p.Multilingual

return p