Modul:Multilingual: Unterschied zwischen den Versionen

Aus FreeWiki
Zur Navigation springen Zur Suche springen
te>PerfektesChaos
(2016-11-29)
te>PerfektesChaos
(2017-11-24)
Zeile 1: Zeile 1:
 
local Multilingual = { suite  = "Multilingual",
 
local Multilingual = { suite  = "Multilingual",
                       serial = "2016-11-29" }
+
                       serial = "2017-11-24" }
  
  
Zeile 501: Zeile 501:
 
     -- Precondition:
 
     -- Precondition:
 
     --    ask  -- language name
 
     --    ask  -- language name
 +
    -- Postcondition:
 +
    --    Returns boolean
 
     local cnf = fetch( "Multilingual/config", true )
 
     local cnf = fetch( "Multilingual/config", true )
 
     local r = true
 
     local r = true
     if cnf and  type( cnf.stopMinusculization ) == "string" then
+
     if cnf then
 
         local s = string.format( " %s ", ask:lower() )
 
         local s = string.format( " %s ", ask:lower() )
         if cnf.stopMinusculization:find( s, 1, true ) then
+
         if type( cnf.stopMinusculization ) == "string"
 +
          and  cnf.stopMinusculization:find( s, 1, true ) then
 
             r = false
 
             r = false
 +
        end
 +
        if r  and  type( cnf.seekMinusculization ) == "string"
 +
          and  cnf.seekMinusculization:find( s, 1, true )
 +
          and  type( cnf.scanMinusculization ) == "string" then
 +
            local scan = ask:gsub( "[%(%)]", " " ) .. " "
 +
            if not cnf.scanMinusculization:find( scan ) then
 +
                r = false
 +
            end
 
         end
 
         end
 
     end
 
     end
Zeile 636: Zeile 647:
 
     --    scream    -- category title in case of error
 
     --    scream    -- category title in case of error
 
     --    split      -- split pattern, if list expected
 
     --    split      -- split pattern, if list expected
     --    separator  -- list separator, else assembly
+
     --    separator  -- list separator, else split
 
     --    start      -- prepend first element, if any
 
     --    start      -- prepend first element, if any
 
     local r
 
     local r

Version vom 25. November 2017, 17:28 Uhr

local Multilingual = { suite = "Multilingual",

                      serial = "2017-11-24" }


-- local globals local Frame


local favorite = function ()

   -- Postcondition:
   --     Returns code of curent project language
   if not Multilingual.self then
       Multilingual.self = mw.language.getContentLanguage():getCode()
                                                           :lower()
   end
   return Multilingual.self

end -- favorite()


local fetch = function ( access, allow )

   -- Attach config or library module
   -- Precondition:
   --     access  -- module title
   --     allow   -- permit non-existence
   -- Postcondition:
   --     Returns  table or false, with library
   --     Throws error, if not available
   if type( Multilingual.ext ) ~= "table" then
       Multilingual.ext = { }
   end
   if Multilingual.ext[ access ] == false then
   elseif not Multilingual.ext[ access ] then
       local lucky, got = pcall( require, "Module:" .. access )
       if lucky then
           if type( got ) == "table" then
               Multilingual.ext[ access ] = got
               if type( got[ access ] ) == "function" then
                   Multilingual.ext[ access ] = got[ access ]()
               end
           end
       end
       if type( Multilingual.ext[ access ] ) ~= "table" then
           if allow then
               Multilingual.ext[ access ] = false
           else
               got = string.format( "Module:%s invalid", access )
               error( got, 0 )
           end
       end
   end
   return Multilingual.ext[ access ]

end -- fetch()


function find( ask, alien )

   -- Derive language code from name
   -- Precondition:
   --     ask    -- language name, downcased
   --     alien  -- language code of ask
   -- Postcondition:
   --     nil, or string
   local codes = mw.language.fetchLanguageNames( alien, "all" )
   local r
   for k, v in pairs( codes ) do
       if mw.ustring.lower( v ) == ask then
           r = k
           break -- for k, v
       end
   end -- for k, v
   if not r then
       r = Multilingual.fair( ask )
   end
   return r

end -- find()


