Modul:Vorlage:lang

Aus FreeWiki
Zur Navigation springen Zur Suche springen

local Export = { serial = "2019-10-19",

                suite  = "lang",
                item   = 26826825 }

--[=[ Vorlage:lang und Sprachnamenvorlagen usw. unterstützen ]=] local Failsafe = Export local GlobalMod = Export


local Config = {

  errCat        = false,
  errClass      = "error_lang",
  errClasses    = false,
  errHide       = true,
  errNS         = false,
  errDoubled    = { en = "Doubled:",
                    de = "Doppelangabe:" },
  errEmpty      = { en = "Empty parameter value",
                    de = "Parameterwert fehlt" },
  errInvalid    = { en = "Invalid:",
                    de = "Ungültig:" },
  errMissing    = { en = "Missing parameter",
                    de = "Parameter fehlt" },
  errSuggest    = { en = "'%s' * probably '%s' intended",
                    de = "'%s' * vermutlich ist '%s' gemeint" },
  errUnkown     = { en = "Unkown parameter:",
                    de = "Parameter unbekannt:" },
  ipa           = "IPA",
  keyBase       = 100,
  keyProject    = 200,
  keyAncient    = 300,
  keyTranslate  = 400,
  params        = { "Text1",
                    "Audio",
                    "IPA",
                    "class",
                    "lenient",
                    "script",
                    "style",
                    "nachgestellt",
                    "demo",
                    "demo2",
                    "demo3",
                    "demo4",
                    "NoCat" },
  percents      = false,
  orderOther    = { grc = 1,
                    hbo = 2,
                    la  = 3,
                    en  = 9 },
  owns          = { de = "‚%s‘" },
  sepComma      = "Latn Armn Cyrl Grek Thai",
  site          = "Latn",
  tmplAudio     = false,    -- template for media player link
  tmplLang      = false,    -- template for language name link
  tmplStyles    = false,
  wikidata      = { Multilingual       = 47541920,
                    ISO15924           = 71584769,
                    ["ISO15924/codes"] = 71840276,
                    TemplUtl           = 52364930 }

}


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 Fetch( ask )

   -- Fetch module (require)
   -- Parameter:
   --     ask  -- string, with name
   --                     "ISO15924"
   --                     "Multilingual"
   --                     "TemplUtl"
   -- Returns string, with error message, if not available
   local ext = Config[ ask ]
   local r
   if not ext  and  ext ~= false then
       ext = foreignModule( ask,
                            true,
                            false,
                            Config.wikidata[ ask ] )
       if type( ext ) == "table" then
           Config[ ask ] = ext
           if type( ext[ ask ] ) == "function" then
               Config[ ask ] = ext[ ask ]()
           end
       end
       if type( Config[ ask ] ) ~= "table" then
           Config[ ask ] = false
           if not ext then
               ext = "Invalid library *** Module:" .. ask
           end
           ext = mw.html.create( "span" )
                        :attr( "class", "error" )
                        :wikitext( ext )
           r = tostring( ext )
       end
   end
   return r

end -- Fetch()


local function Frame()

   -- Fetch current frame
   -- Returns frame
   if not Config.frame then
       Config.frame = mw.getCurrentFrame()
   end
   return Config.frame

end -- Frame()


local function Friend()

   -- Fetch Module:ISO15924/codes
   if not Config.codesISO15924 then
       Config.codesISO15924 = foreignModule( "ISO15924",
                                             false,
                                             "codes",
                                             Config.wikidata.ISO15924 )
       if type( Config.codesISO15924 ) ~= "table" then
           Config.codesISO15924 = { }
       end
   end
   return Config.codesISO15924

end -- Friend()


local function Fulfil( access, args )

   -- Expand template
   --     apply  -- string, with template name
   --     args   -- table, with parameters
   -- Returns string
   -- Throws error, if template not existing
   return Frame():expandTemplate{ title = access,
                                  args  = args }

end -- Fulfil()


local function facet( assign )

   -- Format language name
   --     apply  -- string, with language name, might be linked
   -- Returns string
   local e = mw.html.create( "span" )
                    :css( { ["font-style"]  = "normal",
                            ["font-weight"] = "normal" } )
                    :wikitext( assign )
   return tostring( e )

end -- facet()


local function facility()

   -- Fetch current site language
   -- Returns language code
   if not Config.standard then
       Config.standard = mw.language.getContentLanguage():getCode()
   end
   return Config.standard

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
       local e = mw.html.create( "span" )
                        :attr( "class", "error" )
                        :wikitext( string.format( "????.%s.????",
                                                  apply ) )
       r = tostring( e )
   end
   return r

