Modul:Multilingual: Unterschied zwischen den Versionen
Aus KGS-Wiki
Sn (Diskussion | Beiträge) K (1 Version importiert) |
de>PerfektesChaos (rv, danke, war lieb gemeint, aber in der Auswirkung insbesondere die Entfernung von lucky nicht zielführend für die Gesamtfunktion. For the record: Heute ab ca. 09:30 stand die globale Lua-Bibliothek mw.ext.data anscheinend für eine Stunde oder vielleicht zwei nicht zur Verfügung, was aber zwischenzeitlich global wieder behoben wurde. Das ist ein außergewöhnlicher Vorgang, der von dieser Programmierung nicht ressourcengerecht abzufangen ist.) |
(kein Unterschied)
|
Version vom 18. April 2023, 16:47 Uhr
Die Dokumentation für dieses Modul kann unter Modul:Multilingual/Doku erstellt werden
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