Módulo:Multilingual

Fonte: Enciclopédia de conhecimento da Igreja de Deus
Saltar para a navegação Saltar para a pesquisa

A documentação para este módulo pode ser criada na página Módulo:Multilingual/doc

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.globalModules[ access ]
				if not suited then
								finer()
								lucky, r = pcall( fun,  "Module:" .. storage )
				end
				if not lucky then
								if not suited  and
											type( alt ) == "number"  and
											alt > 0 then
												suited = string.format( "Q%d", alt )
												suited = mw.wikibase.getSitelink( suited )
												GlobalMod.globalModules[ access ] = suited or true
								end
								if type( suited ) == "string" then
												storage = suited
												finer()
												lucky, r = pcall( fun, storage )
								end
								if not lucky and alert then
												error( "Missing or invalid page: " .. storage )
								end
				end
				return r
end -- foreignModule()
local fetchData = function ( access )
				-- Retrieve translated keyword from commons:Data:****.tab
				-- Precondition:
				--     access  -- string, with page identification on Commons
				--     Returns table, with data, or string, with error message
				-- 2019-12-05
				local storage = access
				local r
				if type( storage ) == "string" then
								local s
								storage = mw.text.trim( storage )
								s = storage:lower()
								if s:sub( 1, 2 ) == "c:" then
												storage = mw.text.trim( storage:sub( 3 ) )
												s       = storage:lower()
								elseif s:sub( 1, 8 ) == "commons:" then
												storage = mw.text.trim( storage:sub( 9 ) )
												s       = storage:lower()
								end
								if s:sub( 1, 5 ) == "data:" then
												storage = mw.text.trim( storage:sub( 6 ) )
												s       = storage:lower()
								end
								if s == ""  or  s == ".tab" then
												storage = false
								elseif s:sub( -4 ) == ".tab" then
												storage = storage:sub( 1, -5 ) .. ".tab"
								else
												storage = storage .. ".tab"
								end
				end
				if type( storage ) == "string" then
								local data
								if type( GlobalData.TabDATA ) ~= "table" then
												GlobalData.TabDATA = { }
								end
								data = GlobalData.TabDATA[ storage ]
								if data then
												r = data
								else
												local lucky
												lucky, data = pcall( mw.ext.data.get, storage, "_" )
												if type( data ) == "table" then
																data = data.data
																if type( data ) == "table" then
																				GlobalData.TabDATA[ storage ] = data
																else
																				r = string.format( "%s [[%s%s]]",
																																							"INVALID Data:*.tab",
																																							"commons:Data:",
																																							storage )
																end
												else
																r = "BAD PAGE Data:*.tab – commons:" .. storage
												end
												if r then
																GlobalData.TabDATA[ storage ] = r
																data = false
												else
																r = data
												end
								end
				else
								r = "BAD PAGE commons:Data:*.tab"
				end
				return r
end -- fetchData()
local favorites = function ()
				-- Provide fallback codes
				-- Postcondition:
				--     Returns table with sequence of preferred languages
				--     * ahead elements
				--     * user (not yet accessible)
				--     * page content language (not yet accessible)
				--     * page name subpage
				--     * project
				--     * en
				local r = Multilingual.polyglott
				if not r then
								local self = mw.language.getContentLanguage():getCode():lower()
								local sub  = mw.title.getCurrentTitle().subpageText
								local f    = function ( add )
																									local s = add
																									for i = 1, #r do
																													if r[ i ] == s then
																																	s = false
																																	break -- for i
																													end
																									end -- for i
																									if s then
																													table.insert( r, s )
																									end
																					end
								r = { }
								if sub:find( "/", 2, true ) then
												sub = sub:match( "/(%l%l%l?)$" )
												if sub then
																table.insert( r, sub )
												end
								elseif sub:find( "^%l%l%l?%-?%a?%a?%a?%a?$" )  and
															mw.language.isSupportedLanguage( sub ) then
												table.insert( r, sub )
								end
								f( self )
								f( "en" )
								Multilingual.polyglott = r
				end
				return r
end -- favorites()
local feasible = function ( ask, accept )
				-- Is ask to be supported by application?
				-- Precondition:
				--     ask     -- lowercase code
				--     accept  -- sequence table, with offered lowercase codes
				-- Postcondition:
				--     nil, or true
				local r
				for i = 1, #accept do
								if accept[ i ] == ask then
												r = true
												break -- for i
								end
				end -- for i
				return r
end -- feasible()
local fetch = function ( access, append )
				-- Attach config or library module
				-- Precondition:
				--     access  -- module title
				--     append  -- string, with subpage part of this; or false
				-- Postcondition:
				--     Returns:  table, with library, or false
				local got, sign
				if append then
								sign = string.format( "%s/%s", access, append )
				else
								sign = access
				end
				if type( Multilingual.ext ) ~= "table" then
								Multilingual.ext = { }
				end
				got = Multilingual.ext[ sign ]
				if 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 got