end -- factory()


local function faculty( adjust )

   -- Test template arg for boolean
   --     adjust  -- string or nil
   -- Returns boolean
   local s = type( adjust )
   local r
   if s == "string" then
       r = mw.text.trim( adjust )
       r = ( r ~= ""  and  r ~= "0"  and  r ~= "-" )
   elseif s == "boolean" then
       r = adjust
   else
       r = false
   end
   return r

end -- faculty()


local function familiar( ask, assign )

   -- Check whether valid transcription for context
   --     ask     -- string, with transcription code
   --     assign  -- string or nil, with language or scripting key
   -- Returns boolean
   local r
   Fetch( "ISO15924" )
   if Config.ISO15924 then
       r = Config.ISO15924.isTrans( ask, assign, Config.site )
   end
   return r

end -- familiar()


local function family()

   -- attempt to load local config
   if not Config.data then
       local sub = string.format( "%s/config", Frame():getTitle() )
       local lucky
       lucky, Config.data = pcall( mw.loadData, sub )
       if type( Config.data ) == "table" then
           for k, v in pairs( Config.data ) do
               Config[ k ] = v
           end -- for k, v
       else
           Config.data = sub .. " not found"
       end
   end

end -- family()


local function fault( alert, about, assign )

   -- Format message with class="error" or similar
   --     alert   -- string, with message key
   --     about   -- string, with explanation
   --     assign  -- true, when standard category to be fired
   -- Returns message with markup
   local story = factory( alert )
   local env   = Frame():getParent()
   local err   = mw.html.create( "span" )
   local r
   err:addClass( Config.errClass )
      :css( { ["margin-left"]  = "1em",
              ["margin-right"] = "1em" } )
   family()
   if env then
       story = string.format( "%s – %s",
                              env:getTitle(), story )
   end
   if about then
       story = string.format( "%s %s", story, about )
   end
   if Config.errClasses then
       err:addClass( Config.errClasses )
   end
   err:wikitext( story )
   r = Fetch( "TemplUtl" )
   if Config.TemplUtl then
       r = Config.TemplUtl.failure( tostring( err ),
                                    not Config.errHide,
                                    false,
                                    Frame() )
   end
   if assign  and  type( Config.errCat ) == "string" then
       if Config.errNS then
           local ns = mw.title.getCurrentTitle().namespace
           local st = type( Config.errNS )
           if st == "string" then
               local space  = string.format( ".*%%s%d%%s.*", ns )
               local spaces = string.format( " %s ", Config.errNS )
               if spaces:match( space ) then
                   Config.errNS = false
               end
           elseif st == "table" then
               for i = 1, #Config.errNS do
                   if Config.errNS[ i ] == ns then
                       Config.errNS = false
                       break    -- for i
                   end
               end -- for i
           end
       end
       if not Config.errNS then
           r = string.format( "%s", r, Config.errCat )
       end
   end
   return r

end -- fault()


local function features( apply, assign )

   -- Equip element with script attributes
   --     apply   -- mw.html element
   --     assign  -- string, with script code
   if type( Config.percents ) == "table" then
       local more = Config.percents[ assign ]
       if more then
           apply:css( "font-size",  string.format( "%d%%", more ) )
       end
   end

end -- features()


local function fill( apply )

   -- Expand language name template
   --     apply   -- string, with code
   -- Returns string, identical apply in case of error
   local r = apply
   local template
   family()
   template = Config.tmplLang
   if type( template ) == "table" then
       local source = template.title
       if type( source ) ~= "string" then
           if type( template.namePat ) == "string"  and
              template.namePat:find( "%s", 1, true ) then
               source = string.format( template.namePat, apply )
           end
       end
       if type( source ) == "string" then
           local lucky, s = pcall( Fulfil, source )
           if lucky then
               r = s
           end
       end
   end
   return r

end -- fill()


local function first( assign, apply )

   -- Equip element with TemplateStyles
   --     assign  -- string, with script code
   --     apply   -- mw.html element, or false
   -- Returns string, perhaps with tag
   local r
   if type( Config.tmplStyles ) == "table" then
       local source = Config.tmplStyles[ assign ]
       if source then
           r = Frame():extensionTag( "templatestyles",
                                     nil,
                                     { src = source } )
           if apply then
               apply:wikitext( r )
           end
       end
   end
   return r or ""

end -- first()