function isSupported( ask, accept )

   -- Is ask to be supported by application?
   -- Precondition:
   --     ask     -- lowercase code
   --     accept  -- space separated/terminated list of lowercase codes
   -- Postcondition:
   --     nil, or else
   local seek = string.format( " %s ", ask )
   local supported = string.format( " %s", accept )
   return supported:find( seek, 1, true )

end -- isSupported()


Multilingual.fair = function ( ask )

   -- Format language specification according to RFC 5646 etc.
   -- Precondition:
   --     ask  -- string or table, as created by .getLang()
   -- Postcondition:
   --     Returns string, or false
   local s = type( ask )
   local q, r
   if s == "table" then
       q = ask
   elseif s == "string" then
       q = Multilingual.getLang( ask )
   end
   if q  and
      q.legal  and
      mw.language.isKnownLanguageTag( q.base ) then
       r = q.base
       if q.n > 1 then
           local order = { "extlang",
                           "script",
                           "region",
                           "other",
                           "extension" }
           for i = 1, #order do
               s = q[ order[ i ] ]
               if s then
                   r =  string.format( "%s-%s", r, s )
               end
           end -- for i
       end
   end
   return r or false

end -- Multilingual.fair()


Multilingual.findCode = function ( ask )

   -- Retrieve code of local (current project or English) language name
   -- Precondition:
   --     ask  -- string, with presumable language name
   --             A code itself will be identified, too.
   -- Postcondition:
   --     Returns string, or false
   local seek = mw.text.trim( ask )
   local r = false
   if #seek > 1 then
       if seek:find( "[", 1, true ) then
           seek = fetch( "WLink" ).getPlain( seek )
       end
       seek = mw.ustring.lower( seek )
       if Multilingual.isLang( seek ) then
           r = Multilingual.fair( seek )
       else
           local slang = favorite()
           r = find( seek, slang )
           if not r  and  slang ~= "en" then
               r = find( seek, "en" )
           end
       end
   end
   return r

end -- Multilingual.findCode()


Multilingual.format = function ( apply, alien, alter, active, alert,

                                frame, assembly, adjacent, ahead )
   -- Format one or more languages
   -- Precondition:
   --     apply     -- string with language list or item
   --     alien     -- language of the answer
   --                  -- nil, false, "*": native
   --                  -- "!": current project
   --                  -- "#": code, downcased, space separated
   --                  -- "-": code, mixcase, space separated
   --                  -- any valid code
   --     alter     -- capitalize, if "c"; downcase all, if "d"
   --                  capitalize first item only, if "f"
   --                  downcase every first word only, if "m"
   --     active    -- link items, if true
   --     alert     -- string with category title in case of error
   --     frame     -- if available
   --     assembly  -- string with split pattern, if list expected
   --     adjacent  -- string with list separator, else assembly
   --     ahead     -- string to prepend first element, if any
   -- Postcondition:
   --     Returns string, or false if apply empty
   local r = false
   if apply then
       local slang
       if assembly then
           local bucket = mw.text.split( apply, assembly )
           local shift = alter
           local separator
           if adjacent then
               separator = adjacent
           elseif alien == "#"  or  alien == "-" then
               separator = " "
           else
               separator = assembly
           end
           for k, v in pairs( bucket ) do
               slang = Multilingual.format( v, alien, shift, active,
                                            alert )
               if slang then
                   if r then
                       r = string.format( "%s%s%s",
                                          r, separator, slang )
                   else
                       r = slang
                       if shift == "f" then
                           shift = "d"
                       end
                   end
               end
           end -- for k, v
           if r and ahead then
               r = ahead .. r
           end
       else
           local single = mw.text.trim( apply )
           if single == "" then
               r = false
           else
               local lapsus, slot
               slang = Multilingual.findCode( single )
               if slang then
                   if alien == "-" then
                       r = slang
                   elseif alien == "#" then
                       r = slang:lower()
                   else
                       r = Multilingual.getName( slang, alien )
                       if active then
                           local cnf = fetch( "Multilingual/config",
                                              true )
                           if cnf  and
                              type( cnf.getLink ) == "function" then
                               if not frame then
                                   if not Frame then
                                       Frame = mw.getCurrentFrame()
                                   end
                                   frame = Frame
                               end
                               slot = cnf.getLink( slang, frame )
                               if slot then
                                   local wlink = fetch( "WLink" )
                                   slot = wlink.getTarget( slot )
                               else
                                   lapsus = alert
                               end
                           end
                       end
                   end
               else
                   r = single
                   if active then
                       local title = mw.title.makeTitle( 0, single )
                       if title.exists then
                           slot = single
                       end
                   end
                   lapsus = alert
               end
               if not r then
                   r = single
               elseif alter == "c" or alter == "f" then
                   r = mw.ustring.upper( mw.ustring.sub( r, 1, 1 ) )
                       .. mw.ustring.sub( r, 2 )
               elseif alter == "d" then
                   if Multilingual.isMinusculable( slang or "" ) then
                       r = mw.ustring.lower( r )
                   end
               elseif alter == "m" then
                   if Multilingual.isMinusculable( slang or "" ) then
                       r = mw.ustring.lower( mw.ustring.sub( r, 1, 1 ) )
                           .. mw.ustring.sub( r, 2 )
                   end
               end
               if slot then
                   if r == slot then
                       r = string.format( "%s", r )
                   else
                       r = string.format( "%s", slot, r )
                   end
               end
               if lapsus and alert then
                   r = string.format( "%s", r, alert )
               end
           end
       end
   end
   return r

