Modul:TransText

Modul:TransText

Modul:TransText
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-10-30",
                    item    = 0,
                    globals = { ISO15924 = 71584769 } }
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 Failsafe  = TransText
local GlobalMod = TransText
local Query = { slang    = "und",
                script   = false,
                template = { ["@"] = "lang",
                             ["#"] = "1",
                             ["*"] = "2" }
              }
local Data



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 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 ISO15924 library
    -- Throws error, if library not existing
    local r
    if not TransText.ISO15924 then
        local got = foreignModule( "ISO15924",
                                   true,
                                   false,
                                   TransText.globals.ISO15924,
                                   true )
        if type( got ) == "table"  and
           type( got.ISO15924 ) == "function" then
            TransText.ISO15924 = got.ISO15924()
        end
        if type( TransText.ISO15924 ) == "table" then
            r = TransText.ISO15924
        else
            error( "Library ISO15924 unavailable" )
        end
    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( attempt, accept, after )
    -- Check characters for compatibility with script
    -- Precondition:
    --     attempt  -- 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, attempt )
        if e then
            r = false
        else
            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 about  and  TransText.ISO15924.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
    local v
    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 )
    r = mw.ustring.gsub( r, p, " " )
    return r
end -- flush()



local function focus( achieve, all )
    -- Merge target specifications
    -- Precondition:
    --     achieve  -- table, with specification
    --     all      -- true: include replacements
    -- 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, all )
                end
                if type( part.script ) == "string" and
                   type( achieve.script ) ~= "string" then
                    achieve.script = part.script
                end
                if all  and  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( above, all, at )
    -- Merge base specifications
    -- Precondition:
    --     above  -- table, with task
    --     all    -- true: include replacements
    --     at     -- string or nil, with top entry
    -- Returns string, with error message, if any
    local use = { }
    local r, part
    if type( above.use ) == "string" then
        table.insert( use, above.use )
    elseif type( above.use ) == "table" then
        for k, v in pairs( above.use ) do
            table.insert( use, v )
        end -- for k, v
    end
    above.use = false
    if at then
        table.insert( use, at )
    end
    for i = 1, #use do
        part = Data[ use[ i ] ]
        if type( part ) == "table" then
            if part.use then
                r = fold( part, all )
            end
            if type( part.script ) == "string" then
                Query.script = part.script
            end
            if all  and  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, true )
    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+$" )  or
           s:find( "=", 1, true )  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 fresh( accept, all )
    -- Build environment
    -- Precondition:
    --     accept  -- string, language and/or script specifier
    --     all     -- true: include replacements
    -- Postcondition:
    --     Returns string, if error
    local r
    if accept ~= ""  and  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
                    Data.trans = { }
                    Query.task = Data[ Query.seek ]
                    if Query.task.shift  and
                       Data[ Query.task.shift ] then
                        Query.seek = Query.task.shift
                        Query.task = Data[ Query.seek ]
                    end
                    if type( Query.task.extern ) == "string" then
                        Query.task.extern = { Query.task.extern }
                    end
                    if type( Query.task.extern ) == "table" then
                        local lucky, part
                        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
                        r = fold( Query.task, all, Query.seek )
                    end
                else
                    r = failure( "errBaseUnknown", Query.seek )
                end
            else
                r = failure( "errDefMissing" )
            end
        end
    else
        r = failure( "errBaseMissing" )
    end
    return r
end -- fresh()



local function furnish()
    -- Execute trans series
    -- Returns string, with text
    local part, r
    if type( Query.task ) == "table" 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
    else
        r = failure( "errCompMissing", Query.seek )
    end
    if not r  and  Query.trans then
        r = forward()
    end
    return r
end -- furnish()



Failsafe.failsafe = function ( atleast )
    -- Retrieve versioning and check for compliance
    -- Precondition:
    --     atleast  -- string, with required version or "wikidata" or "~"
    --                 or false
    -- Postcondition:
    --     Returns  string  -- with queried version, also if problem
    --              false   -- if appropriate
    -- 2019-10-15
    local last  = ( atleast == "~" )
    local since = atleast
    local r
    if last  or  since == "wikidata" then
        local item = Failsafe.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 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
                    else
                        r = vsn.value
                    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()



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 = fresh( accept, true )
    if not r then
        local s
        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
                    local lucky
                    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
    end
    return r
end -- TransText.fiat()



TransText.forwarding = function ( accept )
    -- Retrieve available targets for this source
    -- Precondition:
    --     accept  -- string, language and/or script specifier
    -- Postcondition:
    --     Returns table with appropriate keys,
    --             or false,
    --             or string with error message
    local r = fresh( accept, false )
    if not r  and  Data.trans then
        r = { }
        for k, v in pairs( Data.trans ) do
            table.insert( r, k )
        end -- for k, v
        table.sort( r, first )
        for i = #r - 1, 1, -1 do
            if r[ i + 1 ] == r[ i ] then
                table.remove( r, i + 1 )
            end
        end -- for i
    end
    return r
end -- TransText.forwarding()



-- 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.forwarding = function ( frame )
    -- Available targets for this source
    local s  = mw.text.trim( frame.args[ 1 ]  or  "" )
    local r = TransText.forwarding( s )
    if type( r ) == "table" then
        if frame.args.code == "1" then
            local e
            for i = 1, #r do
                s = r[ i ]
                e = mw.html.create( "code" )
                           :wikitext( s )
                if s:find( "-", 1, true ) then
                    e:css( "white-space", "nowrap" )
                end
                r[ i ] = tostring( e )
            end -- for i
        end
        r = table.concat( r, " " )
    else
        r = ""
    end
    return r
end -- p.forwarding



p.from = function ( frame )
    -- Available sources
    local r
    return r or ""
end -- p.from



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.TransText = function ()
    return TransText
end -- p.TransText

return p

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.