local function fit( acquire )

   -- Retrieve script code for language
   --     acquire  -- string, with language code
   -- Returns string or false
   local r
   local iso639script = Friend().iso639script
   if type( iso639script ) == "table" then
       r = iso639script[ acquire ]
       if type( r ) == "table" then
           r = r[ 1 ]
       end
   end
   return r

end -- fit()


local function flag( already, apply )

   -- Check whether space separated classes contain
   --     already  -- string or false, with classes
   --     apply    -- string or false, with single class
   -- Returns true, if apply is in already
   local r
   if already then
       if apply then
           local classes = mw.text.split( already, "%s+" )
           local plus    = mw.text.split( apply, "%s+" )
           local single
           for i = 1, #plus do
               single = plus[ i ]
               for k = 1, #classes do
                   if classes[ k ] == single then
                       single = false
                       break -- for k
                   end
               end -- for k
               if single then
                   table.insert( classes, single )
               end
           end -- for i
           r = table.concat( classes, " " )
       else
           r = already
       end
   else
       r = apply
   end
   return r

end -- flag()


local function flatten( apply )

   -- Trim any whitespace, even HTML encoded
   --     apply   -- string
   -- Returns string, or false if empty
   local r = apply
   if r:find( "&" ) then
       r = r:gsub( " ",    " " )
            :gsub( " ", " " )
            :gsub( "‌",   "‌" )
            :gsub( "‍",    "‍" )
            :gsub( "‎",    "‎" )
            :gsub( "‏",    "‏" )
       if r:find( "&#" ) then
           local f = function ( ax, an )
                         local k
                         if ax == "x" then
                             k = 16
                         else
                             k = 10
                         end
                         return mw.ustring.char( tonumber( an, k ) )
                     end
           r = r:gsub( "&#(x?)(%x+);",  f )
       end
   end
   if not Config.patternWS then
       Config.patternWS = mw.ustring.char(   91,
                                              1, 45, 32,
                                            160,
                                           8194, 45, 8207,
                                           8239,
                                             93,
                                             43 )
   end
   r = mw.ustring.gsub( r,  "^" .. Config.patternWS,  "" )
   r = mw.ustring.gsub( r,  Config.patternWS .. "$",  "" )
   if r == "" then
       r = false
   end
   return r

end -- flatten()


local function follow( affine )

   -- Retrieve appropriate separator
   --     affine    -- string or nil, with code of script
   -- Returns string, with separator (comma) or not
   local r
   if affine then
       if Config.sepComma:find( affine, 1, true ) then
           r = ","
       else
           r = ""
       end
   else
       r = ","
   end
   return r

end -- follow()


local function foreign( apply, acquire, advanced, affine )

   -- Format text in some language
   --     apply     -- string, with text
   --     acquire   -- string, with basic code of language
   --     advanced  -- string, with full code of language
   --     affine    -- string or nil, with code of script
   -- Returns string
   local story  = apply
   local script = affine
   local p = { ["font-weight"] = "normal" }
   local ltr, r
   if advanced == acquire then
       ltr = not mw.language.new( acquire ):isRTL()
   elseif not script  and  advanced:find( "-", 3, true ) then
       local parts = mw.text.split( advanced, "-", true )
       if #parts > 1  and  parts[ 2 ]:match( "^%u%l%l%l$" ) then
           script = parts[ 2 ]
       end
   end
   if script then
       local rtl = Friend().rtl
       if type( rtl ) == "table" then
           ltr = not rtl[ script ]
       end
   end
   if ltr then
       local e = mw.html.create( "span" )
                        :attr( "lang", advanced )
       local s = "normal"
       if acquire == facility()
          and Config.owns[ acquire ]  then
           story = string.format( Config.owns[ acquire ], apply )
       elseif advanced == acquire  and  Config.site == "Latn"   or
              advanced == acquire .. "-Latn" then
           s = "italic"
       end
       p["font-style"] = s
       e:css( p )
        :wikitext( story )
       r = tostring( e )
   else
       p["font-style"] = "normal"
       r = Export.fold( { css       = p,
                          SCRIPTING = script },
                        { [ 1 ] = acquire,
                          [ 2 ] = apply,
                          lang  = advanced } )
   end
   return r

end -- foreign()


