Modul:Multilingual: Unterschied zwischen den Versionen
2019-11-01
w>PerfektesChaos (2019-06-01) |
w>PerfektesChaos (2019-11-01) |
||
| Zeile 1: | Zeile 1: | ||
local Multilingual = { suite | local Multilingual = { suite = "Multilingual", | ||
serial = "2019- | serial = "2019-11-01", | ||
item | item = 47541920, | ||
local User = { sniffer = "showpreview" } | globals = { ISO15924 = 71584769, | ||
WLink = 19363224 } | |||
} | |||
local Failsafe = Multilingual | |||
local GlobalMod = Multilingual | |||
local User = { sniffer = "showpreview" } | |||
Multilingual.globals.Multilingual = Multilingual.item | |||
| Zeile 28: | Zeile 34: | ||
local | 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 | |||
-- 2019-10-29 | |||
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, 0 ) | |||
end | |||
end | |||
return r | |||
end -- foreignModule() | |||
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 | |||
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 | |||
if not r then | |||
local self = mw.language.getContentLanguage():getCode():lower() | |||
local sub = mw.title.getCurrentTitle().subpageText | |||
r = { } | |||
if sub:find( "/", 2, true ) then | |||
sub = sub:match( "/(%l%l%l?)$" ) | |||
if sub then | |||
table.insert( r, sub ) | |||
end | |||
end | |||
f( self ) | |||
f( "en" ) | |||
Multilingual.polyglott = r | |||
end | end | ||
return | return r | ||
end -- | end -- favorites() | ||
function | local feasible = function ( ask, accept ) | ||
-- Is ask to be supported by application? | -- Is ask to be supported by application? | ||
-- Precondition: | -- Precondition: | ||
| Zeile 59: | Zeile 148: | ||
local fetch = function ( access, | 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 ] | ||
got = foreignModule( access, not append, append, global ) | |||
if type( got ) == "table" then | if type( got ) == "table" then | ||
local startup = | if not append then | ||
local startup = got[ access ] | |||
if type( startup ) == "function" then | |||
got = startup() | |||
end | |||
end | end | ||
else | |||
got = false | |||
end | end | ||
Multilingual.ext[ sign ] = got | |||
end | end | ||
return | return got | ||
end -- fetch() | end -- fetch() | ||
local function | local fill = function ( access, alien, frame ) | ||
-- Expand language name template | -- Expand language name template | ||
-- Precondition: | -- Precondition: | ||
| Zeile 111: | Zeile 196: | ||
local r | local r | ||
if type( template ) ~= "table" then | if type( template ) ~= "table" then | ||
local cnf = fetch( "Multilingual | local cnf = fetch( "Multilingual", "config" ) | ||
if | if cnf then | ||
template = cnf.tmplLang | template = cnf.tmplLang | ||
end | end | ||
| Zeile 148: | Zeile 233: | ||
function | local find = function ( ask, alien ) | ||
-- Derive language code from name | -- Derive language code from name | ||
-- Precondition: | -- Precondition: | ||
| Zeile 168: | Zeile 253: | ||
return r | return r | ||
end -- find() | 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() | |||
| Zeile 291: | Zeile 401: | ||
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 ) | ||
| Zeile 297: | Zeile 411: | ||
r = Multilingual.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 | ||
| Zeile 392: | Zeile 508: | ||
if slot then | if slot then | ||
local wlink = fetch( "WLink" ) | local wlink = fetch( "WLink" ) | ||
slot = wlink.getTarget( slot ) | if wlink and | ||
type( wlink.getTarget ) | |||
== "function" then | |||
slot = wlink.getTarget( slot ) | |||
end | |||
else | else | ||
lapsus = alert | lapsus = alert | ||
| Zeile 546: | Zeile 666: | ||
end | end | ||
if not r.legal then | if not r.legal then | ||
local cnf = fetch( "Multilingual | local cnf = fetch( "Multilingual", "config" ) | ||
if | if cnf and type( cnf.scream ) == "string" then | ||
r.scream = cnf.scream | r.scream = cnf.scream | ||
end | end | ||
| Zeile 570: | Zeile 689: | ||
if ask then | if ask then | ||
local slang = alien | local slang = alien | ||
local tLang | local tLang | ||
if slang then | if slang then | ||
| Zeile 576: | Zeile 694: | ||
slang = Multilingual.fair( ask ) | slang = Multilingual.fair( ask ) | ||
elseif slang == "!" then | elseif slang == "!" then | ||
slang = | slang = favorites()[ 1 ] | ||
else | else | ||
slang = Multilingual.fair( slang ) | slang = Multilingual.fair( slang ) | ||
| Zeile 587: | Zeile 705: | ||
end | end | ||
slang = slang:lower() | slang = slang:lower() | ||
tLang = fetch( | tLang = fetch( "Multilingual", "names" ) | ||
if tLang then | if tLang then | ||
tLang = tLang[ slang ] | tLang = tLang[ slang ] | ||
| Zeile 633: | Zeile 751: | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns string | -- Returns string | ||
local | local bib = fetch( "ISO15924" ) | ||
local | local r | ||
if bib and | |||
type( bib.getScriptName ) == "function" then | |||
r = bib.getScriptName( assigned, alien, add ) | |||
end | end | ||
return r or "" | |||
return r | |||
end -- Multilingual.getScriptName() | end -- Multilingual.getScriptName() | ||
| Zeile 769: | Zeile 885: | ||
local r = true | local r = true | ||
if ask then | if ask then | ||
local cnf = fetch( "Multilingual | local cnf = fetch( "Multilingual", "config" ) | ||
if cnf then | if cnf then | ||
local s = string.format( " %s ", ask:lower() ) | local s = string.format( " %s ", ask:lower() ) | ||
| Zeile 800: | Zeile 916: | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns boolean | -- Returns boolean | ||
local | local bib = fetch( "ISO15924" ) | ||
local | local r | ||
if type( | if type( bib ) == "table" and | ||
type( bib.isTrans ) == "function" then | |||
r = bib.isTrans( ask, assign, about ) | |||
end | end | ||
return r or false | |||
if type( | end -- Multilingual.isTrans() | ||
for k, v in pairs( | |||
if v == | |||
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 | |||
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 | ||
end -- for k, v | end -- for k, v | ||
r = Multilingual.i18n( t, nil, 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 | end | ||
if | if type( object ) == "table" then | ||
r = ( | 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 | end | ||
return r | return r or "" | ||
end -- Multilingual. | end -- Multilingual.sitelink() | ||
| Zeile 839: | Zeile 1.026: | ||
local codes, r, slang | local codes, r, slang | ||
if s == "string" then | if s == "string" then | ||
codes = mw.text.split( accept:lower(), " " ) | codes = mw.text.split( accept:lower(), "%s+" ) | ||
elseif s == "table" then | elseif s == "table" then | ||
codes = { } | codes = { } | ||
for i = 1, #accept do | for i = 1, #accept do | ||
s = accept[ i ] | s = accept[ i ] | ||
if type( s ) == "string" then | if type( s ) == "string" and | ||
s ~= "" then | |||
table.insert( codes, s:lower() ) | table.insert( codes, s:lower() ) | ||
end | end | ||
end -- for i | end -- for i | ||
end | end | ||
slang = User.favorize( codes, frame ) | slang = User.favorize( codes, frame ) | ||
if | if slang then | ||
if feasible( slang, codes ) then | if feasible( slang, codes ) then | ||
r = slang | 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 not r then | ||
local | local back = favorites() | ||
for i = 1, # | for i = 1, #back do | ||
slang = | slang = back[ i ] | ||
if feasible( slang, codes ) then | if feasible( slang, codes ) then | ||
r = slang | r = slang | ||
| Zeile 876: | Zeile 1.067: | ||
end | end | ||
end -- for i | end -- for i | ||
if not r | if not r and codes[ 1 ] then | ||
r = codes[ 1 ] | |||
end | end | ||
end | end | ||
return r or | return r or favorites()[ 1 ] | ||
end -- Multilingual.userLang() | end -- Multilingual.userLang() | ||
| Zeile 895: | Zeile 1.080: | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns code of current best guess | -- Returns code of current best guess | ||
return User.self or | return User.self or favorites()[ 1 ] | ||
end -- Multilingual.userLangCode() | end -- Multilingual.userLangCode() | ||
Multilingual.failsafe = function ( atleast ) | 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 string with appropriate code | |||
local s = type( all ) | |||
local object, r | |||
if s == "table" then | |||
object = all | |||
elseif s == "string" then | |||
object = mw.wikibase.getEntity( all ) | |||
end | |||
if type( object ) == "table" then | |||
if about then | |||
s = "descriptions" | |||
else | |||
s = "labels" | |||
end | |||
object = object[ s ] | |||
if type( object ) == "table" then | |||
if object[ attempt ] then | |||
r = object[ attempt ].value | |||
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 = Multilingual.i18n( poly, nil, frame ) | |||
end | |||
end | |||
end | |||
end | |||
return r or "" | |||
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" | -- atleast -- string, with required version or "wikidata" or "~" | ||
-- | -- or false | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns string with | -- Returns string -- with queried version, also if problem | ||
-- false -- if appropriate | |||
-- 2019-10-15 | |||
local last = ( atleast == "~" ) | |||
local since = atleast | local since = atleast | ||
local r | local r | ||
if since == "wikidata" then | if last or since == "wikidata" then | ||
local item = | local item = Failsafe.item | ||
since = false | since = false | ||
if type( item ) == "number" and item > 0 then | if type( item ) == "number" and item > 0 then | ||
| Zeile 916: | Zeile 1.147: | ||
item ) ) | item ) ) | ||
if type( entity ) == "table" then | if type( entity ) == "table" then | ||
local vsn = entity:formatPropertyValues( | local seek = Failsafe.serialProperty or "P348" | ||
local vsn = entity:formatPropertyValues( seek ) | |||
if type( vsn ) == "table" and | if type( vsn ) == "table" and | ||
type( vsn.value ) == "string" and | type( vsn.value ) == "string" and | ||
vsn.value ~= "" then | vsn.value ~= "" then | ||
r = vsn.value | if last and vsn.value == Failsafe.serial then | ||
r = false | |||
else | |||
r = vsn.value | |||
end | |||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
if | if type( r ) == "nil" then | ||
if not since or since <= | if not since or since <= Failsafe.serial then | ||
r = | r = Failsafe.serial | ||
else | else | ||
r = false | r = false | ||
| Zeile 933: | Zeile 1.169: | ||
end | end | ||
return r | return r | ||
end -- | end -- Failsafe.failsafe() | ||
| Zeile 1.044: | Zeile 1.280: | ||
p.getScriptName = function ( frame ) | p.getScriptName = function ( frame ) | ||
-- OBSOLETE | |||
-- Retrieve script name from ISO 15924 script code, hopefully linked | -- Retrieve script name from ISO 15924 script code, hopefully linked | ||
-- 1 -- code | -- 1 -- code | ||
| Zeile 1.121: | Zeile 1.358: | ||
p.isTrans = function ( frame ) | p.isTrans = function ( frame ) | ||
-- OBSOLETE | |||
-- Check whether valid transcription for context | -- Check whether valid transcription for context | ||
-- 1 -- string, with transcription key | -- 1 -- string, with transcription key | ||
| Zeile 1.130: | Zeile 1.368: | ||
return Multilingual.isTrans( s1, s2, site ) and "1" or "" | return Multilingual.isTrans( s1, s2, site ) and "1" or "" | ||
end -- p.isTrans | end -- p.isTrans | ||
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 | |||
| Zeile 1.139: | Zeile 1.402: | ||
return Multilingual.userLang( s, frame ) | return Multilingual.userLang( s, frame ) | ||
end -- p.userLang | end -- p.userLang | ||
p.wikibase = function ( frame ) | |||
-- Optimal translation of wikibase component | |||
-- 1 -- object ID | |||
-- 2 -- 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 | |||
| Zeile 1.157: | Zeile 1.440: | ||
end | end | ||
end | end | ||
return | return Failsafe.failsafe( since ) or "" | ||
end -- p.failsafe() | end -- p.failsafe() | ||