From Wikipedia, the free encyclopedia

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.

* 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   = Multilingual

local GlobalMod  = Multilingual

local GlobalData = Multilingual

local User	   = { sniffer = "showpreview" }

Multilingual.globals.Multilingual = Multilingual.item







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.globalModulesaccess

	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.globalModulesaccess = 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.TabDATAstorage

		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.TabDATAstorage = 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.TabDATAstorage = 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 ri == 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 accepti == 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.extsign

	if got == nil then

		local global = Multilingual.globalsaccess

		local lib = (not append or append == "config")

		got = foreignModule(access, lib, append, global)

		if type(got) == "table" then

			if lib then

				local startup = gotaccess

				if type(startup) == "function" then

					got = startup()

				end

			end

		else

			got = false

		end

		Multilingual.extsign = 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.iso639access

	if type(r) == "nil" then

		local raw = fetchData("ISO639/" .. access)

		if type(raw) == "table" then

			local t

			r = {}

			for i = 1, #raw do

				t = rawi

				if type(t) == "table" and

				   type(t1]) == "string" and

				   type(t2]) == "string" then

					rt1]] = t2

				else

					break -- for i

				end

			end -- for i

		else

			r = false

		end

		Multilingual.iso639access = 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

					rtostring(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 = accepti

				if not User.trialss then

					if #s > 2 then

						if s:find("-", 3, true) then

							table.insert(unfold, s)

						else

							table.insert(three, s)

						end

					elseif Multilingual.prefers then

						table.insert(order, s)

					else

						table.insert(post, s)

					end

				end

			end -- for i

			for i = 1, #post do

				table.insert(order, posti])

			end -- for i

			for i = 1, #three do

				table.insert(order, threei])

			end -- for i

			for i = 1, #unfold do

				table.insert(order, unfoldi])

			end -- for i

			for i = 1, #order do

				s = orderi

				sin = User.tell:inLanguage(s):plain()

				if sin == User.sin then

					User.self = s

					break -- for i

				else

					User.trialss = 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 = qorderi]]

				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 r1 == "en" then

					local d = fetchISO639("fallback")

					if type(d) == "table" and

					   type(ds]) == "string" then

						r = mw.text.split(ds], "|")

						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, collectioni])

				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|%s]]", slot, r)

					end

				end

				if lapsus and alert then

					r = string.format("%s[[Category:%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	= tags1

	local r

	if s:match("^%a%a%a?$") then

		r = { base  = s:lower(),

			  legal = true,

			  n	 = #tags }

		for i = 2, r.n do

			s = tagsi

			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 = tagsk

						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 = tLangslang

			if tLang then

				r = tLangask

			end

		end

		if not r then

			if not Multilingual.ext.tMW then

				Multilingual.ext.tMW = {}

			end

			tLang = Multilingual.ext.tMWslang

			if tLang == nil then

				tLang = mw.language.fetchLanguageNames(slang)

				if tLang then

					Multilingual.ext.tMWslang = tLang

				else

					Multilingual.ext.tMWslang = false

				end

			end

			if tLang then

				r = tLangask

			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)

				trslslang = v

			end

		end -- for k, v

		slang = Multilingual.userLang(codes, frame)

		if slang and trslslang then

			r1 = mw.text.trim(trslslang])

			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.exotics 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.exoticask

	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.rtlask

	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.rtlask = 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

						tk = v

					elseif k:match("^%$%d$") and k ~= "$0" then

						p = p or {}

						k = tonumber(k:match("^%$(%d)$"))

						pk = v

						if not m or k > m then

							m = k

						end

					end

				end

			end

		end -- for k, v

		if type(arglist"-"]) == "string" then

			save = arglistarglist"-"]]

		end

		r = Multilingual.i18n(t, save, frame)

		if p and r and r:find("$", 1, true) then

			t = {}

			for i = 1, m do

				ti = pi 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 = collectionMultilingual.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]]", 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 = datai

					if type(e) == "table" then

						if e1 == seek then

							if type(e2]) == "table" then

								poly = e2

							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 = accepti

			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 = othersi

				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 = backi

			if feasible(slang, codes) then

				r = slang

				break -- for i

			end

		end -- for i

		if not r and codes1 then

			r = codes1

		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 = objects

		if type(object) == "table" then

			if objectattempt then

				r  = objectattempt].value

				r2 = attempt

			else

				local poly

				for k, v in pairs(object) do

					poly = poly or {}

					polyk = 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.args1 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.args1 or "")

	local s2 = mw.text.trim(frame.args2 or "")

	local r  = Multilingual.fallback(s1, s2)

	if type(r) == "table" then

		r = r1

	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.args1 or "")

	return Multilingual.findCode(s) or ""

end -- p.findCode