local function foreigns( ahead, aliens, alone )

   -- Create list of translations
   --     ahead   -- string, with leading separator
   --     aliens  -- sequence table, with assignment tables
   --     alone   -- boolean, no other elements in collection yet
   -- Returns string, starting with separator (comma)
   local pars = { }
   local sep  = ahead
   local lone = alone
   local r    = ""
   local k, lucky, s, t
   family()
   facility()
   Fetch( "Multilingual" )
   for i = 1, #aliens do
       t = aliens[ i ]
       if t.short == Config.standard then
           t.m = Config.keyProject
           t.n = 0
       else
           k = Config.orderOther[ t.short ]
           if k then
               t.n = k
               t.m = Config.keyAncient
           end
           lone = false
       end
       t.n = t.m + t.n
   end -- for i
   table.sort( aliens,
               function ( a1, a2 )
                   return ( a1.n < a2.n )
               end )
   for i = 1, #aliens do
       t = aliens[ i ]
       if lone then
           s   = ""
           sep = ""
       else
           s = false
           if t.short == Config.slang  and
              t.script  and
              t.n > 0 then
               Fetch( "ISO15924" )
               if Config.ISO15924 then
                   s = Config.ISO15924.scriptName( t.script )
                   if s == t.script then
                       s = false
                   end
               end
           end
           if not s  and  t.n > Config.keyProject then
               s = fill( t.short )
           end
           if not s then
               Fetch( "Multilingual" )
               s = mw.language.fetchLanguageName( t.short,
                                                  Config.standard )
               if Config.Multilingual  and
                  Config.Multilingual.isMinusculable( s ) then
                   s = mw.ustring.lower( mw.ustring.sub( s, 1, 1 ) )
                       .. mw.ustring.sub( s, 2 )
               end
           end
       end
       r   = string.format( "%s%s %s %s",
                            r,
                            sep,
                            s,
                            foreign( t.story,
                                     t.short,
                                     t.slang,
                                     t.script ) )
       sep = follow( t.script )
   end -- for i
   return r

end -- foreigns()


local function friend( assigned, apply )

   -- Transcription unit
   --     assigned  -- string, transcription ID
   --     apply     -- string, transcription text
   -- Returns string
   local e = mw.html.create( "span" )
                    :addClass( Config.site )
                    :attr( "lang",
                           string.format( "%s-%s",
                                          Config.slang,
                                          Config.site ) )
                    :css( { ["font-weight"] = "normal" } )
                    :wikitext( apply )
   local r, s
   if Config.site == "Latn" then
       s = "italic"
   else
       s = "normal"
   end
   e:css( { ["font-style"] = s } )
   if type( Config.transys ) == "table"  and
      type( Config.transys[ assigned ] ) == "table" then
       local transys = Config.transys[ assigned ]
       if transys.class then
           e:addClass( transys.class )
       end
       if transys.show then
           s = transys.show
       else
           s = assigned
       end
       if transys.support then
           if s == transys.support then
               s = string.format( "%s", s )
           else
               s = string.format( "%s", transys.support, s )
           end
       end
   else
       s = assigned
   end
   r = string.format( "%s %s%s",
                      s,  first( Config.site ),  tostring( e ) )
   return r

end -- friend()


local function frontend( action, argsF, argsT, about )

   -- Template service
   --     action  -- string, "flat" or "full" etc.
   --     argsF   -- table, with #invoke parameters, or false
   --     argsT   -- table, with template parameters
   --     about   -- string or nil, invocation name
   -- Returns frame
   local lucky, r
   lucky, r = pcall( Export[ action ], argsF, argsT )
   if not lucky then
       local e = mw.html.create( "span" )
                        :attr( "class", "error" )
       if about then
           r = string.format( "{{%s}} %s",  about,  r )
       end
       e:wikitext( r )
       r = tostring( e )
   end
   return r

end -- frontend()


local function frontier( frame, action )

   -- Template transclusion
   --     frame   -- object
   --     action  -- string, "flat" or "full" etc.
   -- Returns appropriate string
   Config.frame = frame
   return frontend( action,
                    frame.args,
                    frame:getParent().args,
                    frame:getTitle() )

end -- frontier()


