Modul:Multilingual
Utilities for multilingual texts and ISO 639 (BCP47) issues etc.
Verwendung in anderen Modulen
Dieses Modul ist notwendig für die Ausführung folgender Module. Bei Anpassungen sollte die Funktionstüchtigkeit der folgenden Module geprüft werden. Benutze dazu auch diese Tracking-Kategorie um Fehler zu finden, die sich dann auf Artikel auswirken:
- TemplateData
- Modul benötigt das Modul Multilingual – Wartungskategorie, in der nochmals alle Module gelistet sind, die von diesem Modul abhängig sind.|}}
local Multilingual = { suite = "Multilingual",
serial = "2016-06-12" };
-- local globals
local Frame
function fair( ask )
-- Format language code according to BCP 47 / RFC 4646
-- Precondition:
-- ask -- language name, downcased
-- Postcondition:
-- nil, or string
local r
if ask:find( "-", 3, true ) then
local parts = mw.text.split( ask, "-" )
if parts[ 1 ]:match( "^%l%l%l?$" ) then
local script = parts[ 2 ]
r = parts[ 1 ]
if script then
local subA2 = parts[ 3 ]
if script:match( "^%l%l%l%l$" ) then
r = string.format( "%s-%s%s",
r,
script:sub( 1, 1 ):upper(),
script:sub( 2 ) )
else
subA2 = script
end
if subA2 and subA2:match( "^%l%l$" ) then
r = string.format( "%s-%s",
r,
subA2:upper() )
end
end
end
elseif ask:match( "^%l%l%l?$" ) then
r = ask
end
return r
end -- fair()
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 = 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.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 = 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
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|%s]]", slot, r )
end
end
if lapsus then
r = string.format( "%s[[Category:%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.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 = ask:lower()
elseif slang == "!" then
slang = favorite()
else
slang = slang:lower()
end
else
slang = ask:lower()
end
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, 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
local cnf = fetch( "Multilingual/config", true )
local r = true
if cnf and type( cnf.stopMinusculization ) == "string" then
local s = string.format( " %s ", ask:lower() )
if cnf.stopMinusculization:find( s, 1, true ) then
r = false
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()
-- Export
local p = { }
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 assembly
-- 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
function p.failsafe()
return Multilingual.serial
end
p.Multilingual = function ()
return Multilingual
end -- p.Multilingual
return p