end -- Multilingual.format()


Multilingual.getBase = function ( ask )

   -- Retrieve base language from possibly combined ISO language code
   -- Precondition:
   --     ask  -- language code
   -- Postcondition:
   --     Returns string, or false
   local r
   if ask then
       local slang = ask:match( "^%s*(%a%a%a?)-?%a*%s*$" )
       if slang then
           r = slang:lower()
       else
           r = false
       end
   else
       r = false
   end
   return r

end -- Multilingual.getBase()


Multilingual.getLang = function ( ask )

   -- Retrieve components of a RFC 5646 language code
   -- Precondition:
   --     ask  -- language code with subtags
   -- Postcondition:
   --     Returns table with formatted subtags
   --             .base
   --             .region
   --             .script
   --             .year
   --             .extension
   --             .other
   --             .n
   local tags = mw.text.split( ask, "-" )
   local s    = tags[ 1 ]
   local r
   if s:match( "^%a%a%a?$" ) then
       r = { base  = s:lower(),
             legal = true,
             n     = #tags }
       for i = 2, r.n do
           s = tags[ i ]
           if #s == 2 then
               if r.region  or  not s:match( "%a%a" ) then
                   r.legal = false
               else
                   r.region = s:upper()
               end
           elseif #s == 4 then
               if s:match( "%a%a%a%a" ) then
                   r.legal = ( not r.script )
                   r.script = s:sub( 1, 1 ):upper() ..
                              s:sub( 2 ):lower()
               elseif s:match( "20%d%d" )  or
                      s:match( "1%d%d%d" ) then
                   r.legal = ( not r.year )
                   r.year = s
               else
                   r.legal = false
               end
           elseif #s == 3 then
               if r.extlang  or  not s:match( "%a%a%a" ) then
                   r.legal = false
               else
                   r.extlang = s:lower()
               end
           elseif #s == 1 then
               s = s:lower()
               if s:match( "[tux]" ) then
                   r.extension = s
                   for k = i + 1, r.n do
                       s = tags[ k ]
                       if s:match( "^%w+$" ) then
                           r.extension = string.format( "%s-%s",
                                                        r.extension, s )
                       else
                           r.legal = false
                       end
                   end -- for k
               else
                   r.legal = false
               end
               break -- for i
           else
               r.legal = ( not r.other )  and
                         s:match( "%a%a%a" )
               r.other = s:lower()
           end
           if not r.legal then
               break -- for i
           end
       end -- for i
   else
       r = { legal = false }
   end
   return r

end -- Multilingual.getLang()


Multilingual.getName = function ( ask, alien )

   -- Which name is assigned to this language code?
   -- Precondition:
   --     ask    -- language code
   --     alien  -- language of the answer
   --               -- nil, false, "*": native
   --               -- "!": current project
   --               -- any valid code
   -- Postcondition:
   --     Returns string, or false
   local r
   if ask then
       local slang   = alien
       local support = "Multilingual/names"
       local tLang
       if slang then
           if slang == "*" then
               slang = Multilingual.fair( ask )
           elseif slang == "!" then
               slang = favorite()
           else
               slang = Multilingual.fair( slang )
           end
       else
           slang = Multilingual.fair( ask )
       end
       if not slang then
           slang = ask or "?????"
       end
       slang = slang:lower()
       tLang = fetch( support, true )
       if tLang then
           tLang = tLang[ slang ]
           if tLang then
               r = tLang[ ask ]
           end
       end
       if not r then
           if not Multilingual.ext.tMW then
               Multilingual.ext.tMW = { }
           end
           tLang = Multilingual.ext.tMW[ slang ]
           if tLang == nil then
               tLang = mw.language.fetchLanguageNames( slang )
               if tLang then
                   Multilingual.ext.tMW[ slang ] = tLang
               else
                   Multilingual.ext.tMW[ slang ] = false
               end
           end
           if tLang then
               r = tLang[ ask ]
           end
       end
       if not r then
           r = mw.language.fetchLanguageName( ask:lower(), slang )
           if r == "" then
               r = false
           end
       end
   else
       r = false
   end
   return r

end -- Multilingual.getName()


Multilingual.isLang = function ( ask )

   -- Could this be an ISO language code?
   -- Precondition:
   --     ask  -- language code
   -- Postcondition:
   --     Returns boolean
   local r
   local s = Multilingual.getBase( ask )
   if s then
       r = mw.language.isKnownLanguageTag( s )
   else
       r = false
   end
   return r

end -- Multilingual.isLang()


Multilingual.isLangWiki = function ( ask )

   -- Could this be a Wiki language version?
   -- Precondition:
   --     ask  -- language version specifier
   -- Postcondition:
   --     Returns boolean
   local r
   local s = Multilingual.getBase( ask )
   if s then
       r = mw.language.isSupportedLanguage( s )
   else
       r = false
   end
   return r

end -- Multilingual.isLangWiki()


Multilingual.isMinusculable = function ( ask )

   -- Could this language name become downcased?
   -- Precondition:
   --     ask  -- language name
   -- Postcondition:
   --     Returns boolean
   local cnf = fetch( "Multilingual/config", true )
   local r = true
   if cnf then
       local s = string.format( " %s ", ask:lower() )
       if type( cnf.stopMinusculization ) == "string"
          and  cnf.stopMinusculization:find( s, 1, true ) then
           r = false
       end
       if r  and  type( cnf.seekMinusculization ) == "string"
          and  cnf.seekMinusculization:find( s, 1, true )
          and  type( cnf.scanMinusculization ) == "string" then
           local scan = ask:gsub( "[%(%)]", " " ) .. " "
           if not cnf.scanMinusculization:find( scan ) then
               r = false
           end
       end
   end
   return r

end -- Multilingual.isMinusculable()


Multilingual.kannDeutsch = function ( ask )

   -- Kann man mit diesem Sprachcode deutsch verstehen?
   -- Precondition:
   --     ask  -- language version specifier
   -- Postcondition:
   --     Returns boolean
   local r
   local s = Multilingual.getBase( ask )
   if s then
       local support = [=[ de als bar dsb frr gsw hsb ksh |
                           lb nds pdc pdt pfl sli stq vmf ]=]
       if support:find( string.format( " %s ", s ),  1,  true ) then
           r = true
       else
           r = false
       end
   else
       r = false
   end
   return r

end -- Multilingual.kannDeutsch()


Multilingual.userLang = function ( accept, frame )

   -- Try to support user language by application
   -- Precondition:
   --     accept  -- space separated list of available ISO 639 codes
   --                Default: project language, or English
   --     frame   -- frame, if available
   -- Postcondition:
   --     Returns string with appropriate code
   local r, slang, support
   if not frame then
       if not Frame then
           Frame = mw.getCurrentFrame()
       end
       frame = Frame
   end
   slang = frame:callParserFunction( "int", "lang" ):lower()
   if type( accept ) == "string" then
       support = accept:lower() .. " "
   else
       support = favorite()
       if mw.language.isKnownLanguageTag( support ) then
           support = string.format( "%s en ", support )
       else
           support = "en "
       end
   end
   if isSupported( slang, support ) then
       r = slang
   elseif slang:find( "-", 1, true ) then
       slang = Multilingual.getBase( slang )
       if isSupported( slang, support ) then
           r = slang
       end
   end
   if not r then
       if Multilingual.kannDeutsch( slang )  and
          isSupported( "de", support ) then
           r = "de"
       end
       if not r then
           r = support:match( "^(%S+) " )
       end
   end
   return r

end -- Multilingual.userLang()


function Multilingual.failsafe( assert )

   -- Retrieve versioning and check for compliance
   -- Precondition:
   --     assert  -- string, with required version, or false
   -- Postcondition:
   --     Returns  string with appropriate version, or false
   local r
   if assert  and  assert > Multilingual.serial then
       r = false
   else
       r = Multilingual.serial
   end
   return r

end -- Multilingual.failsafe()


-- Export local p = { }


p.fair = function ( frame )

   -- Format language code
   --     1  -- language code
   return Multilingual.fair( frame.args[ 1 ] )  or  ""

end -- p.fair


p.findCode = function ( frame )

   -- Retrieve language code from language name
   --     1  -- name in current project language
   return Multilingual.findCode( frame.args[ 1 ] )  or  ""

end -- p.findCode


p.format = function ( frame )

   -- Format one or more languages
   --     1          -- language list or item
   --     slang      -- language of the answer, if not native
   --                   * -- native
   --                   ! -- current project
   --                   any valid code
   --     shift      -- capitalize, if "c"; downcase, if "d"
   --                   capitalize first item only, if "f"
   --     link       -- 1 -- link items
   --     scream     -- category title in case of error
   --     split      -- split pattern, if list expected
   --     separator  -- list separator, else split
   --     start      -- prepend first element, if any
   local r
   local link
   if frame.args.link == "1" then
       link = true
   end
   r = Multilingual.format( frame.args[ 1 ],
                            frame.args.slang,
                            frame.args.shift,
                            link,
                            frame.args.scream,
                            frame,
                            frame.args.split,
                            frame.args.separator,
                            frame.args.start )
   return r or ""

end -- p.format


p.getBase = function ( frame )

   -- Retrieve base language from possibly combined ISO language code
   --     1  -- code
   return Multilingual.getBase( frame.args[ 1 ] )  or  ""

end -- p.getBase


p.getName = function ( frame )

   -- Retrieve language name from ISO language code
   --     1  -- code
   --     2  -- language to be used for the answer, if not native
   --           ! -- current project
   --           * -- native
   --           any valid code
   local slang = frame.args[ 2 ]
   local r
   if slang then
       slang = mw.text.trim( slang )
   end
   r = Multilingual.getName( frame.args[ 1 ], slang )
   return r or ""

end -- p.getName


p.isLang = function ( frame )

   -- Could this be an ISO language code?
   --     1  -- code
   local lucky, r = pcall( Multilingual.isLang,
                           frame.args[ 1 ] )
   return r and "1" or ""

end -- p.isLang


p.isLangWiki = function ( frame )

   -- Could this be a Wiki language version?
   --     1  -- code
   local lucky, r = pcall( Multilingual.isLangWiki,
                           frame.args[ 1 ] )
   return r and "1" or ""

end -- p.isLangWiki


p.kannDeutsch = function ( frame )

   -- Kann man mit diesem Sprachcode deutsch verstehen?
   --     1  -- code
   local r = Multilingual.kannDeutsch( frame.args[ 1 ] )
   return r and "1" or ""

end -- p.kannDeutsch


p.userLang = function ( frame )

   -- Which language does the current user prefer?
   --     1  -- space separated list of available ISO 639 codes
  return Multilingual.userLang( frame.args[ 1 ], frame )

end -- p.userLang


p.failsafe = function ( frame )

   local since = frame.args[ 1 ]
   if since then
       since = mw.text.trim( since )
       if since == "" then
           since = false
       end
   end
   return Multilingual.failsafe( since )  or  ""

end


p.Multilingual = function ()

   return Multilingual

end -- p.Multilingual

return p