local function full( arglist )

   -- Finalize invocation of template
   --     arglist  -- table, with parameters
   -- Returns appropriate string
   local r
   if arglist.Text1 then
       local slang = Config.slang
       local lone  = true
       local s, sep
       if not Config.scripting then
           Config.scripting = fit( Config.slang )
       end
       sep = follow( Config.scripting )
       if Config.scripting == Config.site then
           if slang == facility() then
               arglist.style = false
           elseif not arglist.style then
               arglist.style = "font-style:italic"
           end
           sep = follow( Config.site )
           if arglist.Text2 then
               arglist.Text2 = fault( "errInvalid",
                                      Config.site .. "+2=",
                                      true )
           end
       elseif not Config.low  and  not arglist.style then
           arglist.style = "font-style:normal"
       end
       if Config.state then
           slang = string.format( "%s-%s", slang, Config.state )
       end
       if Config.scripting then
           slang = string.format( "%s-%s", slang, Config.scripting )
       end
       r = Export.format( slang,
                          arglist.Text1,
                          arglist.style,
                          arglist.Audio,
                          arglist.class )
       if arglist.Text2 then
           local e = mw.html.create( "span" )
                            :addClass( Config.site )
                            :attr( "lang",
                                   string.format( "%s-%s",
                                                  Config.slang,
                                                  Config.site ) )
                            :css( { ["font-weight"] = "normal" } )
                            :wikitext( arglist.Text2 )
                            if Config.site == "Latn" then
                                s = "italic"
                            else
                                s = "normal"
                            end
                            e:css( { ["font-style"] = s } )
           r    = string.format( "%s %s%s",
                                 r,
                                 first( Config.site ),
                                 tostring( e ) )
           sep  = follow( Config.site )
           lone = false
       end
       if arglist.trans then
           local e
           for i = 1, #arglist.trans do
               e   = arglist.trans[ i ]
               r   = string.format( "%s%s %s",
                                    r,
                                    sep,
                                    friend( e.system, e.story ) )
               sep = follow( Config.site )
           end   -- for i
           lone = false
       end
       if not Config.low then
           if Config.scripting then
               s = false
               if arglist[ Config.scripting ]  and
                  not arglist.Text2 then
                   Fetch( "ISO15924" )
                   if Config.ISO15924 then
                       s = Config.ISO15924
                                         .scriptName( Config.scripting )
                   end
               end
               if not s  and  Config.service then
                   s = facet( Config.service )
               end
           elseif Config.service then
               s = Config.service
           else
               s = false
           end
           if s then
               if arglist.later then
                   r    = string.format( "%s (%s)", r, s )
                   lone = false
               else
                   r = string.format( "%s %s", s, r )
               end
           end
       end
       if arglist.IPA then
           local params = { [1] = arglist.IPA }
           s    = Frame():expandTemplate{ title = Config.ipa,
                                          args  = params }
           r    = string.format( "%s [%s]", r, s )
           sep  = follow()
           lone = false
       end
       if arglist.trsl then
           r = r .. foreigns( sep, arglist.trsl, lone )
       end
   elseif arglist.Text2 then
       r = fault( "errInvalid", "|1=|2=", true )
   else
       if Config.sole then
           r = Config.sole
       else
           r = Config.service
       end
   end
   return r

end -- full()


