Modul:TransText
Erscheinungsbild
Vorlagenprogrammierung | Diskussionen | Lua | Unterseiten | |||
Modul | Deutsch | English
|
Modul: | Dokumentation |
Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus
local TransText = { suite = "TransText",
serial = "2019-01-22",
item = 0 }
local Config = {
errBaseInvalid = { en = "Base code invalid",
de = "Ausgangscode ungültig" },
errBaseMissing = { en = "Base code missing",
de = "Ausgangscode fehlt" },
errBaseUnknown = { en = "Base code unknown:",
de = "Ausgangscode unbekannt:" },
errCompInvalid = { en = "Definition invalid",
de = "Definition ungültig" },
errCompMissing = { en = "Definition missing",
de = "Definition fehlt" },
errDefMissing = { en = "Definition module missing",
de = "Definitionsmodul fehlt" },
errTargetMissing = { en = "Target code missing",
de = "Zielcode fehlt" },
errTextCoding = { en = "Wrong encoding of base text",
de = "Schriftzeichen im Ausgangstext falsch" },
errTextMissing = { en = "Text missing",
de = "Ausgangstext fehlt" },
errTransCoding = { en = "Wrong encoding in result text",
de = "Schriftzeichen im Ergebnistext falsch" },
errMissing = { en = "Missing parameter",
de = "Parameter fehlt" },
errUnkown = { en = "Unkown parameter:",
de = "Parameter unbekannt:" }
}
local Query = { slang = "und",
script = false,
template = { ["@"] = "lang",
["#"] = "1",
["*"] = "2" }
}
local Data
local function Feed( all )
local r = { }
local val
for k, v in pairs( all ) do
if type( k ) == "number" or k:match( "^%d" ) then
for ik, iv in pairs( all ) do
if type( iv ) == "table" then
val = Feed( iv )
else
val = iv
end
table.insert( r, val )
end -- for ik, iv
break -- for k, v
else
if type( v ) == "table" then
r[ k ] = Feed( v )
else
r[ k ] = v
end
end
end -- for k, v
return r
end -- Feed()
local function Fellow()
-- Attach Multilingual/scripting library
-- Throws error, if library not existing
local r
if not TransText.MultiScript then
local lucky, got = pcall( require,
"Module:Multilingual/scripting" )
if type( got ) == "table" and
type( got.MultiScript ) == "function" then
TransText.MultiScript = got.MultiScript()
else
error( "Library MultiScript unavailable" )
end
end
if type( TransText.MultiScript ) == "table" then
r = TransText.MultiScript
end
return r
end -- Fellow()
local function Fetch( at )
-- Attempt to load Data
-- Precondition:
-- at -- string, page name
-- Returns table, or not if invalid
-- Throws error, if not existing
local d = mw.loadData( at )
local r
if type( d ) == "table" and
type( d.data ) == "table" then
r = Feed( d.data )
else
r = { }
end
return r
end -- Fetch()
local function Frame()
-- Fetch current frame
-- Returns frame
if not Query.frame then
Query.frame = mw.getCurrentFrame()
end
return Query.frame
end -- Frame()
local function facility()
-- Fetch current site language
-- Returns language code
local r
if Data then
r = Data.stdLang
end
if not r then
r = mw.language.getContentLanguage():getCode()
if Data then
Data.stdLang = r
end
end
return r
end -- facility()
local function factory( apply )
-- Localization of messages
-- apply -- string, with message key
-- Returns message text; at least english
local entry = Config[ apply ]
local r
if entry then
r = entry[ facility() ]
if not r then
r = entry.en
end
else
r = string.format( "????.%s.????", apply )
end
return r
end -- factory()
local function failure( alert, about )
-- Format message with class="error"
-- alert -- string, with message key
-- about -- string, with explanation
-- Returns message with markup
local story = factory( alert )
local err = mw.html.create( "span" )
:addClass( "error" )
local env = Frame():getParent()
if env then
story = string.format( "[[%s]] – %s",
env:getTitle(), story )
end
if about then
story = string.format( "%s %s", story, about )
end
err:wikitext( story )
return tostring( err )
end -- failure()
local function fetch( assigned )
-- Load data page
-- Precondition:
-- assigned -- string or nil, for sub module
-- Returns table, if assigned, or not
-- Throws error, if not existing
local sub = string.format( "%s/data", Frame():getTitle() )
local r
if not Data then
Data = Fetch( sub )
end
if assigned then
sub = string.format( "%s/%s", sub, assigned )
r = Fetch( sub )
end
return r
end -- fetch()
local function fidelity( assume, accept, after )
-- Check characters for compatibility with script
-- Precondition:
-- assume -- string, source text
-- accept -- string, script specifier
-- after -- true, for result text
-- Returns false, if okay, else string with coding sequence
local bib = Fellow()
local r
if bib then
local e, cp = bib.isScript( accept, assume )
if not e then
local scream
if after then
scream = "errTransCoding"
else
scream = "errTextCoding"
end
r = failure( scream, bib.showScripts( cp ) )
end
end
return r
end -- fidelity()
local function finish( adjust, about )
-- Finalize template parameter text
-- Precondition:
-- adjust -- string, with source text
-- about -- string or nil, with script or lang code
-- Returns string, with source text
local r = adjust:gsub( "{{", "{{" )
:gsub( "|", "|" )
:gsub( "}}", "}}" )
if TransText.MultiScript.isRTL( about ) then
r = r .. "‎"
end
if not mw.isSubsting() then
r = r:gsub( "&(#?x?[lrm%x]+;)", "&%1" )
end
return r
end -- finish()
local function first( a1, a2 )
-- Compare a1 with a2
-- a1 -- string, with name
-- a2 -- string, with name
-- Returns true if a1 < a2
local f = function( a )
local d = { n = 0, s = "" }
local k = a
local s = type( k )
if s == "string" then
if k:match( "^%d+$" ) then
d.n = tonumber( k ) - 100
else
d.s = k
if k:match( "^%l+$" ) then
d.n = 0.1
elseif k:sub( 1, 3 ) == "ISO" then
local n, m = k:match( "^ISO(%d+)%-(%d+)$" )
if n then
d.n = tonumber( n )
+ tonumber( m ) * 0.0001
else
n = k:match( "^ISO(%d+)$" )
d.n = tonumber( n )
end
else
d.n = 1000000
end
end
elseif s == "number" then
d.n = k - 100
end
return d
end
local d1 = f( a1 )
local d2 = f( a2 )
local r
if d1.n == d2.n then
r = ( d1.s < d2.s )
else
r = ( d1.n < d2.n )
end
return r
end -- first()
local function flat( adjust )
-- Make string of specification
-- Precondition:
-- adjust -- string, number, table, with specification
-- Returns string, with specification
local r = adjust
local s = type( r )
if s == "number" then
r = mw.ustring.char( r )
elseif s == "string" then
elseif s == "table" then
local collection = {}
for k, v in pairs( r ) do
s = type( v )
if s == "number" then
table.insert( collection, mw.ustring.char( v ) )
elseif s == "string" then
table.insert( collection, v )
else
table.insert( collection, "/????/" )
end
end -- for k, v
r = table.concat( collection )
end
return r
end -- flat()
local function flip( adjust, apply )
-- Replace by set of string pattern rules
-- Precondition:
-- adjust -- string, with text
-- apply -- sequence table, with tupels { seek, set }
-- Returns string, with text
local r = adjust
local seek, set, v
for i = 1, #apply do
v = apply[ i ]
seek = flat( v[ 1 ] )
set = flat( v[ 2 ] )
apply[ i ] = { seek, set }
r = mw.ustring.gsub( r, seek, set )
end -- for i
return r
end -- flip()
local function flipper( apply, append )
-- Extend table by set of replacement rules
-- Precondition:
-- apply -- sequence table or nil, to be extended
-- append -- table, to be appended
-- Returns sequence table with replacement rules
local r = apply
if type( r ) ~= "table" then
r = { }
end
for i = 1, #append do
v = append[ i ]
table.insert( r,
{ flat( v[ 1 ] ),
flat( v[ 2 ] ) } )
end -- for i
return r
end -- flipper()
local function flush( adjust )
-- Cleanup for whitespace and invisible characters
-- Precondition:
-- adjust -- string, with source text
-- Returns string, with source text
local r = adjust
local p
if r:find( "&", 1, true ) then
r = mw.text.decode( r, true )
end
p = mw.ustring.char( 91, 0x200E, 45, 0x200F,
0x202A, 45, 0x202E,
0x2066, 45, 0x2069, 93 )
r = mw.ustring.gsub( r, p, "" )
p = mw.ustring.char( 91, 0x0001, 45, 0x001F,
0x00A0,
0x2002, 45, 0x200A,
0x202F, 93 ) -- NARROW NO-BREAK SPACE
r = mw.ustring.gsub( r, p, " " )
return r
end -- flush()
local function focus( achieve )
-- Merge target specifications
-- Precondition:
-- achieve -- table, with specification
-- Postcondition:
-- specification expanded and resolved
local use = { }
local s
if type( Data.use ) ~= "table" then
Data.use = { }
end
if type( achieve.use ) == "string" then
table.insert( use, achieve.use )
elseif type( achieve.use ) == "table" then
for k, v in pairs( achieve.use ) do
table.insert( use, v )
end -- for k, v
end
achieve.use = false
for i = 1, #use do
s = use[ i ]
if Data.use[ s ] then
error( "Recursive loop: " .. s )
break -- for i
else
Data.use[ s ] = true
part = Data.trans[ s ]
if type( part ) == "table" then
if part.use then
focus( part )
end
if type( part.script ) == "string" and
type( achieve.script ) ~= "string" then
achieve.script = part.script
end
if type( part.replace ) == "table" then
achieve.replace = flipper( achieve.replace,
part.replace )
end
else
error( "Bad transclusion: " .. s )
end
end
end -- for i
end -- focus()
local function fold()
-- Merge base specifications
-- Returns string, with error message, if any
local use = { }
local r, part
Query.task = Data[ Query.seek ]
if type( Query.task.use ) == "string" then
table.insert( use, Query.task.use )
elseif type( Query.task.use ) == "table" then
for k, v in pairs( Query.task.use ) do
table.insert( use, v )
end -- for k, v
end
table.insert( use, Query.seek )
for i = 1, #use do
part = Data[ use[ i ] ]
if type( part ) == "table" then
if type( part.script ) == "string" then
Query.script = part.script
end
if type( part.replace ) == "table" then
Data.replace = flipper( Data.replace, part.replace )
end
if type( part.targets ) == "table" then
for k, v in pairs( part.targets ) do
Data.trans[ k ] = v
end -- for k, v
end
else
r = failure( "errCompInvalid", v )
break -- for i
end
end -- for i
return r
end -- fold()
local function foreign( achieve )
-- Execute transformation
-- Precondition:
-- achieve -- table, with specification
-- Returns table with components
-- text -- string, with transformed text
-- script -- string, with transformed text
-- error -- string, with problem
local r = { text = Query.source }
focus( achieve )
if type( achieve.replace ) == "table" then
r.text = flip( r.text, achieve.replace )
end
if type( achieve.script ) == "string" then
r.script = achieve.script
r.error = fidelity( r.text, achieve.script, true )
end
return r
end -- foreign()
local function forward()
-- Create template transclusion
-- Returns string, with wikisyntax text
local o = { }
local t = { }
local e, r, s, x
if Query.template[ "@" ]:find( "%s", 1, true ) then
Query.template[ "@" ] = string.format( Query.template[ "@" ],
Query.seek )
end
r = string.format( "{{%s", Query.template[ "@" ] )
e = Query.template[ "#" ]
if e then
s = Query.slang
if Query.script then
s = string.format( "%s-%s", s, Query.script )
end
t[ e ] = s
table.insert( o, e )
end
e = Query.template[ "*" ]
if e then
t[ e ] = finish( Query.source, Query.script or Query.slang )
table.insert( o, e )
end
for k, v in pairs( Query.trans ) do
if v.text then
if Query.template[ k ] then
k = Query.template[ k ]
end
t[ k ] = finish( v.text, v.script or v.slang )
table.insert( o, k )
if v.error then
x = x or ""
x = string.format( "%s %s", x, v.error )
end
end
end -- for k, v
table.sort( o, first )
for i = 1, #o do
e = o[ i ]
s = t[ e ]
if not e:match( "^%d+$" ) then
s = string.format( "%s=%s", e, s )
end
r = string.format( "%s |%s", r, s )
end -- for i
r = r .. "}}"
if x then
r = r .. x
end
return r
end -- forward()
local function furnish()
-- Execute trans series
-- Returns string, with text
local part, r
if Query.task.shift and
Data[ Query.task.shift ] then
Query.seek = Query.task.shift
Query.task = Data[ Query.seek ]
end
Data.trans = { }
if type( Query.task.extern ) == "string" then
Query.task.extern = { Query.task.extern }
end
if type( Query.task.extern ) == "table" then
local lucky
for k, v in pairs( Query.task.extern ) do
lucky, part = pcall( fetch, v )
if type( part ) == "table" then
for rk, rv in pairs( part ) do
Data[ rk ] = rv
end -- for rk, rv
else
r = failure( "errDefMissing", v )
break -- for k, v
end
end -- for k, v
end
if not r then
if type( Query.task ) == "table" then
r = fold()
else
r = failure( "errCompMissing", Query.seek )
end
end
if not r then
Query.source = flush( Query.source )
if Data.replace then
Query.source = flip( Query.source, Data.replace )
end
r = fidelity( Query.source, Query.script )
if not r then
local s
for i = 1, #Query.targets do
s = Query.targets[ i ]
part = Data.trans[ s ]
if type( part ) == "table" then
Query.trans = Query.trans or { }
Query.trans[ s ] = foreign( part )
else
if type( s ) == "string" and s ~= "" then
s = ": " ..s
else
s = false
end
r = failure( "errTargetMissing", s )
break -- for i
end
end -- for i
end
end
if not r and Query.trans then
r = forward()
end
return r
end -- furnish()
TransText.fiat = function ( accept, adjust, adapt, alter )
-- Main entry
-- Precondition:
-- accept -- string, language and/or script specifier
-- adjust -- string, source text
-- adapt -- table, with required targets
-- alter -- string or nil, with JSON template spec
-- Postcondition:
-- Returns string
local r
if type( accept ) == "string" then
Query.seek = accept
if Query.seek:match( "^%l%l%l?%-?" ) then
Query.slang = Query.seek:match( "^(%l%l%l?)$" ) or
Query.seek:match( "^(%l%l%l?)%-%u%u$" ) or
Query.seek:match( "^(%l%l%l?)%-%u%l%l%l$" )
Query.script = Query.seek:match( "^%l+%-(%u%l%l%l)$" )
if not Query.script then
local bib = Fellow()
if bib then
Query.script = bib.getLanguageScript( Query.slang )
end
end
else
Query.script = Query.seek:match( "^(%u%l%l%l)$" )
end
if not ( Query.script or Query.slang ) then
r = failure( "errBaseInvalid", Query.seek )
end
if not r then
local lucky
lucky, r = pcall( fetch )
if lucky then
if Data[ Query.seek ] then
local s
Query.task = Data[ Query.seek ]
if type( adjust ) == "string" then
s = mw.text.trim( adjust )
if s == "" then
r = failure( "errTextMissing" )
else
Query.source = s
end
else
r = failure( "errTextMissing" )
end
if not r then
s = type( adapt )
if s == "table" then
Query.targets = adapt
elseif s == "string" then
Query.targets = { adapt }
end
if Query.targets then
if alter then
lucky, r = pcall( mw.text.jsonDecode,
alter )
if type( r ) == "table" then
Query.template = r
end
end
r = furnish()
else
r = failure( "errTargetMissing" )
end
end
else
r = failure( "errBaseUnknown", Query.seek )
end
else
r = failure( "errDefMissing" )
end
end
else
r = failure( "errBaseMissing" )
end
return r
end -- TransText.fiat()
TransText.failsafe = function ( assert )
-- Retrieve versioning and check for compliance
-- Precondition:
-- assert -- string, with required version or "wikidata",
-- or false
-- Postcondition:
-- Returns string with appropriate version, or false
local since = assert
local r
if since == "wikidata" then
local item = TransText.item
since = false
if type( item ) == "number" and item > 0 then
local entity = mw.wikibase.getEntity( string.format( "Q%d",
item ) )
if type( entity ) == "table" then
local vsn = entity:formatPropertyValues( "P348" )
if type( vsn ) == "table" and
type( vsn.value ) == "string" and
vsn.value ~= "" then
r = vsn.value
end
end
end
end
if not r then
if not since or since <= TransText.serial then
r = TransText.serial
else
r = false
end
end
return r
end -- TransText.failsafe()
-- Export
local p = { }
p.fiat = function ( frame )
-- Main task
-- 1 -- language or script code of request text
-- 2 -- request text
-- 3 -- code of first transformation
-- template -- 1 for template data
local s = mw.text.trim( frame.args[ 3 ] or "" )
local r
if s ~= "" then
local start = mw.text.trim( frame.args[ 1 ] or "" )
local source = mw.text.trim( frame.args[ 2 ] or "" )
if start ~= "" and source ~= "" then
local syntax = mw.text.trim( frame.args.template or "" )
local trans = { }
table.insert( trans, s )
for k, v in pairs( frame.args ) do
if type( k ) == "number" and k > 3 then
s = mw.text.trim( v )
if s ~= "" then
table.insert( trans, s )
end
end
end -- for k, v
Query.frame = frame
r = TransText.fiat( start, source, trans, syntax )
end
end
return r or ""
end -- p.fiat
p.from = function ( frame )
-- Available sources
local r
return r or ""
end -- p.from
p.forwarding = function ( frame )
-- Available targets for this source
local r
return r or ""
end -- p.forwarding
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 TransText.failsafe( since ) or ""
end -- p.failsafe()
p.TransText = function ()
return TransText
end -- p.TransText
return p