Importer, Bürokraten, Moderatoren (CommentStreams), Strukturierte-Diskussionen-Bots, Oberflächenadministratoren, Push-Abonnementverwalter, Oversighter, Administratoren, Kampagnenbearbeiter (Hochladeassistent)
855
Bearbeitungen
w>PerfektesChaos (2016-06-18) |
K (43 Versionen von wikivoyage:Modul:Multilingual importiert) |
||
| (34 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
local Multilingual = { suite | local Multilingual = { suite = "Multilingual", | ||
serial = " | 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 } | |||
function | local foreignModule = function ( access, advanced, append, alt, alert ) | ||
-- | -- Fetch global module | ||
-- Precondition: | -- 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: | -- 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 | local r | ||
if | if type( storage ) == "string" then | ||
local | local s | ||
if | 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 | else | ||
r = string.format( "%s [[%s%s]]", | |||
"INVALID Data:*.tab", | |||
"commons:Data:", | |||
r = string.format( "%s | storage ) | ||
end | end | ||
else | |||
r = "BAD PAGE Data:*.tab – commons:" .. storage | |||
end | |||
if r then | |||
GlobalData.TabDATA[ storage ] = r | |||
data = false | |||
else | |||
r = data | |||
end | end | ||
end | end | ||
else | |||
r = | r = "BAD PAGE commons:Data:*.tab" | ||
end | end | ||
return r | return r | ||
end -- | end -- fetchData() | ||
local | local favorites = function () | ||
-- Provide fallback codes | |||
-- Postcondition: | -- Postcondition: | ||
-- Returns | -- Returns table with sequence of preferred languages | ||
if not | -- * 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 | end | ||
return | return r | ||
end -- | end -- favorites() | ||
local fetch = function ( access, | 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 | -- Attach config or library module | ||
-- Precondition: | -- Precondition: | ||
-- access -- module title | -- access -- module title | ||
-- | -- append -- string, with subpage part of this; or false | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns table | -- 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 | if type( Multilingual.ext ) ~= "table" then | ||
Multilingual.ext = { } | Multilingual.ext = { } | ||
end | end | ||
got = Multilingual.ext[ sign ] | |||
if not got and got ~= false then | |||
local | local global = Multilingual.globals[ access ] | ||
local lib = ( not append or append == "config" ) | |||
got = foreignModule( access, lib, append, global ) | |||
if type( got ) == "table" then | |||
if type( | if lib then | ||
local startup = got[ access ] | |||
if type( startup ) == "function" then | |||
got = startup() | |||
end | end | ||
end | end | ||
else | |||
got = false | |||
end | end | ||
if type( Multilingual. | Multilingual.ext[ sign ] = got | ||
if | end | ||
Multilingual. | 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 | end | ||
end | end | ||
return | return r | ||
end -- | end -- fill() | ||
function | local find = function ( ask, alien ) | ||
-- Derive language code from name | -- Derive language code from name | ||
-- Precondition: | -- Precondition: | ||
| Zeile 110: | Zeile 377: | ||
end -- for k, v | end -- for k, v | ||
if not r then | if not r then | ||
r = fair( ask ) | r = Multilingual.fair( ask ) | ||
end | end | ||
return r | return r | ||
| Zeile 117: | Zeile 384: | ||
function | 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: | -- Precondition: | ||
-- | -- able -- language version specifier to be supported | ||
-- | -- another -- language specifier of a possible replacement, | ||
-- or not to retrieve a fallback table | |||
-- Postcondition: | -- Postcondition: | ||
-- | -- Returns boolean, or table with fallback codes | ||
local | local r | ||
if type( able ) == "string" and #able > 0 then | |||
if type( another ) == "string" and #another > 0 then | |||
end -- | 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() | |||
| Zeile 142: | Zeile 573: | ||
if #seek > 1 then | if #seek > 1 then | ||
if seek:find( "[", 1, true ) 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 | end | ||
seek = mw.ustring.lower( seek ) | seek = mw.ustring.lower( seek ) | ||
if Multilingual.isLang( seek ) then | if Multilingual.isLang( seek ) then | ||
r = fair( seek ) | r = Multilingual.fair( seek ) | ||
else | else | ||
local | local collection = favorites() | ||
r = find( seek, | for i = 1, #collection do | ||
r = find( seek, collection[ i ] ) | |||
if r then | |||
end | break -- for i | ||
end | |||
end -- for i | |||
end | end | ||
end | end | ||
return r | return r | ||
end -- Multilingual.findCode() | 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() | |||
| Zeile 229: | Zeile 678: | ||
r = Multilingual.getName( slang, alien ) | r = Multilingual.getName( slang, alien ) | ||
if active then | if active then | ||
local | slot = fill( slang, false, frame ) | ||
if slot then | |||
local wlink = fetch( "WLink" ) | |||
if wlink and | |||
type( wlink.getTarget ) | |||
== "function" then | |||
slot = wlink.getTarget( slot ) | slot = wlink.getTarget( slot ) | ||
end | end | ||
else | |||
lapsus = alert | |||
end | end | ||
end | end | ||
| Zeile 265: | Zeile 707: | ||
.. mw.ustring.sub( r, 2 ) | .. mw.ustring.sub( r, 2 ) | ||
elseif alter == "d" then | elseif alter == "d" then | ||
if Multilingual.isMinusculable( slang | if Multilingual.isMinusculable( slang, r ) then | ||
r = mw.ustring.lower( r ) | r = mw.ustring.lower( r ) | ||
end | end | ||
elseif alter == "m" then | elseif alter == "m" then | ||
if Multilingual.isMinusculable( slang | if Multilingual.isMinusculable( slang, r ) then | ||
r = mw.ustring.lower( mw.ustring.sub( r, 1, 1 ) ) | r = mw.ustring.lower( mw.ustring.sub( r, 1, 1 ) ) | ||
.. mw.ustring.sub( r, 2 ) | .. mw.ustring.sub( r, 2 ) | ||
| Zeile 311: | Zeile 753: | ||
return r | return r | ||
end -- Multilingual.getBase() | 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() | |||
| Zeile 327: | Zeile 862: | ||
if ask then | if ask then | ||
local slang = alien | local slang = alien | ||
local tLang | local tLang | ||
if slang then | if slang then | ||
if slang == "*" then | if slang == "*" then | ||
slang = fair( ask ) | slang = Multilingual.fair( ask ) | ||
elseif slang == "!" then | elseif slang == "!" then | ||
slang = | slang = favorites()[ 1 ] | ||
else | else | ||
slang = fair( slang ) | slang = Multilingual.fair( slang ) | ||
end | end | ||
else | else | ||
slang = fair( ask ) | slang = Multilingual.fair( ask ) | ||
end | end | ||
if not slang then | if not slang then | ||
slang = ask or "?????" | slang = ask or "?????" | ||
end | end | ||
tLang = fetch( | slang = slang:lower() | ||
tLang = fetch( "Multilingual", "names" ) | |||
if tLang then | if tLang then | ||
tLang = tLang[ slang ] | tLang = tLang[ slang ] | ||
| Zeile 368: | Zeile 903: | ||
end | end | ||
if not r then | if not r then | ||
r = mw.language.fetchLanguageName( ask, slang ) | r = mw.language.fetchLanguageName( ask:lower(), slang ) | ||
if r == "" then | if r == "" then | ||
r = false | r = false | ||
| Zeile 381: | Zeile 916: | ||
Multilingual.isLang = function ( ask ) | 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? | -- Could this be an ISO language code? | ||
-- Precondition: | -- Precondition: | ||
-- ask | -- ask -- language code | ||
-- additional -- true, if Wiki codes like "simple" permitted | |||
-- Postcondition: | -- Postcondition: | ||
-- Returns boolean | -- Returns boolean | ||
local r | local r, s | ||
if additional then | |||
s = ask | |||
else | |||
s = Multilingual.getBase( ask ) | |||
end | |||
if s then | if s then | ||
r = mw.language.isKnownLanguageTag( s ) | r = mw.language.isKnownLanguageTag( s ) | ||
if r then | |||
r = not Multilingual.fix( s ) | |||
elseif additional then | |||
r = Multilingual.exotic[ s ] or false | |||
end | |||
else | else | ||
r = false | r = false | ||
| Zeile 408: | Zeile 1.020: | ||
local s = Multilingual.getBase( ask ) | local s = Multilingual.getBase( ask ) | ||
if s then | if s then | ||
r = mw.language.isSupportedLanguage( s ) | r = mw.language.isSupportedLanguage( s ) or | ||
Multilingual.exotic[ ask ] | |||
else | else | ||
r = false | r = false | ||
| Zeile 417: | Zeile 1.030: | ||
Multilingual.isMinusculable = function ( ask ) | Multilingual.isMinusculable = function ( ask, assigned ) | ||
-- Could this language name become downcased? | -- Could this language name become downcased? | ||
-- Precondition: | -- Precondition: | ||
-- ask -- language name | -- ask -- language code, or nil | ||
-- assigned -- language name, or nil | |||
-- Postcondition: | |||
-- Returns boolean | |||
local r = true | local r = true | ||
if cnf | if ask then | ||
local cnf = fetch( "Multilingual", "config" ) | |||
if cnf then | |||
r = false | 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 | ||
end | end | ||
| Zeile 434: | Zeile 1.062: | ||
Multilingual. | Multilingual.isRTL = function ( ask ) | ||
-- | -- Check whether language is written right-to-left | ||
-- Precondition: | -- Precondition: | ||
-- ask -- language | -- 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: | -- Postcondition: | ||
-- Returns | -- Returns string with appropriate text | ||
local r | local r | ||
local | if type( arglist ) == "table" then | ||
if s then | local t = { } | ||
local | local m, p, save | ||
for k, v in pairs( arglist ) do | |||
if | 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 | else | ||
r1 = "INVALID Multilingual.tabData key" | |||
end | end | ||
else | else | ||
r1 = data | |||
end | |||
if r1 then | |||
r2 = "error" | |||
elseif data then | |||
r1, r2 = Multilingual.i18n( data, alt, frame ) | |||
r2 = r2 or "error" | |||
end | end | ||
return | return r1, r2 | ||
end -- Multilingual. | end -- Multilingual.tabData() | ||
| Zeile 461: | Zeile 1.236: | ||
-- Try to support user language by application | -- Try to support user language by application | ||
-- Precondition: | -- Precondition: | ||
-- accept -- space separated list of available ISO 639 codes | -- accept -- string or table | ||
-- space separated list of available ISO 639 codes | |||
-- Default: project language, or English | -- Default: project language, or English | ||
-- frame -- frame, if available | -- frame -- frame, if available | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns string with appropriate code | -- Returns string with appropriate code | ||
local r, slang, | local s = type( accept ) | ||
if | local codes, r, slang | ||
if not | 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 | ||
end | end | ||
if not r then | |||
if | 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 | ||
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 | end | ||
if | if type( object ) == "table" then | ||
if | if about and about ~= "labels" then | ||
s = "descriptions" | |||
else | |||
s = "labels" | |||
end | end | ||
if | object = object[ s ] | ||
r = | 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 | ||
end | end | ||
return r | return r or "", r2 | ||
end -- Multilingual. | end -- Multilingual.wikibase() | ||
Failsafe.failsafe = function ( atleast ) | |||
-- Retrieve versioning and check for compliance | -- Retrieve versioning and check for compliance | ||
-- Precondition: | -- Precondition: | ||
-- | -- atleast -- string, with required version | ||
-- or wikidata|item|~|@ or false | |||
-- Postcondition: | -- Postcondition: | ||
-- Returns string with | -- 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 | local r | ||
if | if last or link or linked or since == "wikidata" then | ||
local item = Failsafe.item | |||
since = false | |||
r = | 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 | end | ||
return r | return r | ||
end -- | end -- Failsafe.failsafe() | ||
| Zeile 525: | Zeile 1.410: | ||
-- Export | -- Export | ||
local p = { } | 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 | |||
| Zeile 531: | Zeile 1.442: | ||
-- Retrieve language code from language name | -- Retrieve language code from language name | ||
-- 1 -- name in current project language | -- 1 -- name in current project language | ||
local s = mw.text.trim( frame.args[ 1 ] or "" ) | |||
return Multilingual.findCode( s ) or "" | |||
end -- p.findCode | 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 | |||
| Zeile 548: | Zeile 1.470: | ||
-- scream -- category title in case of error | -- scream -- category title in case of error | ||
-- split -- split pattern, if list expected | -- split -- split pattern, if list expected | ||
-- separator -- list separator, else | -- separator -- list separator, else split | ||
-- start -- prepend first element, if any | -- start -- prepend first element, if any | ||
local r | local r | ||
| Zeile 572: | Zeile 1.494: | ||
-- Retrieve base language from possibly combined ISO language code | -- Retrieve base language from possibly combined ISO language code | ||
-- 1 -- code | -- 1 -- code | ||
local s = mw.text.trim( frame.args[ 1 ] or "" ) | |||
return Multilingual.getBase( s ) or "" | |||
end -- p.getBase | end -- p.getBase | ||
| Zeile 584: | Zeile 1.507: | ||
-- * -- native | -- * -- native | ||
-- any valid code | -- any valid code | ||
local s = mw.text.trim( frame.args[ 1 ] or "" ) | |||
local slang = frame.args[ 2 ] | local slang = frame.args[ 2 ] | ||
local r | local r | ||
Multilingual.frame = frame | |||
if slang then | if slang then | ||
slang = mw.text.trim( slang ) | slang = mw.text.trim( slang ) | ||
end | end | ||
r = Multilingual.getName( | r = Multilingual.getName( s, slang ) | ||
return r or "" | return r or "" | ||
end -- p.getName | 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 | |||
| Zeile 598: | Zeile 1.566: | ||
-- Could this be an ISO language code? | -- Could this be an ISO language code? | ||
-- 1 -- code | -- 1 -- code | ||
local lucky, r = pcall( Multilingual.isLang, | local s = mw.text.trim( frame.args[ 1 ] or "" ) | ||
local lucky, r = pcall( Multilingual.isLang, s ) | |||
return r and "1" or "" | return r and "1" or "" | ||
end -- p.isLang | end -- p.isLang | ||
| Zeile 608: | Zeile 1.576: | ||
-- Could this be a Wiki language version? | -- Could this be a Wiki language version? | ||
-- 1 -- code | -- 1 -- code | ||
local lucky, r = pcall( Multilingual.isLangWiki, | -- 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 "" | return r and "1" or "" | ||
end -- p.isLangWiki | end -- p.isLangWiki | ||
| Zeile 615: | Zeile 1.584: | ||
p. | p.isRTL = function ( frame ) | ||
-- | -- Check whether language is written right-to-left | ||
-- 1 -- code | -- 1 -- string, with language code | ||
local | -- Returns non-empty, if right-to-left | ||
return | local s = mw.text.trim( frame.args[ 1 ] or "" ) | ||
end -- p. | 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 | |||
| Zeile 627: | Zeile 1.636: | ||
-- Which language does the current user prefer? | -- Which language does the current user prefer? | ||
-- 1 -- space separated list of available ISO 639 codes | -- 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 | 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 ) | p.failsafe = function ( frame ) | ||
local since = frame.args[ 1 ] | -- 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 | if since then | ||
since = mw.text.trim( since ) | since = mw.text.trim( since ) | ||
| Zeile 640: | Zeile 1.678: | ||
end | end | ||
end | end | ||
return | return Failsafe.failsafe( since ) or "" | ||
end | end -- p.failsafe() | ||