end -- fetch()
local fetchISO639 = function ( access )
				-- Retrieve table from commons:Data:ISO639/***.tab
				-- Precondition:
				--     access  -- string, with subpage identification
				-- Postcondition:
				--     Returns table, with data, even empty
				local r
				if type( Multilingual.iso639 ) ~= "table" then
								Multilingual.iso639 = { }
				end
				r = Multilingual.iso639[ access ]
				if type( r ) == "nil" then
								local raw = fetchData( "ISO639/" .. access )
								if type( raw ) == "table" then
												local t
												r = { }
												for i = 1, #raw do
																t = raw[ i ]
																if type( t ) == "table"  and
																			type( t[ 1 ] ) == "string"  and
																			type( t[ 2 ] ) == "string" then
																				r[ t[ 1 ] ] =  t[ 2 ]
																else
																				break -- for i
																end
												end -- for i
								else
												r = false
								end
								Multilingual.iso639[ access ] = r
				end
				return r or { }
end -- fetchISO639()
local fill = function ( access, alien, frame )
				-- Expand language name template
				-- Precondition:
				--     access  -- string, with language code
				--     alien   -- language code for which to be generated
				--     frame   -- frame, if available
				-- Postcondition:
				--     Returns string
				local template = Multilingual.tmplLang
				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{ title = a }
																end
												lucky, s = pcall( f, source )
												if lucky then
																r = s
												end
								end
				end
				return r
end -- fill()
local find = function ( ask, alien )
				-- Derive language code from name
				-- Precondition:
				--     ask    -- language name, downcased
				--     alien  -- language code of ask
				-- Postcondition:
				--     nil, or string
				local codes = mw.language.fetchLanguageNames( alien, "all" )
				local r
				for k, v in pairs( codes ) do
								if mw.ustring.lower( v ) == ask then
												r = k
												break -- for k, v
								end
				end -- for k, v
				if not r then
								r = Multilingual.fair( ask )
				end
				return r
end -- find()
local fold = function ( frame )
				-- Merge template and #invoke arglist
				-- Precondition:
				--     frame   -- template frame
				-- Postcondition:
				--     table, with combined arglist
				local r = { }
				local f = function ( apply )
																		if type( apply ) == "table"  and
																					type( apply.args ) == "table" then
																						for k, v in pairs( apply.args ) do
																										v = mw.text.trim( v )
																										if v ~= "" then
																														r[ tostring( k ) ] = v
																										end
																						end -- for k, v
																		end
														end -- f()
				f( frame:getParent() )
				f( frame )
				return r
end -- fold()
User.favorize = function ( accept, frame )
				-- Guess user language
				-- Precondition:
				--     accept  -- sequence table, with offered ISO 639 etc. codes
				--     frame   -- frame, if available
				-- Postcondition:
				--     Returns string with best code, or nil
				if not ( User.self or User.langs ) then
								if not User.trials then
												User.tell = mw.message.new( User.sniffer )
												if User.tell:exists() then
																User.trials = { }
																if not Multilingual.frame then
																				if frame then
																								Multilingual.frame = frame
																				else
																								Multilingual.frame = mw.getCurrentFrame()
																				end
																end
																User.sin = Multilingual.frame:callParserFunction( "int",
																																																											User.sniffer )
												else
																User.langs = true
												end
								end
								if User.sin then
												local order  = { }
												local post   = { }
												local three  = { }
												local unfold = { }
												local s, sin
												for i = 1, #accept do
																s = accept[ i ]
																if not User.trials[ s ] then
																				if #s > 2 then
																								if s:find( "-", 3, true ) then
																												table.insert( unfold, s )
																								else
																												table.insert( three, s )
																								end
																				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.self
end -- User.favorize()
Multilingual.fair = function ( ask )
				-- Format language specification according to RFC 5646 etc.
				-- Precondition:
				--     ask  -- string or table, as created by .getLang()
				-- Postcondition:
				--     Returns string, or false
				local s = type( ask )
				local q, r
				if s == "table" then
								q = ask
				elseif s == "string" then
								q = Multilingual.getLang( ask )
				end
				if q  and
							q.legal  and
							mw.language.isKnownLanguageTag( q.base ) then
								r = q.base
								if q.n > 1 then
												local order = { "extlang",
																												"script",
																												"region",
																												"other",
																												"extension" }
												for i = 1, #order do
																s = q[ order[ i ] ]
																if s then
																				r =  string.format( "%s-%s", r, s )
																end
												end -- for i
								end
				end
				return r or false
end -- Multilingual.fair()
Multilingual.fallback = function ( able, another )
				-- Is another language suitable as replacement?
				-- Precondition:
				--     able     -- language version specifier to be supported
				--     another  -- language specifier of a possible replacement,
				--                 or not to retrieve a fallback table
				-- Postcondition:
				--     Returns boolean, or table with fallback codes
				local r
				if type( able ) == "string"  and  #able > 0 then
								if type( another ) == "string"  and  #another > 0 then
												if able == another then
																r = true
												else
																local s = Multilingual.getBase( able )
																if s == another then
																				r = true
																else
																				local others = mw.language.getFallbacksFor( s )
																				r = feasible( another, others )
																end
												end
								else
												local s = Multilingual.getBase( able )
												if s then
																r = mw.language.getFallbacksFor( s )
																if r[ 1 ] == "en" then
																				local d = fetchISO639( "fallback" )
																				if type( d ) == "table"  and
																							type( d[ s ] ) == "string" then
																								r = mw.text.split( d[ s ], "|" )
																								table.insert( r, "en" )
																				end
																end
												end
								end
				end
				return r or false
end -- Multilingual.fallback()
Multilingual.findCode = function ( ask )
				-- Retrieve code of local (current project or English) language name
				-- Precondition:
				--     ask  -- string, with presumable language name
				--             A code itself will be identified, too.
				-- Postcondition:
				--     Returns string, or false
				local seek = mw.text.trim( ask )
				local r = false
				if #seek > 1 then
								if seek:find( "[", 1, true ) then
												local wlink = fetch( "WLink" )
												if wlink  and
															type( wlink.getPlain ) == "function" then
																seek = wlink.getPlain( seek )
												end
								end
								seek = mw.ustring.lower( seek )
								if Multilingual.isLang( seek ) then
												r = Multilingual.fair( seek )
								else
												local collection = favorites()
												for i = 1, #collection do
																r = find( seek, collection[ i ] )
																if r then
																				break -- for i
																end
												end -- for i
								end
				end
				return r
end -- Multilingual.findCode()
Multilingual.fix = function ( attempt )
				-- Fix frequently mistaken language code
				-- Precondition:
				--     attempt  -- string, with presumable language code
				-- Postcondition:
				--     Returns string with correction, or false if no problem known
				local r = fetchISO639( "correction" )[ attempt:lower() ]
				return r or false
end -- Multilingual.fix()
Multilingual.format = function ( apply, alien, alter, active, alert,
																																	frame, assembly, adjacent, ahead )
				-- Format one or more languages
				-- Precondition:
				--     apply     -- string with language list or item
				--     alien     -- language of the answer
				--                  -- nil, false, "*": native
				--                  -- "!": current project
				--                  -- "#": code, downcased, space separated
				--                  -- "-": code, mixcase, space separated
				--                  -- any valid code
				--     alter     -- capitalize, if "c"; downcase all, if "d"
				--                  capitalize first item only, if "f"
				--                  downcase every first word only, if "m"
				--     active    -- link items, if true
				--     alert     -- string with category title in case of error
				--     frame     -- if available
				--     assembly  -- string with split pattern, if list expected
				--     adjacent  -- string with list separator, else assembly
				--     ahead     -- string to prepend first element, if any
				-- Postcondition:
				--     Returns string, or false if apply empty
				local r = false
				if apply then
								local slang
								if assembly then
												local bucket = mw.text.split( apply, assembly )
												local shift = alter
												local separator
												if adjacent then
																separator = adjacent
												elseif alien == "#"  or  alien == "-" then
																separator = " "
												else
																separator = assembly
												end
												for k, v in pairs( bucket ) do
																slang = Multilingual.format( v, alien, shift, active,
																																													alert )
																if slang then
																				if r then
																								r = string.format( "%s%s%s",
																																											r, separator, slang )
																				else
																								r = slang
																								if shift == "f" then
																												shift = "d"
																								end
																				end
																end
												end -- for k, v
												if r and ahead then
																r = ahead .. r
												end
								else
												local single = mw.text.trim( apply )
												if single == "" then
																r = false
												else
																local lapsus, slot
																slang = Multilingual.findCode( single )
																if slang then
																				if alien == "-" then
																								r = slang
																				elseif alien == "#" then
																								r = slang:lower()
																				else
																								r = Multilingual.getName( slang, alien )
																								if active then
																												slot = fill( slang, false, frame )
																												if slot then
																																local wlink = fetch( "WLink" )
																																if wlink  and
																																			type( wlink.getTarget )
																																																							== "function" then
																																				slot = wlink.getTarget( slot )
																																end
																												else
																																lapsus = alert
																												end
																								end
																				end
																else
																				r = single
																				if active then
																								local title = mw.title.makeTitle( 0, single )
																								if title.exists then
																												slot = single
																								end
																				end
																				lapsus = alert
																end
																if not r then
																				r = single
																elseif alter == "c" or alter == "f" then
																				r = mw.ustring.upper( mw.ustring.sub( r, 1, 1 ) )
																								.. mw.ustring.sub( r, 2 )
																elseif alter == "d" then
																				if Multilingual.isMinusculable( slang, r ) then
																								r = mw.ustring.lower( r )
																				end
																elseif alter == "m" then
																				if Multilingual.isMinusculable( slang, r ) then
																								r = mw.ustring.lower( mw.ustring.sub( r, 1, 1 ) )
																												.. mw.ustring.sub( r, 2 )
																				end
																end
																if slot then
																				if r == slot then
																								r = string.format( "[[%s]]", r )
																				else
																								r = string.format( "[[%s|%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    = tags[ 1 ]
				local r
				if s:match( "^%a%a%a?$" ) then
								r = { base  = s:lower(),
														legal = true,
														n     = #tags }
								for i = 2, r.n do
												s = tags[ i ]
												if #s == 2 then
																if r.region  or  not s:match( "%a%a" ) then
																				r.legal = false
																else
																				r.region = s:upper()
																end
												elseif #s == 4 then
																if s:match( "%a%a%a%a" ) then
																				r.legal = ( not r.script )
																				r.script = s:sub( 1, 1 ):upper() ..
																															s:sub( 2 ):lower()
																elseif s:match( "20%d%d" )  or
																							s:match( "1%d%d%d" ) then
																				r.legal = ( not r.year )
																				r.year = s
																else
																				r.legal = false
																end
												elseif #s == 3 then
																if r.extlang  or  not s:match( "%a%a%a" ) then
																				r.legal = false
																else
																				r.extlang = s:lower()
																end
												elseif #s == 1 then
																s = s:lower()
																if s:match( "[tux]" ) then
																				r.extension = s
																				for k = i + 1, r.n do
																								s = tags[ k ]
																								if s:match( "^%w+$" ) then
																												r.extension = string.format( "%s-%s",
																																																									r.extension, s )
																								else
																												r.legal = false
																								end
																				end -- for k
																else
																				r.legal = false
																end
																break -- for i
												else
																r.legal = ( not r.other )  and
																										s:match( "%a%a%a" )
																r.other = s:lower()
												end
												if not r.legal then
																break -- for i
												end
								end -- for i
								if r.legal then
												r.suggest = Multilingual.fix( r.base )
												if r.suggest then
																r.legal = false
												end
								end
				else
								r = { legal = false }
				end
				if not r.legal then
								local cnf = fetch( "Multilingual", "config" )
								if cnf  and  type( cnf.scream ) == "string" then
												r.scream = cnf.scream
								end
				end
				return r
end -- Multilingual.getLang()
Multilingual.getName = function ( ask, alien )
				-- Which name is assigned to this language code?
				-- Precondition:
				--     ask    -- language code
				--     alien  -- language of the answer
				--               -- nil, false, "*": native
				--               -- "!": current project
				--               -- any valid code
				-- Postcondition:
				--     Returns string, or false
				local r
				if ask then
								local slang   = alien
								local tLang
								if slang then
												if slang == "*" then
																slang = Multilingual.fair( ask )
												elseif slang == "!" then
																slang = favorites()[ 1 ]
												else
																slang = Multilingual.fair( slang )
												end
								else
												slang = Multilingual.fair( ask )
								end
								if not slang then
												slang = ask or "?????"
								end
								slang = slang:lower()
								tLang = fetch( "Multilingual", "names" )
								if tLang then
												tLang = tLang[ slang ]
												if tLang then
																r = tLang[ ask ]
												end
								end
								if not r then
												if not Multilingual.ext.tMW then
																Multilingual.ext.tMW = { }
												end
												tLang = Multilingual.ext.tMW[ slang ]
												if tLang == nil then
																tLang = mw.language.fetchLanguageNames( slang )
																if tLang then
																				Multilingual.ext.tMW[ slang ] = tLang
																else
																				Multilingual.ext.tMW[ slang ] = false
																end
												end
												if tLang then
																r = tLang[ ask ]
												end
								end
								if not r then
												r = mw.language.fetchLanguageName( ask:lower(), slang )
												if r == "" then
																r = false
												end
								end
				else
								r = false
				end
				return r
end -- Multilingual.getName()
Multilingual.i18n = function ( available, alt, frame )
				-- Select translatable message
				-- Precondition:
				--     available  -- table, with mapping language code ./. text
				--     alt        -- string|nil|false, with fallback text
				--     frame      -- frame, if available
				--     Returns
				--         1. string|nil|false, with selected message
				--         2. string|nil|false, with language code
				local r1, r2
				if type( available ) == "table" then
								local codes = { }
								local trsl  = { }
								local slang
								for k, v in pairs( available ) do
												if type( k ) == "string"  and
															type( v ) == "string" then
																slang = mw.text.trim( k:lower() )
																table.insert( codes, slang )
																trsl[ slang ] = v
												end
								end -- for k, v
								slang = Multilingual.userLang( codes, frame )
								if slang  and  trsl[ slang ] then
												r1 = mw.text.trim( trsl[ slang ] )
												if r1 == "" then
																r1 = false
												else
																r2 = slang
												end
								end
				end
				if not r1  and  type( alt ) == "string" then
								r1 = mw.text.trim( alt )
								if r1 == "" then
												r1 = false
								end
				end
				return r1, r2
end -- Multilingual.i18n()
Multilingual.int = function ( access, alien, apply )
				-- Translated system message
				-- Precondition:
				--     access  -- message ID
				--     alien   -- language code
				--     apply   -- nil, or sequence table with parameters $1, $2, ...
				-- Postcondition:
				--     Returns string, or false
				local o = mw.message.new( access )
				local r
				if o:exists() then
								if type( alien ) == "string" then
												o:inLanguage( alien:lower() )
								end
								if type( apply ) == "table" then
												o:params( apply )
								end
								r = o:plain()
				end
				return r or false
end -- Multilingual.int()
Multilingual.isLang = function ( ask, additional )
				-- Could this be an ISO language code?
				-- Precondition:
				--     ask         -- language code
				--     additional  -- true, if Wiki codes like "simple" permitted
				-- Postcondition:
				--     Returns boolean
				local r, s
				if additional then
								s = ask
				else
								s = Multilingual.getBase( ask )
				end
				if s then
								r = mw.language.isKnownLanguageTag( s )
								if r then
												r = not Multilingual.fix( s )
								elseif additional then
												r = Multilingual.exotic[ s ] or false
								end
				else
								r = false
				end
				return r
end -- Multilingual.isLang()
Multilingual.isLangWiki = function ( ask )
				-- Could this be a Wiki language version?
				-- Precondition:
				--     ask  -- language version specifier
				-- Postcondition:
				--     Returns boolean
				local r
				local s = Multilingual.getBase( ask )
				if s then
								r = mw.language.isSupportedLanguage( s )  or
												Multilingual.exotic[ ask ]
				else
								r = false
				end
				return r
end -- Multilingual.isLangWiki()
Multilingual.isMinusculable = function ( ask, assigned )
				-- Could this language name become downcased?
				-- Precondition:
				--     ask       -- language code, or nil
				--     assigned  -- language name, or nil
				-- Postcondition:
				--     Returns boolean
				local r = true
				if ask then
								local cnf = fetch( "Multilingual", "config" )
								if cnf then
												local s = string.format( " %s ", ask:lower() )
												if type( cnf.stopMinusculization ) == "string"
															and  cnf.stopMinusculization:find( s, 1, true ) then
																r = false
												end
												if r  and  assigned
															and  type( cnf.seekMinusculization ) == "string"
															and  cnf.seekMinusculization:find( s, 1, true )
															and  type( cnf.scanMinusculization ) == "string" then
																local scan = assigned:gsub( "[%(%)]", " " ) .. " "
																if not scan:find( cnf.scanMinusculization ) then
																				r = false
																end
												end
								end
				end
				return r
end -- Multilingual.isMinusculable()
Multilingual.isRTL = function ( ask )
				-- Check whether language is written right-to-left
				-- Precondition:
				--     ask  -- string, with language (or script) code
				-- Returns true, if right-to-left
				local r
				Multilingual.rtl = Multilingual.rtl or { }
				r = Multilingual.rtl[ ask ]
				if type( r ) ~= "boolean" then
								local bib = fetch( "ISO15924" )
								if type( bib ) == "table"  and
											type( bib.isRTL ) == "function" then
												r = bib.isRTL( ask )
								else
												r = mw.language.new( ask ):isRTL()
								end
								Multilingual.rtl[ ask ] = r
				end
				return r
end -- Multilingual.isRTL()
Multilingual.message = function ( arglist, frame )
				-- Show text in best match of user language like system message
				-- Precondition:
				--     arglist  -- template arguments
				--     frame    -- frame, if available
				-- Postcondition:
				--     Returns string with appropriate text
				local r
				if type( arglist ) == "table" then
								local t = { }
								local m, p, save
								for k, v in pairs( arglist ) do
												if type( k ) == "string"  and
															type( v ) == "string" then
																v = mw.text.trim( v )
																if v ~= "" then
																				if k:match( "^%l%l" ) then
																								t[ k ] = v
																				elseif k:match( "^%$%d$" )  and  k ~= "$0" then
																								p = p or { }
																								k = tonumber( k:match( "^%$(%d)$" ) )
																								p[ k ] = v
																								if not m  or  k > m then
																												m = k
																								end
																				end
																end
												end
								end -- for k, v
								if type( arglist[ "-" ] ) == "string" then
												save = arglist[ arglist[ "-" ] ]
								end
								r = Multilingual.i18n( t, save, frame )
								if p  and  r  and  r:find( "$", 1, true ) then
												t = { }
												for i = 1, m do
																t[ i ] = p[ i ]  or  ""
												end -- for i
												r = mw.message.newRawMessage( r, t ):plain()
								end
				end
				return r  or  ""
end -- Multilingual.message()
Multilingual.sitelink = function ( all, frame )
				-- Make link at local or other site with optimal linktext translation
				-- Precondition:
				--     all    -- string or table or number, item ID or entity
				--     frame  -- frame, if available
				-- Postcondition:
				--     Returns string with any helpful internal link, or plain text
				local s = type( all )
				local object, r
				if s == "table" then
								object = all
				elseif s == "string" then
								object = mw.wikibase.getEntity( all )
				elseif s == "number" then
								object = mw.wikibase.getEntity( string.format( "Q%d", all ) )
				end
				if type( object ) == "table" then
								local collection = object.sitelinks
								local entry
								s = false
								if type( collection ) == "table" then
												Multilingual.site = Multilingual.site  or
																																mw.wikibase.getGlobalSiteId()
												entry = collection[ Multilingual.site ]
												if entry then
																s = ":" .. entry.title
												elseif collection.enwiki then
																s = "w:en:" .. collection.enwiki.title
												end
								end
								r = Multilingual.wikibase( object, "labels", frame )
								if s then
												if s == ":" .. r then
																r = string.format( "[[%s]]", s )
												else
																r = string.format( "[[%s|%s]]", s, r )
												end
								end
				end
				return r  or  ""
end -- Multilingual.sitelink()
Multilingual.tabData = function ( access, at, alt, frame )
				-- Retrieve translated keyword from commons:Data:****.tab
				-- Precondition:
				--     access  -- string, with page identification on Commons
				--     at      -- string, with keyword
				--     alt     -- string|nil|false, with fallback text
				--     frame   -- frame, if available
				--     Returns
				--         1. string|nil|false, with selected message
				--         2. language code, or "error"
				local data = fetchData( access )
				local r1, r2
				if  type( data ) == "table" then
								if type( at ) == "string" then
												local seek = mw.text.trim( at )
												if seek == "" then
																r1 = "EMPTY Multilingual.tabData key"
												else
																local e, poly
																for i = 1, #data do
																				e = data[ i ]
																				if type( e ) == "table" then
																								if e[ 1 ] == seek then
																												if type( e[ 2 ] ) == "table" then
																																poly = e[ 2 ]
																												else
																																r1 = "INVALID Multilingual.tabData bad #"
																																																									.. tostring( i )
																												end
																												break   -- for i
																								end
																				else
																								break   -- for i
																				end
																end   -- for i
																if poly then
																				data = poly
																else
																				r1 = "UNKNOWN Multilingual.tabData key: " .. seek
																end
												end
								else
												r1 = "INVALID Multilingual.tabData key"
								end
				else
								r1 = data
				end
				if r1 then
								r2 = "error"
				elseif data then
								r1, r2 = Multilingual.i18n( data, alt, frame )
								r2 = r2 or "error"
				end
				return r1, r2
end -- Multilingual.tabData()
Multilingual.userLang = function ( accept, frame )
				-- Try to support user language by application
				-- Precondition:
				--     accept  -- string or table
				--                space separated list of available ISO 639 codes
				--                Default: project language, or English
				--     frame   -- frame, if available
				-- Postcondition:
				--     Returns string with appropriate code
				local s = type( accept )
				local codes, r, slang
				if s == "string" then
								codes = mw.text.split( accept:lower(), "%s+" )
				elseif s == "table" then
								codes = { }
								for i = 1, #accept do
												s = accept[ i ]
												if type( s ) == "string"  and
															s ~= "" then
																table.insert( codes, s:lower() )
												end
								end -- for i
				end
				slang = User.favorize( codes, frame )
				if slang then
								if feasible( slang, codes ) then
												r = slang
								elseif slang:find( "-", 1, true ) then
												slang = Multilingual.getBase( slang )
												if feasible( slang, codes ) then
																r = slang
												end
								end
								if not r then
												local others = mw.language.getFallbacksFor( slang )
												for i = 1, #others do
																slang = others[ i ]
																if feasible( slang, codes ) then
																				r = slang
																				break -- for i
																end
												end -- for i
								end
				end
				if not r then
								local back = favorites()
								for i = 1, #back do
												slang = back[ i ]
												if feasible( slang, codes ) then
																r = slang
																break -- for i
												end
								end -- for i
								if not r  and  codes[ 1 ] then
												r = codes[ 1 ]
								end
				end
				return r  or  favorites()[ 1 ]
end -- Multilingual.userLang()
Multilingual.userLangCode = function ()
				-- Guess a user language code
				-- Postcondition:
				--     Returns code of current best guess
				return User.self  or  favorites()[ 1 ]
end -- Multilingual.userLangCode()
Multilingual.wikibase = function ( all, about, attempt, frame )
				-- Optimal translation of wikibase component
				-- Precondition:
				--     all      -- string or table, object ID or entity
				--     about    -- boolean, true "descriptions" or false "labels"
				--     attempt  -- string or not, code of preferred language
				--     frame    -- frame, if available
				-- Postcondition:
				--     Returns
				--         1. string, with selected message
				--         2. string, with language code, or not
				local s = type( all )
				local object, r, r2
				if s == "table" then
								object = all
				elseif s == "string" then
								object = mw.wikibase.getEntity( all )
				end
				if type( object ) == "table" then
								if about  and  about ~= "labels" then
												s = "descriptions"
								else
												s = "labels"
								end
								object = object[ s ]
								if type( object ) == "table" then
												if object[ attempt ] then
																r  = object[ attempt ].value
																r2 = attempt
												else
																local poly
																for k, v in pairs( object ) do
																				poly = poly or { }
																				poly[ k ] = v.value
																end -- for k, v
																if poly then
																				r, r2 = Multilingual.i18n( poly, nil, frame )
																end
												end
								end
				end
				return r  or  "",   r2
end -- Multilingual.wikibase()
Failsafe.failsafe = function ( atleast )
				-- Retrieve versioning and check for compliance
				-- Precondition:
				--     atleast  -- string, with required version
				--                         or wikidata|item|~|@ or false
				-- Postcondition:
				--     Returns  string  -- with queried version/item, also if problem
				--              false   -- if appropriate
				-- 2020-08-17
				local since = atleast
				local last    = ( since == "~" )
				local linked  = ( since == "@" )
				local link    = ( since == "item" )
				local r
				if last  or  link  or  linked  or  since == "wikidata" then
								local item = Failsafe.item
								since = false
								if type( item ) == "number"  and  item > 0 then
												local suited = string.format( "Q%d", item )
												if link then
																r = suited
												else
																local entity = mw.wikibase.getEntity( suited )
																if type( entity ) == "table" then
																				local seek = Failsafe.serialProperty or "P348"
																				local vsn  = entity:formatPropertyValues( seek )
																				if type( vsn ) == "table"  and
																							type( vsn.value ) == "string"  and
																							vsn.value ~= "" then
																								if last  and  vsn.value == Failsafe.serial then
																												r = false
																								elseif linked then
																												if mw.title.getCurrentTitle().prefixedText
																															==  mw.wikibase.getSitelink( suited ) then
																																r = false
																												else
																																r = suited
																												end
																								else
																												r = vsn.value
																								end
																				end
																end
												end
								end
				end
				if type( r ) == "nil" then
								if not since  or  since <= Failsafe.serial then
												r = Failsafe.serial
								else
												r = false
								end
				end
				return r
end -- Failsafe.failsafe()
-- Export
local p = { }
p.fair = function ( frame )
				-- Format language code
				--     1  -- language code
				local s = mw.text.trim( frame.args[ 1 ]  or  "" )
				return Multilingual.fair( s )  or  ""
end -- p.fair
p.fallback = function ( frame )
				-- Is another language suitable as replacement?
				--     1  -- language version specifier to be supported
				--     2  -- language specifier of a possible replacement
				local s1 = mw.text.trim( frame.args[ 1 ]  or  "" )
				local s2 = mw.text.trim( frame.args[ 2 ]  or  "" )
				local r  = Multilingual.fallback( s1, s2 )
				if type( r ) == "table" then
								r = r[ 1 ]
				else
								r = r  and  "1"   or   ""
				end
				return r
end -- p.fallback
p.findCode = function ( frame )
				-- Retrieve language code from language name
				--     1  -- name in current project language
				local s = mw.text.trim( frame.args[ 1 ]  or  "" )
				return Multilingual.findCode( s )  or  ""
end -- p.findCode
p.fix = function ( frame )
				local r = frame.args[ 1 ]
				if r then
								r = Multilingual.fix( mw.text.trim( r ) )
				end
				return r or ""
end -- p.fix
p.format = function ( frame )
				-- Format one or more languages
				--     1          -- language list or item
				--     slang      -- language of the answer, if not native
				--                   * -- native
				--                   ! -- current project
				--                   any valid code
				--     shift      -- capitalize, if "c"; downcase, if "d"
				--                   capitalize first item only, if "f"
				--     link       -- 1 -- link items
				--     scream     -- category title in case of error
				--     split      -- split pattern, if list expected
				--     separator  -- list separator, else split
				--     start      -- prepend first element, if any
				local r
				local link
				if frame.args.link == "1" then
								link = true
				end
				r = Multilingual.format( frame.args[ 1 ],
																													frame.args.slang,
																													frame.args.shift,
																													link,
																													frame.args.scream,
																													frame,
																													frame.args.split,
																													frame.args.separator,
																													frame.args.start )
				return r or ""
end -- p.format
p.getBase = function ( frame )
				-- Retrieve base language from possibly combined ISO language code
				--     1  -- code
				local s = mw.text.trim( frame.args[ 1 ]  or  "" )
				return Multilingual.getBase( s )  or  ""
end -- p.getBase
p.getName = function ( frame )
				-- Retrieve language name from ISO language code
				--     1  -- code
				--     2  -- language to be used for the answer, if not native
				--           ! -- current project
				--           * -- native
				--           any valid code
				local s     = mw.text.trim( frame.args[ 1 ]  or  "" )
				local slang = frame.args[ 2 ]
				local r
				Multilingual.frame = frame
				if slang then
								slang = mw.text.trim( slang )
				end
				r = Multilingual.getName( s, slang )
				return r or ""
end -- p.getName
p.int = function ( frame )
				-- Translated system message
				--     1             -- message ID
				--     lang          -- language code
				--     $1, $2, ...   -- parameters
				local sysMsg = frame.args[ 1 ]
				local r
				if sysMsg then
								sysMsg = mw.text.trim( sysMsg )
								if sysMsg ~= "" then
												local n     = 0
												local slang = frame.args.lang
												local i, params, s
												if slang == "" then
																slang = false
												end
												for k, v in pairs( frame.args ) do
																if type( k ) == "string" then
																				s = k:match( "^%$(%d+)$" )
																				if s then
																								i = tonumber( s )
																								if i > n then
																												n = i
																								end
																				end
																end
												end -- for k, v
												if n > 0 then
																local s
																params = { }
																for i = 1, n do
																				s = frame.args[ "$" .. tostring( i ) ]  or  ""
																				table.insert( params, s )
																end -- for i
												end
												r = Multilingual.int( sysMsg, slang, params )
								end
				end
				return r or ""
end -- p.int
p.isLang = function ( frame )
				-- Could this be an ISO language code?
				--     1  -- code
				local s = mw.text.trim( frame.args[ 1 ]  or  "" )
				local lucky, r = pcall( Multilingual.isLang, s )
				return r and "1" or ""
end -- p.isLang
p.isLangWiki = function ( frame )
				-- Could this be a Wiki language version?
				--     1  -- code
				-- Returns non-empty, if possibly language version
				local s = mw.text.trim( frame.args[ 1 ]  or  "" )
				local lucky, r = pcall( Multilingual.isLangWiki, s )
				return r and "1" or ""
end -- p.isLangWiki
p.isRTL = function ( frame )
				-- Check whether language is written right-to-left
				--     1  -- string, with language code
				-- Returns non-empty, if right-to-left
				local s = mw.text.trim( frame.args[ 1 ]  or  "" )
				return Multilingual.isRTL( s ) and "1" or ""
end -- p.isRTL()
p.message = function ( frame )
				-- Translation of text element
				return Multilingual.message( fold( frame ), frame )
end -- p.message
p.sitelink = function ( frame )
				-- Make link at local or other site with optimal linktext translation
				--     1  -- item ID
				local s = mw.text.trim( frame.args[ 1 ]  or  "" )
				local r
				if s:match( "^%d+$") then
								r = tonumber( s )
				elseif s:match( "^Q%d+$") then
								r = s
				end
				if r then
								r = Multilingual.sitelink( r, frame )
				end
				return r or s
end -- p.sitelink
p.tabData = function ( frame )
				-- Retrieve best message text from Commons Data
				--     1    -- page identification on Commons
				--     2    -- keyword
				--     alt  -- fallback text
				local suite = frame.args[ 1 ]
				local seek  = frame.args[ 2 ]
				local salt  = frame.args.alt
				local r     = Multilingual.tabData( suite, seek, salt, frame )
				return r
end -- p.tabData
p.userLang = function ( frame )
				-- Which language does the current user prefer?
				--     1  -- space separated list of available ISO 639 codes
				local s = mw.text.trim( frame.args[ 1 ]  or  "" )
				return Multilingual.userLang( s, frame )
end -- p.userLang
p.wikibase = function ( frame )
				-- Optimal translation of wikibase component
				--     1  -- object ID
				--     2  -- 1 for "descriptions", 0 for "labels".
				--           or either "descriptions" or "labels"
				local r
				local s = mw.text.trim( frame.args[ 1 ]  or  "" )
				if s ~= "" then
								local s2    = mw.text.trim( frame.args[ 2 ]  or  "0" )
								local slang = mw.text.trim( frame.args.lang  or  "" )
								local large = ( s2 ~= ""  and  s2 ~= "0" )
								if slang == "" then
												slang = false
								end
								r = Multilingual.wikibase( s, large, slang, frame )
				end
				return r or ""
end -- p.wikibase
p.failsafe = function ( frame )
				-- Versioning interface
				local s = type( frame )
				local since
				if s == "table" then
								since = frame.args[ 1 ]
				elseif s == "string" then
								since = frame
				end
				if since then
								since = mw.text.trim( since )
								if since == "" then
												since = false
								end
				end
				return Failsafe.failsafe( since )  or  ""
end -- p.failsafe()
p.Multilingual = function ()
				return Multilingual
end -- p.Multilingual
return p