local function furnish( argsF, argsT )

   -- General entry point; basic argument consumption
   --     argsF  -- table, with #invoke parameters, or false
   --     argsT  -- table, with template parameters
   -- Returns appropriate string
   local parIgnore = { }
   local r = { }
   local s
   if argsF then
       if argsF.errHide ~= nil then
           Config.errHide = faculty( argsF.errHide )
       end
       Config.errCat     = argsF.errCat
       Config.errClasses = argsF.errClasses
       Config.errNS      = argsF.errNS
       if argsF.ELEMENT and argsF.ELEMENT:match( "^%a+$" ) then
           Config.scope = argsF.ELEMENT:lower()
       end
       if argsF.SUITABLE then
           local params = mw.text.split( argsF.SUITABLE, " ", true )
           for k, v in pairs( params ) do
               parIgnore[ v ] = true
           end -- for k, v
       end
   end
   if Config.scripting == "" then
       Config.scripting = false
   end
   if Config.scripting then
       table.insert( Config.params, Config.scripting )
   end
   if type( argsT ) == "table" then
       local n = table.maxn( Config.params )
       local script, unknown
       for k, v in pairs( argsT ) do
           v = flatten( v )
           s = type( k )
           if s == "number" then
               if Config.low then
                   k = k - 1
               end
               if k <= 2 then
                   if v then
                       if k == 0 then
                           k = false
                       else
                           if k == 1 then
                               r.Text1 = v
                               k       = false
                           elseif Config.scripting == Config.site then
                               k = "2"
                           else
                               r.Text2 = v
                               k       = false
                           end
                       end
                   else
                       if not Config.lenient then
                           -- LEGACY
                           k = string.format( "%s %s, |%d=",
                                              "Sprachvorlage:",
                                              factory( "errEmpty" ),
                                              k )
                           mw.addWarning( k )
                       end
                       k = false
                   end
               else
                   k = tostring( k )
               end
           elseif parIgnore[ k ] then
               k = false
           elseif k:match( "^%l%l%l?%-?" ) then
               s = k:match( "^(%l%l%l?)$" )  or
                   k:match( "^(%l%l%l?)%-%u%u$" )  or
                   k:match( "^(%l%l%l?)%-%u%l%l%l$" )
               if v then
                   script = k:match( "^%l%l%l?%-(%u%l%l%l)$" )
               else
                   k = false
               end
               if v   and   s   and
                  ( s ~= Config.slang  or
                    script ~= Config.scripting ) then
                   local legal = mw.language.isSupportedLanguage( s )
                   if not legal then
                       legal = ( s ~= fill( s ) )
                   end
                   if legal then
                       local state = k:match( "^%l%l%l?%-(%u%u)$" )
                       local m
                       if s == Config.slang then
                           m = Config.keyBase
                       else
                           m = Config.keyTranslate
                       end
                       if not script then
                           script = fit( s )
                           if script then
                               k = string.format( "%s-%s", s, script )
                           end
                       end
                       if not r.trsl then
                           r.trsl = { }
                       end
                       table.insert( r.trsl,
                                     { m      = m,
                                       n      = #r.trsl + 1,
                                       script = script,
                                       short  = s,
                                       slang  = k,
                                       state  = state,
                                       story  = v } )
                       k = false
                   end
               end
           elseif k:match( "^%u%u+%d*%.?%d*%-?%u*%d*$" ) then
               if familiar( k, Config.scripting )  or
                  familiar( k, Config.slang ) then
                   if v then
                       if not r.trans then
                           r.trans = { }
                       end
                       table.insert( r.trans,
                                     { system = k,
                                       story  = v } )
                   end
                   k = false
               end
           elseif k:match( "^%u%l%l%l$" ) then
               if k == Config.scripting then
                   if faculty( v ) then
                       r[ k ] = true
                   end
               else
                   if not r.trsl then
                       r.trsl = { }
                   end
                   table.insert( r.trsl,
                                 { m      = 100,
                                   n      = #r.trsl + 1,
                                   script = k,
                                   short  = Config.slang,
                                   slang  = string.format( "%s-%s",
                                                           Config.slang,
                                                           k ),
                                   story  = v } )
               end
               k = false
           end
           if k then
               for i = 1, n do
                   if Config.params[ i ] == k then
                       if v then
                           r[ k ] = v
                       end
                       k = false
                       break -- for i
                   end
               end -- for i
           end
           if k then
               if not unknown then
                   unknown = { }
               end
               table.insert( unknown, k )
           end
       end -- for k, v
       if r.demo  or  faculty( r.NoCat ) then
           Config.errCat  = 0
           Config.errHide = false
       end
       r.later = faculty( r.nachgestellt )
       if r.b and Config[ "OBSOLETING-bwd" ] then
           if r.de then
               r = fault( "errDoubled", "'de=' und 'b='", true )
           else
               r.de = r.b
           end
       end
       if r.w and Config[ "OBSOLETING-bwd" ] then
           if r.Text2 then
               r = fault( "errDoubled", "'2=' und 'w='", true )
           else
               r.Text2 = r.w
           end
       end
       if unknown then
           local e = mw.html.create( "code" )
                            :wikitext( table.concat( unknown, " " ) )
           r = fault( "errUnkown",
                      string.format( "'%s'",  tostring( e ) ),
                      true )
       end
   end
   if type( r ) == "table" then
       r = full( r )
   end
   return r

end -- furnish()


Export.flat = function ( argsF, argsT, auxilary )

   -- Invocation of basic language template
   --     argsF     -- table, with #invoke parameters, or false
   --     argsT     -- table, with template parameters
   --     auxilary  -- Multilingual library, or false
   -- Returns appropriate string
   local r
   if type( auxilary ) == "table" then
       Config.Multilingual = auxilary
   else
       r = Fetch( "Multilingual" )
   end
   if Config.Multilingual then
       local slang = argsT[ 1 ]
       local show  = argsT[ 2 ]
       if slang then
           slang = mw.text.trim( slang )
           if slang == "" then
               slang = false
           end
       end
       if show then
           show = flatten( show )
       end
       if slang and show then
           local q = Config.Multilingual.getLang( slang )
           if q and q.legal then
               Config.lenient   = faculty( argsT.lenient )
               Config.low       = true
               Config.slang     = q.base
               Config.state     = q.region
               Config.scripting = q.script
               r = furnish( argsF, argsT )
           else
               local e = mw.html.create( "span" )
                                :attr( "lang", slang )
                                :wikitext( show )
               local list = ( q and q.scream )
               local s
               if q and q.suggest then
                   local say = factory( "errSuggest" )
                   s = string.format( say, slang, q.suggest )
               else
                   s = slang
               end
               r = tostring( e ) ..
                   fault( "errInvalid",  s,  not list )
               if list then
                   r = string.format( "%s", r, q.scream )
               end
           end
       else
           r = fault( "errMissing", false, true )
           if show then
               r = show .. r
           end
       end
   end
   return r

end -- Export.flat()


Export.fold = function ( argsF, argsT )

   -- Invocation of RTL template
   --     argsF  -- table, with #invoke parameters, or false
   --     argsT  -- table, with template parameters
   -- Returns appropriate string, or nil
   local params = { }
   local r, s, slang
   for k, v in pairs( argsT ) do
       if v then
           v = mw.text.trim( v )
           if v ~= "" then
               params[ k ] = v
           end
       end
   end -- for k, v
   if params[ 2 ] then
       s = params[ 2 ]:gsub( mw.ustring.char( 0x202A ), "" )
                      :gsub( mw.ustring.char( 0x202B ), "" )
                      :gsub( mw.ustring.char( 0x202C ), "" )
                      :gsub( mw.ustring.char( 0x202D ), "" )
                      :gsub( mw.ustring.char( 0x202E ), "" )
                      :gsub( mw.ustring.char( 0x2066 ), "" )
                      :gsub( mw.ustring.char( 0x2067 ), "" )
                      :gsub( mw.ustring.char( 0x2068 ), "" )
                      :gsub( mw.ustring.char( 0x2069 ), "" )
                      :gsub( mw.ustring.char( 8206 ), "‎" )
                      :gsub( "&#0*8206;",             "‎" )
                      :gsub( "&#x0*200[Ee];",         "‎" )
                      :gsub( mw.ustring.char( 8207 ), "‏" )
                      :gsub( "&#0*8207;",             "‏" )
                      :gsub( "&#x0*200[Ff];",         "‏" )
       if s:find( "&", 1, true ) then
           local shift = "^‏%s*"
           while s:match( shift ) do
               s = s:gsub( shift, "" )
           end -- while
           shift = "%s*‎$"
           while s:match( shift ) do
               s = s:gsub( shift, "" )
           end -- while
           if s == "" then
               s = false
           end
       end
   end
   if s   and
      s:sub( 1, 5 ) == "<bdo "   and
      s:find( "^<bdo [^<>]+><bdi [^<>]+>[^<>]+$" ) then
       r = s
       s = false
   end
   if s then
       local bdi    = mw.html.create( "bdi" )
                             :attr( "dir", "rtl" )
                             :css( "unicode-bidi", "isolate" )
                             :wikitext( s )
       local bdo    = mw.html.create( "bdo" )
                             :attr( "dir", "ltr" )
       local slang  = params[ 1 ]
       local script = argsF.SCRIPTING
       local state  = argsF.STATE
       local selector
       family()
       if slang then
           if slang:find( "-", 3, true ) then
               local parts = mw.text.split( slang, "-", true )
               slang = parts[ 1 ]
               for i = 2, #parts do
                   if parts[ i ]:match( "^%u%u$" ) then
                       state = parts[ i ]
                   elseif parts[ i ]:match( "^%u%l%l%l$" ) then
                       script = parts[ i ]
                   end
               end -- for i
           end
           slang = slang:lower()
       else
           slang = "ar"
       end
       if script then
           first( script, bdo )
           features( bdi, script )
           selector = script
           if type( Config.classScript ) == "table" then
               selector = flag( selector, Config.classScript[ script ] )
           end
           s = string.format( "%s-%s", slang, script )
       else
           s = slang
       end
       if state then
           s = string.format( "%s-%s", s, state )
       end
       bdi:attr( "lang", s )
       selector = flag( selector, argsF.class )
       selector = flag( selector, params.class )
       if selector then
           bdi:addClass( selector )
       end
       if argsF.css then
           bdi:css( argsF.css )
       end
       if argsF.style then
           bdi:cssText( argsF.style )
       end
       if params.style then
           bdi:cssText( params.style )
       end
       bdo:node( bdi )
       r = tostring( bdo )
   end
   return r

end -- Export.fold()


Export.format = function ( alien, apply, appear, audio, alike )

   -- Markup foreign language text
   --     alien   -- string, with language code
   --     apply   -- string, with text
   --     appear  -- string, with additional CSS, or nil
   --     audio   -- string, with title of an audio file, or nil
   --     alike   -- string, with additional class(es), or nil
   -- Returns appropriate string with HTML tag
   local ltr = true
   local r, script, selector, slang, state
   family()
   if alien:find( "-", 3, true ) then
       local parts = mw.text.split( alien, "-", true )
       slang = parts[ 1 ]
       for i = 2, #parts do
           if parts[ i ]:match( "^%u%u$" ) then
               state = parts[ i ]
           elseif parts[ i ]:match( "^%u%l%l%l$" ) then
               script = parts[ i ]
           end
       end -- for i
   else
       slang = alien
   end
   slang = slang:lower()
   if not script then
       script = fit( slang )
   end
   if script then
       local rtl = Friend().rtl
       if type( rtl ) == "table" then
           ltr = not rtl[ script ]
       end
       if script ~= Config.site then
           selector = script
           if type( Config.classScript ) == "table" then
               selector = flag( selector, Config.classScript[ script ] )
           end
       end
   end
   selector = flag( selector, alike )
   if ltr then
       local scope = Config.scope or "span"
       local elem = mw.html.create( scope )
       local story = apply
       local set
       if script then
           set = string.format( "%s-%s", slang, script )
           features( elem, script )
       else
           set = slang
       end
       if state then
           set = string.format( "%s-%s", set, state )
       end
       elem:attr( "lang", set )
       if selector then
           elem:addClass( selector )
       end
       if appear then
           elem:cssText( appear )
       end
       if scope == "span" then
           story = story:gsub( "\n", " " )
       end
       elem:wikitext( story )
       r = tostring( elem )
       if script  and  script ~= Config.site then
           local lucky, x = pcall( require,
                                   "Module:Vorlage:lang/Zwiebelfisch" )
           r = first( script ) .. r
           if type( x ) == "table"  and
              type( x.finder ) == "function" then
               x = x.finder( apply, script )
               if type( x ) == "string" then
                   r = r .. x
               end
           end
       end
   else
       r = Export.fold( { SCRIPTING = script },
                        { [ 1 ] = slang,
                          [ 2 ] = apply,
                          class = selector,
                          style = appear } )
   end
   if mw.text.unstrip( apply ):sub( 1, 1 ) == "<" then
       local seek = "^(<([sd][paniv]+) [^>]+>)%1(.+)(</%2>)%4$"
       local s, start, story, stop
       start, s, story, stop = mw.text.unstrip( r ):match( seek )
       if story then
           r = Export.format( slang, story, appear, audio, alike )
       end
   end
   if audio and Config.tmplAudio then
       local params = { [ Config.tmplAudio.filepar ] = audio,
                        [ Config.tmplAudio.textpar ] = r }
       r = Frame():expandTemplate{ title = Config.tmplAudio.title,
                                   args  = params }
   end
   return r or ""

end -- Export.format()


Export.full = function ( argsF, argsT )

   -- Invocation of language name template
   --     argsF  -- table, with #invoke parameters, or false
   --     argsT  -- table, with template parameters
   -- Returns appropriate string
   if argsF then
       Config.lenient   = faculty( argsF.lenient )
       Config.long      = faculty( argsF.LONG )
       Config.scripting = argsF.SCRIPTING
       Config.service   = argsF.SERVICE
       Config.slang     = argsF.CODE
       Config.sole      = argsF.SOLE
       if argsF["OBSOLETING-bwd"] then
           table.insert( Config.params, "b" )
           table.insert( Config.params, "d" )
           table.insert( Config.params, "w" )
       end
   end
   Config.low = false
   return furnish( argsF, argsT )

end -- Export.full()


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
   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 vsn = entity:formatPropertyValues( "P348" )
               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()


-- Export local p = { }


p.test = function ( action, argsF, argsT )

   --     action  -- string, "flat" or "full" etc.
   --     argsF   -- table, with #invoke parameters, or false
   --     argsT   -- table, with template parameters
   return frontend( action, argsF, argsT )

end -- p.test()


p.flat = function ( frame )

   return frontier( frame, "flat" )

end -- p.flat()


p.fold = function ( frame )

   return frontier( frame, "fold" )  or   ""

end -- p.fold()


p.full = function ( frame )

   return frontier( frame, "full" )

end -- p.full()


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.lang = function ()

   return Export

end -- p.lang()

return p