p.fix = function(frame)

	local r = frame.args1

	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.args1],

							 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.args1 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.args1 or "")

	local slang = frame.args2

	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.args1

	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.args1 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.args1 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.args1 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.args1 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.args1

	local seek  = frame.args2

	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.args1 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.args1 or "")

	if s ~= "" then

		local s2 = mw.text.trim(frame.args2 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.args1

	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
From Wikipedia, the free encyclopedia

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.

* 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   = Multilingual

local GlobalMod  = Multilingual

local GlobalData = Multilingual

local User	   = { sniffer = "showpreview" }

Multilingual.globals.Multilingual = Multilingual.item







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.globalModulesaccess

	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.globalModulesaccess = 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.TabDATAstorage

		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.TabDATAstorage = 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.TabDATAstorage = 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 ri == 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 accepti == 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.extsign

	if got == nil then

		local global = Multilingual.globalsaccess

		local lib = (not append or append == "config")

		got = foreignModule(access, lib, append, global)

		if type(got) == "table" then

			if lib then

				local startup = gotaccess

				if type(startup) == "function" then

					got = startup()

				end

			end

		else

			got = false

		end

		Multilingual.extsign = 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.iso639access

	if type(r) == "nil" then

		local raw = fetchData("ISO639/" .. access)

		if type(raw) == "table" then

			local t

			r = {}

			for i = 1, #raw do

				t = rawi

				if type(t) == "table" and

				   type(t1]) == "string" and

				   type(t2]) == "string" then

					rt1]] = t2

				else

					break -- for i

				end

			end -- for i

		else

			r = false

		end

		Multilingual.iso639access = 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

					rtostring(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 = accepti

				if not User.trialss then

					if #s > 2 then

						if s:find("-", 3, true) then

							table.insert(unfold, s)

						else

							table.insert(three, s)

						end

					elseif Multilingual.prefers then

						table.insert(order, s)

					else

						table.insert(post, s)

					end

				end

			end -- for i

			for i = 1, #post do

				table.insert(order, posti])

			end -- for i

			for i = 1, #three do

				table.insert(order, threei])

			end -- for i

			for i = 1, #unfold do

				table.insert(order, unfoldi])

			end -- for i

			for i = 1, #order do

				s = orderi

				sin = User.tell:inLanguage(s):plain()

				if sin == User.sin then

					User.self = s

					break -- for i

				else

					User.trialss = 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 = qorderi]]

				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 r1 == "en" then

					local d = fetchISO639("fallback")

					if type(d) == "table" and

					   type(ds]) == "string" then

						r = mw.text.split(ds], "|")

						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, collectioni])

				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|%s]]", slot, r)

					end

				end

				if lapsus and alert then

					r = string.format("%s[[Category:%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	= tags1

	local r

	if s:match("^%a%a%a?$") then

		r = { base  = s:lower(),

			  legal = true,

			  n	 = #tags }

		for i = 2, r.n do

			s = tagsi

			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 = tagsk

						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 = tLangslang

			if tLang then

				r = tLangask

			end

		end

		if not r then

			if not Multilingual.ext.tMW then

				Multilingual.ext.tMW = {}

			end

			tLang = Multilingual.ext.tMWslang

			if tLang == nil then

				tLang = mw.language.fetchLanguageNames(slang)

				if tLang then

					Multilingual.ext.tMWslang = tLang

				else

					Multilingual.ext.tMWslang = false

				end

			end

			if tLang then

				r = tLangask

			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)

				trslslang = v

			end

		end -- for k, v

		slang = Multilingual.userLang(codes, frame)

		if slang and trslslang then

			r1 = mw.text.trim(trslslang])

			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.exotics 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.exoticask

	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.rtlask

	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.rtlask = 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

						tk = v

					elseif k:match("^%$%d$") and k ~= "$0" then

						p = p or {}

						k = tonumber(k:match("^%$(%d)$"))

						pk = v

						if not m or k > m then

							m = k

						end

					end

				end

			end

		end -- for k, v

		if type(arglist"-"]) == "string" then

			save = arglistarglist"-"]]

		end

		r = Multilingual.i18n(t, save, frame)

		if p and r and r:find("$", 1, true) then

			t = {}

			for i = 1, m do

				ti = pi 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 = collectionMultilingual.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]]", 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 = datai

					if type(e) == "table" then

						if e1 == seek then

							if type(e2]) == "table" then

								poly = e2

							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 = accepti

			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 = othersi

				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 = backi

			if feasible(slang, codes) then

				r = slang

				break -- for i

			end

		end -- for i

		if not r and codes1 then

			r = codes1

		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 = objects

		if type(object) == "table" then

			if objectattempt then

				r  = objectattempt].value

				r2 = attempt

			else

				local poly

				for k, v in pairs(object) do

					poly = poly or {}

					polyk = 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.args1 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.args1 or "")

	local s2 = mw.text.trim(frame.args2 or "")

	local r  = Multilingual.fallback(s1, s2)

	if type(r) == "table" then

		r = r1

	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.args1 or "")

	return Multilingual.findCode(s) or ""

end -- p.findCode



p.fix = function(frame)

	local r = frame.args1

	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.args1],

							 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.args1 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.args1 or "")

	local slang = frame.args2

	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.args1

	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.args1 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.args1 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.args1 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.args1 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.args1

	local seek  = frame.args2

	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.args1 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.args1 or "")

	if s ~= "" then

		local s2 = mw.text.trim(frame.args2 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.args1

	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

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook