Modul:Multilingual: Unterschied zwischen den Versionen
te>PerfektesChaos (+ format|start=) |
Admin (Diskussion | Beiträge) K (36 Versionen importiert) |
||
(33 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
− | + | local Multilingual = { suite = "Multilingual", | |
− | + | serial = "2019-06-01", | |
− | + | item = 47541920 } | |
+ | local User = { sniffer = "showpreview" } | ||
− | + | Multilingual.correction = { -- Frequently mistaken language code | |
− | + | aze = "az", | |
− | + | cz = "cs", | |
− | + | deu = "de", | |
+ | dk = "da", | ||
+ | ["en-UK"] = "en-GB", | ||
+ | ["en-uk"] = "en-GB", | ||
+ | eng = "en", | ||
+ | ger = "de", | ||
+ | gr = "el", | ||
+ | ["in"] = "id", | ||
+ | iw = "he", | ||
+ | jp = "ja", | ||
+ | lat = "la", | ||
+ | se = "sv", | ||
+ | tj = "tg" | ||
+ | } | ||
+ | Multilingual.exotic = { simple = true, | ||
+ | no = true } | ||
− | local fetch = function ( access, allow ) | + | local favorite = function () |
+ | -- Postcondition: | ||
+ | -- Returns code of current project language | ||
+ | if not Multilingual.self then | ||
+ | Multilingual.self = mw.language.getContentLanguage():getCode() | ||
+ | :lower() | ||
+ | end | ||
+ | return Multilingual.self | ||
+ | end -- favorite() | ||
+ | |||
+ | |||
+ | |||
+ | function feasible( ask, accept ) | ||
+ | -- Is ask to be supported by application? | ||
+ | -- Precondition: | ||
+ | -- ask -- lowercase code | ||
+ | -- accept -- sequence table, with offered lowercase codes | ||
+ | -- Postcondition: | ||
+ | -- nil, or true | ||
+ | local r | ||
+ | for i = 1, #accept do | ||
+ | if accept[ i ] == ask then | ||
+ | r = true | ||
+ | break -- for i | ||
+ | end | ||
+ | end -- for i | ||
+ | return r | ||
+ | end -- feasible() | ||
+ | |||
+ | |||
+ | |||
+ | local fetch = function ( access, allow, ahead ) | ||
-- Attach config or library module | -- Attach config or library module | ||
-- Precondition: | -- Precondition: | ||
-- access -- module title | -- access -- module title | ||
-- allow -- permit non-existence | -- allow -- permit non-existence | ||
+ | -- ahead -- name of startup procedure, if not access; | ||
+ | -- false for mw.loadData | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns table or false, with library | -- Returns table or false, with library | ||
-- Throws error, if not available | -- Throws error, if not available | ||
− | if | + | if type( Multilingual.ext ) ~= "table" then |
− | elseif not | + | Multilingual.ext = { } |
− | local | + | end |
− | if lucky | + | if Multilingual.ext[ access ] == false then |
− | if type( got ) == "table" then | + | elseif not Multilingual.ext[ access ] then |
− | + | local src = "Module:" .. access | |
− | + | local lucky, got | |
− | + | if ahead == false then | |
− | + | lucky, got = pcall( mw.loadData, src ) | |
+ | else | ||
+ | lucky, got = pcall( require, src ) | ||
+ | end | ||
+ | Multilingual.ext[ access ] = false | ||
+ | if type( got ) == "table" then | ||
+ | local startup = ahead or access | ||
+ | Multilingual.ext[ access ] = got | ||
+ | if type( got[ startup ] ) == "function" then | ||
+ | Multilingual.ext[ access ] = got[ startup ]() | ||
end | end | ||
− | |||
end | end | ||
− | if type( | + | if type( Multilingual.ext[ access ] ) ~= "table" and |
+ | not allow then | ||
+ | got = string.format( "Module:%s invalid", access ) | ||
error( got, 0 ) | error( got, 0 ) | ||
end | end | ||
end | end | ||
− | return | + | return Multilingual.ext[ access ] |
end -- fetch() | end -- fetch() | ||
− | function | + | local function fill( access, alien, frame ) |
− | -- | + | -- Expand language name template |
+ | -- Precondition: | ||
+ | -- access -- string, with language code | ||
+ | -- alien -- language code for which to be generated | ||
+ | -- frame -- frame, if available | ||
+ | -- Postcondition: | ||
+ | -- Returns string | ||
+ | local template = Multilingual.tmplLang | ||
+ | local r | ||
+ | if type( template ) ~= "table" then | ||
+ | local cnf = fetch( "Multilingual/config", true, true ) | ||
+ | if type( cnf ) == "table" then | ||
+ | template = cnf.tmplLang | ||
+ | end | ||
+ | end | ||
+ | if type( template ) == "table" then | ||
+ | local source = template.title | ||
+ | local f, lucky, s | ||
+ | Multilingual.tmplLang = template | ||
+ | if type( source ) ~= "string" then | ||
+ | if type( template.namePat ) == "string" and | ||
+ | template.namePat:find( "%s", 1, true ) then | ||
+ | source = string.format( template.namePat, access ) | ||
+ | end | ||
+ | end | ||
+ | if type( source ) == "string" then | ||
+ | if not Multilingual.frame then | ||
+ | if frame then | ||
+ | Multilingual.frame = frame | ||
+ | else | ||
+ | Multilingual.frame = mw.getCurrentFrame() | ||
+ | end | ||
+ | end | ||
+ | f = function ( a ) | ||
+ | return Multilingual.frame:expandTemplate{ title = a } | ||
+ | end | ||
+ | lucky, s = pcall( f, source ) | ||
+ | if lucky then | ||
+ | r = s | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | return r | ||
+ | end -- fill() | ||
+ | |||
+ | |||
+ | |||
+ | 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() | ||
+ | |||
+ | |||
+ | |||
+ | User.favorize = function ( accept, frame ) | ||
+ | -- Guess user language | ||
+ | -- Precondition: | ||
+ | -- accept -- sequence table, with offered ISO 639 etc. codes | ||
+ | -- frame -- frame, if available | ||
+ | -- Postcondition: | ||
+ | -- Returns string with best code, or nil | ||
+ | if not ( User.self or User.langs ) then | ||
+ | if not User.trials then | ||
+ | User.tell = mw.message.new( User.sniffer ) | ||
+ | if User.tell:exists() then | ||
+ | User.trials = { } | ||
+ | if not Multilingual.frame then | ||
+ | if frame then | ||
+ | Multilingual.frame = frame | ||
+ | else | ||
+ | Multilingual.frame = mw.getCurrentFrame() | ||
+ | end | ||
+ | end | ||
+ | User.sin = Multilingual.frame:callParserFunction( "int", | ||
+ | User.sniffer ) | ||
+ | else | ||
+ | User.langs = true | ||
+ | end | ||
+ | end | ||
+ | if User.sin then | ||
+ | local s, sin | ||
+ | for i = 1, #accept do | ||
+ | s = accept[ i ] | ||
+ | if not User.trials[ s ] then | ||
+ | sin = User.tell:inLanguage( s ):plain() | ||
+ | if sin == User.sin then | ||
+ | User.self = s | ||
+ | break -- for i | ||
+ | else | ||
+ | User.trials[ s ] = true | ||
+ | end | ||
+ | end | ||
+ | end -- for i | ||
+ | end | ||
+ | end | ||
+ | return User.self | ||
+ | end -- User.favorize() | ||
+ | |||
+ | |||
+ | |||
+ | 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.fallback = function ( able, another ) | ||
+ | -- Is another language suitable as replacement? | ||
-- Precondition: | -- Precondition: | ||
− | -- | + | -- able -- language version specifier to be supported |
− | -- | + | -- another -- language specifier of a possible replacement |
-- Postcondition: | -- Postcondition: | ||
− | -- | + | -- Returns boolean |
− | local | + | local r |
− | + | if type( able ) == "string" and | |
− | return | + | type( another ) == "string" then |
− | end -- | + | if able == another then |
+ | r = true | ||
+ | else | ||
+ | local s = Multilingual.getBase( able ) | ||
+ | if s == another then | ||
+ | r = true | ||
+ | else | ||
+ | local others = mw.language.getFallbacksFor( s ) | ||
+ | r = feasible( another, others ) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | return r or false | ||
+ | end -- Multilingual.fallback() | ||
Multilingual.findCode = function ( ask ) | Multilingual.findCode = function ( ask ) | ||
− | -- Retrieve code of local (current project) language name | + | -- Retrieve code of local (current project or English) language name |
-- Precondition: | -- Precondition: | ||
-- ask -- string, with presumable language name | -- ask -- string, with presumable language name | ||
Zeile 70: | Zeile 295: | ||
seek = mw.ustring.lower( seek ) | seek = mw.ustring.lower( seek ) | ||
if Multilingual.isLang( seek ) then | if Multilingual.isLang( seek ) then | ||
− | r = seek | + | r = Multilingual.fair( seek ) |
else | else | ||
− | local | + | local slang = favorite() |
− | if not | + | r = find( seek, slang ) |
− | + | if not r and slang ~= "en" then | |
+ | r = find( seek, "en" ) | ||
end | end | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
return r | return r | ||
end -- Multilingual.findCode() | end -- Multilingual.findCode() | ||
+ | |||
+ | |||
+ | |||
+ | Multilingual.fix = function ( attempt ) | ||
+ | -- Fix frequently mistaken language code | ||
+ | -- Precondition: | ||
+ | -- attempt -- string, with presumable language code | ||
+ | -- Postcondition: | ||
+ | -- Returns string with correction, or false if no problem known | ||
+ | return Multilingual.correction[ attempt:lower() ] or false | ||
+ | end -- Multilingual.fix() | ||
Zeile 98: | Zeile 328: | ||
-- -- nil, false, "*": native | -- -- nil, false, "*": native | ||
-- -- "!": current project | -- -- "!": current project | ||
+ | -- -- "#": code, downcased, space separated | ||
+ | -- -- "-": code, mixcase, space separated | ||
-- -- any valid code | -- -- any valid code | ||
− | -- alter -- capitalize, if "c"; downcase, if "d" | + | -- alter -- capitalize, if "c"; downcase all, if "d" |
-- capitalize first item only, if "f" | -- capitalize first item only, if "f" | ||
+ | -- downcase every first word only, if "m" | ||
-- active -- link items, if true | -- active -- link items, if true | ||
-- alert -- string with category title in case of error | -- alert -- string with category title in case of error | ||
Zeile 108: | Zeile 341: | ||
-- ahead -- string to prepend first element, if any | -- ahead -- string to prepend first element, if any | ||
-- Postcondition: | -- Postcondition: | ||
− | -- Returns string, or false | + | -- Returns string, or false if apply empty |
local r = false | local r = false | ||
if apply then | if apply then | ||
Zeile 118: | Zeile 351: | ||
if adjacent then | if adjacent then | ||
separator = adjacent | separator = adjacent | ||
+ | elseif alien == "#" or alien == "-" then | ||
+ | separator = " " | ||
else | else | ||
separator = assembly | separator = assembly | ||
Zeile 147: | Zeile 382: | ||
slang = Multilingual.findCode( single ) | slang = Multilingual.findCode( single ) | ||
if slang then | if slang then | ||
− | r = | + | if alien == "-" then |
− | + | r = slang | |
− | + | elseif alien == "#" then | |
− | + | r = slang:lower() | |
− | + | else | |
− | + | r = Multilingual.getName( slang, alien ) | |
− | + | if active then | |
− | + | slot = fill( slang, false, frame ) | |
− | |||
− | |||
− | slot = | ||
if slot then | if slot then | ||
− | + | local wlink = fetch( "WLink" ) | |
+ | slot = wlink.getTarget( slot ) | ||
else | else | ||
lapsus = alert | lapsus = alert | ||
Zeile 175: | Zeile 408: | ||
lapsus = alert | lapsus = alert | ||
end | end | ||
− | if alter == "c" or alter == "f" then | + | if not r then |
+ | r = single | ||
+ | elseif alter == "c" or alter == "f" then | ||
r = mw.ustring.upper( mw.ustring.sub( r, 1, 1 ) ) | r = mw.ustring.upper( mw.ustring.sub( r, 1, 1 ) ) | ||
.. mw.ustring.sub( r, 2 ) | .. mw.ustring.sub( r, 2 ) | ||
elseif alter == "d" then | elseif alter == "d" then | ||
− | r = mw.ustring.lower( r ) | + | if Multilingual.isMinusculable( slang, r ) then |
+ | r = mw.ustring.lower( r ) | ||
+ | end | ||
+ | elseif alter == "m" then | ||
+ | if Multilingual.isMinusculable( slang, r ) then | ||
+ | r = mw.ustring.lower( mw.ustring.sub( r, 1, 1 ) ) | ||
+ | .. mw.ustring.sub( r, 2 ) | ||
+ | end | ||
end | end | ||
if slot then | if slot then | ||
Zeile 188: | Zeile 430: | ||
end | end | ||
end | end | ||
− | if lapsus then | + | if lapsus and alert then |
r = string.format( "%s[[Category:%s]]", r, alert ) | r = string.format( "%s[[Category:%s]]", r, alert ) | ||
end | end | ||
Zeile 218: | Zeile 460: | ||
return r | return r | ||
end -- Multilingual.getBase() | 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 | ||
+ | -- .suggest | ||
+ | -- .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 | ||
+ | if r.legal then | ||
+ | r.suggest = Multilingual.fix( r.base ) | ||
+ | if r.suggest then | ||
+ | r.legal = false | ||
+ | end | ||
+ | end | ||
+ | else | ||
+ | r = { legal = false } | ||
+ | end | ||
+ | if not r.legal then | ||
+ | local cnf = fetch( "Multilingual/config", true, true ) | ||
+ | if type( cnf ) == "table" and | ||
+ | type( cnf.scream ) == "string" then | ||
+ | r.scream = cnf.scream | ||
+ | end | ||
+ | end | ||
+ | return r | ||
+ | end -- Multilingual.getLang() | ||
Zeile 233: | Zeile 569: | ||
local r | local r | ||
if ask then | if ask then | ||
− | local slang = alien | + | local slang = alien |
+ | local support = "Multilingual/names" | ||
+ | local tLang | ||
if slang then | if slang then | ||
if slang == "*" then | if slang == "*" then | ||
− | slang = | + | slang = Multilingual.fair( ask ) |
elseif slang == "!" then | elseif slang == "!" then | ||
− | if not | + | 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 | ||
− | + | 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 | ||
end | end | ||
− | |||
else | else | ||
r = false | r = false | ||
Zeile 255: | Zeile 625: | ||
− | Multilingual.isLang = function ( ask ) | + | Multilingual.getScriptName = function ( assigned, alien, add ) |
+ | -- Retrieve script name, hopefully linked | ||
+ | -- Precondition: | ||
+ | -- assigned -- string, with ISO 15924 script code | ||
+ | -- alien -- string, with ISO language code, or not | ||
+ | -- add -- arbitrary additional information | ||
+ | -- Postcondition: | ||
+ | -- Returns string | ||
+ | local r = assigned | ||
+ | local src = "Multilingual/scripting" | ||
+ | if not Multilingual[ src ] then | ||
+ | Multilingual[ src ] = fetch( src, true, "MultiScript" ) | ||
+ | end | ||
+ | if Multilingual[ src ] then | ||
+ | r = Multilingual[ src ].Text.scriptName( assigned, alien, add ) | ||
+ | end | ||
+ | return r | ||
+ | end -- Multilingual.getScriptName() | ||
+ | |||
+ | |||
+ | |||
+ | Multilingual.i18n = function ( available, alt, frame ) | ||
+ | -- Select translatable message | ||
+ | -- Precondition: | ||
+ | -- available -- table, with mapping language code ./. text | ||
+ | -- alt -- string|nil|false, with fallback | ||
+ | -- frame -- frame, if available | ||
+ | -- Returns | ||
+ | -- 1. string|nil|false, with selected message | ||
+ | -- 2. string|nil|false, with language code | ||
+ | local r1, r2 | ||
+ | if type( available ) == "table" then | ||
+ | local codes = { } | ||
+ | local trsl = { } | ||
+ | local slang | ||
+ | for k, v in pairs( available ) do | ||
+ | if type( k ) == "string" and | ||
+ | type( v ) == "string" then | ||
+ | slang = mw.text.trim( k:lower() ) | ||
+ | table.insert( codes, slang ) | ||
+ | trsl[ slang ] = v | ||
+ | end | ||
+ | end -- for k, v | ||
+ | slang = Multilingual.userLang( codes, frame ) | ||
+ | if slang and trsl[ slang ] then | ||
+ | r1 = mw.text.trim( trsl[ slang ] ) | ||
+ | if r1 == "" then | ||
+ | r1 = false | ||
+ | else | ||
+ | r2 = slang | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | if not r1 and type( alt ) == "string" then | ||
+ | r1 = mw.text.trim( alt ) | ||
+ | if r1 == "" then | ||
+ | r1 = false | ||
+ | end | ||
+ | end | ||
+ | return r1, r2 | ||
+ | end -- Multilingual.i18n() | ||
+ | |||
+ | |||
+ | |||
+ | Multilingual.int = function ( access, alien, apply ) | ||
+ | -- Translated system message | ||
+ | -- Precondition: | ||
+ | -- access -- message ID | ||
+ | -- alien -- language code | ||
+ | -- apply -- nil, or sequence table with parameters $1, $2, ... | ||
+ | -- Postcondition: | ||
+ | -- Returns string, or false | ||
+ | local o = mw.message.new( access ) | ||
+ | local r | ||
+ | if o:exists() then | ||
+ | if type( alien ) == "string" then | ||
+ | o:inLanguage( alien:lower() ) | ||
+ | end | ||
+ | if type( apply ) == "table" then | ||
+ | o:params( apply ) | ||
+ | end | ||
+ | r = o:plain() | ||
+ | end | ||
+ | return r or false | ||
+ | end -- Multilingual.int() | ||
+ | |||
+ | |||
+ | |||
+ | Multilingual.isLang = function ( ask, additional ) | ||
-- Could this be an ISO language code? | -- Could this be an ISO language code? | ||
-- Precondition: | -- Precondition: | ||
− | -- ask | + | -- ask -- language code |
+ | -- additional -- true, if Wiki codes like "simple" permitted | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns boolean | -- Returns boolean | ||
− | local r | + | local r, s |
− | + | if additional then | |
+ | s = ask | ||
+ | else | ||
+ | s = Multilingual.getBase( ask ) | ||
+ | end | ||
if s then | if s then | ||
r = mw.language.isKnownLanguageTag( s ) | r = mw.language.isKnownLanguageTag( s ) | ||
+ | if r then | ||
+ | r = not Multilingual.fix( s ) | ||
+ | elseif additional then | ||
+ | r = Multilingual.exotic[ s ] or false | ||
+ | end | ||
else | else | ||
r = false | r = false | ||
Zeile 282: | Zeile 750: | ||
local s = Multilingual.getBase( ask ) | local s = Multilingual.getBase( ask ) | ||
if s then | if s then | ||
− | r = mw.language.isSupportedLanguage( s ) | + | r = mw.language.isSupportedLanguage( s ) or |
+ | Multilingual.exotic[ ask ] | ||
else | else | ||
r = false | r = false | ||
Zeile 291: | Zeile 760: | ||
− | Multilingual. | + | Multilingual.isMinusculable = function ( ask, assigned ) |
− | -- | + | -- Could this language name become downcased? |
-- Precondition: | -- Precondition: | ||
− | -- ask -- language | + | -- ask -- language code, or nil |
+ | -- assigned -- language name, or nil | ||
+ | -- Postcondition: | ||
+ | -- Returns boolean | ||
+ | local r = true | ||
+ | if ask then | ||
+ | local cnf = fetch( "Multilingual/config", true, 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 assigned | ||
+ | and type( cnf.seekMinusculization ) == "string" | ||
+ | and cnf.seekMinusculization:find( s, 1, true ) | ||
+ | and type( cnf.scanMinusculization ) == "string" then | ||
+ | local scan = assigned:gsub( "[%(%)]", " " ) .. " " | ||
+ | if not scan:find( cnf.scanMinusculization ) then | ||
+ | r = false | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | return r | ||
+ | end -- Multilingual.isMinusculable() | ||
+ | |||
+ | |||
+ | |||
+ | Multilingual.isTrans = function ( ask, assign, about ) | ||
+ | -- Check whether valid transcription for context | ||
+ | -- Precondition: | ||
+ | -- ask -- string, with transcription key | ||
+ | -- assign -- string, with language or scripting code | ||
+ | -- about -- string or nil, with site scripting code | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns boolean | -- Returns boolean | ||
− | local r | + | local r = false |
− | local | + | local t |
− | + | if type( Multilingual.trans ) ~= "table" then | |
− | + | t = fetch( "Multilingual/scripts", true, false ) | |
− | + | if type( t ) == "table" then | |
− | if | + | Multilingual.trans = t.trans or { } |
− | |||
else | else | ||
− | + | Multilingual.trans = { } | |
end | end | ||
− | + | end | |
− | r = | + | t = Multilingual.trans[ assign ] |
+ | if type( t ) == "table" then | ||
+ | for k, v in pairs( t ) do | ||
+ | if v == ask then | ||
+ | r = true | ||
+ | break -- for i | ||
+ | end | ||
+ | end -- for k, v | ||
+ | end | ||
+ | if not r and about == "Latn" then | ||
+ | r = ( ask == "BGN-PCGN" or ask == "ALA-LC" ) | ||
end | end | ||
return r | return r | ||
− | end -- Multilingual. | + | end -- Multilingual.isTrans() |
Zeile 318: | Zeile 830: | ||
-- Try to support user language by application | -- Try to support user language by application | ||
-- Precondition: | -- Precondition: | ||
− | -- accept -- space separated list of available ISO 639 codes | + | -- accept -- string or table |
+ | -- space separated list of available ISO 639 codes | ||
-- Default: project language, or English | -- Default: project language, or English | ||
-- frame -- frame, if available | -- frame -- frame, if available | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns string with appropriate code | -- Returns string with appropriate code | ||
− | local r, slang | + | local s = type( accept ) |
− | if | + | local codes, r, slang |
− | + | if s == "string" then | |
− | + | codes = mw.text.split( accept:lower(), " " ) | |
− | + | elseif s == "table" then | |
− | + | codes = { } | |
− | + | for i = 1, #accept do | |
+ | s = accept[ i ] | ||
+ | if type( s ) == "string" then | ||
+ | table.insert( codes, s:lower() ) | ||
+ | end | ||
+ | end -- for i | ||
else | else | ||
− | + | codes = { } | |
− | if mw.language.isKnownLanguageTag( | + | slang = favorite() |
− | + | if mw.language.isKnownLanguageTag( slang ) then | |
− | + | table.insert( codes, slang ) | |
− | |||
end | end | ||
end | end | ||
− | if | + | slang = User.favorize( codes, frame ) |
+ | if not slang then | ||
+ | slang = favorite() or "en" | ||
+ | end | ||
+ | if feasible( slang, codes ) then | ||
r = slang | r = slang | ||
elseif slang:find( "-", 1, true ) then | elseif slang:find( "-", 1, true ) then | ||
− | slang = Multilingual.getBase() | + | slang = Multilingual.getBase( slang ) |
− | if | + | if feasible( slang, codes ) then |
r = slang | r = slang | ||
end | end | ||
end | end | ||
if not r then | if not r then | ||
− | if | + | local others = mw.language.getFallbacksFor( slang ) |
− | + | for i = 1, #others do | |
− | + | slang = others[ i ] | |
+ | if feasible( slang, codes ) then | ||
+ | r = slang | ||
+ | break -- for i | ||
+ | end | ||
+ | end -- for i | ||
+ | if not r then | ||
+ | if feasible( "en", codes ) then | ||
+ | r = "en" | ||
+ | elseif #codes > 1 and | ||
+ | codes[ 1 ] and | ||
+ | codes[ 1 ]~= "" then | ||
+ | r = codes[ 1 ] | ||
+ | end | ||
end | end | ||
− | if | + | end |
− | + | return r or favorite() or "en" | |
+ | end -- Multilingual.userLang() | ||
+ | |||
+ | |||
+ | |||
+ | Multilingual.userLangCode = function () | ||
+ | -- Guess a user language code | ||
+ | -- Postcondition: | ||
+ | -- Returns code of current best guess | ||
+ | return User.self or favorite() or "en" | ||
+ | end -- Multilingual.userLangCode() | ||
+ | |||
+ | |||
+ | |||
+ | Multilingual.failsafe = function ( atleast ) | ||
+ | -- Retrieve versioning and check for compliance | ||
+ | -- Precondition: | ||
+ | -- atleast -- string, with required version or "wikidata", | ||
+ | -- or false | ||
+ | -- Postcondition: | ||
+ | -- Returns string with appropriate version, or false | ||
+ | local since = atleast | ||
+ | local r | ||
+ | if since == "wikidata" then | ||
+ | local item = Multilingual.item | ||
+ | since = false | ||
+ | if type( item ) == "number" and item > 0 then | ||
+ | local entity = mw.wikibase.getEntity( string.format( "Q%d", | ||
+ | item ) ) | ||
+ | if type( entity ) == "table" then | ||
+ | local vsn = entity:formatPropertyValues( "P348" ) | ||
+ | if type( vsn ) == "table" and | ||
+ | type( vsn.value ) == "string" and | ||
+ | vsn.value ~= "" then | ||
+ | r = vsn.value | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | if not r then | ||
+ | if not since or since <= Multilingual.serial then | ||
+ | r = Multilingual.serial | ||
+ | else | ||
+ | r = false | ||
end | end | ||
end | end | ||
return r | return r | ||
− | end -- Multilingual. | + | end -- Multilingual.failsafe() |
Zeile 362: | Zeile 939: | ||
-- Export | -- Export | ||
local p = { } | local p = { } | ||
+ | |||
+ | |||
+ | |||
+ | p.fair = function ( frame ) | ||
+ | -- Format language code | ||
+ | -- 1 -- language code | ||
+ | local s = mw.text.trim( frame.args[ 1 ] or "" ) | ||
+ | return Multilingual.fair( s ) or "" | ||
+ | end -- p.fair | ||
+ | |||
+ | |||
+ | |||
+ | p.fallback = function ( frame ) | ||
+ | -- Is another language suitable as replacement? | ||
+ | -- 1 -- language version specifier to be supported | ||
+ | -- 2 -- language specifier of a possible replacement | ||
+ | local s1 = mw.text.trim( frame.args[ 1 ] or "" ) | ||
+ | local s2 = mw.text.trim( frame.args[ 2 ] or "" ) | ||
+ | return Multilingual.fallback( s1, s2 ) and "1" or "" | ||
+ | end -- p.fallback | ||
Zeile 368: | Zeile 965: | ||
-- Retrieve language code from language name | -- Retrieve language code from language name | ||
-- 1 -- name in current project language | -- 1 -- name in current project language | ||
− | + | local s = mw.text.trim( frame.args[ 1 ] or "" ) | |
+ | return Multilingual.findCode( s ) or "" | ||
end -- p.findCode | end -- p.findCode | ||
+ | |||
+ | |||
+ | |||
+ | p.fix = function ( frame ) | ||
+ | local r = frame.args[ 1 ] | ||
+ | if r then | ||
+ | r = Multilingual.fix( mw.text.trim( r ) ) | ||
+ | end | ||
+ | return r or "" | ||
+ | end -- p.fix | ||
Zeile 385: | Zeile 993: | ||
-- 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 | + | -- separator -- list separator, else split |
-- start -- prepend first element, if any | -- start -- prepend first element, if any | ||
local r | local r | ||
Zeile 409: | Zeile 1.017: | ||
-- Retrieve base language from possibly combined ISO language code | -- Retrieve base language from possibly combined ISO language code | ||
-- 1 -- code | -- 1 -- code | ||
− | + | local s = mw.text.trim( frame.args[ 1 ] or "" ) | |
+ | return Multilingual.getBase( s ) or "" | ||
end -- p.getBase | end -- p.getBase | ||
Zeile 421: | Zeile 1.030: | ||
-- * -- native | -- * -- native | ||
-- any valid code | -- any valid code | ||
+ | local s = mw.text.trim( frame.args[ 1 ] or "" ) | ||
local slang = frame.args[ 2 ] | local slang = frame.args[ 2 ] | ||
local r | local r | ||
+ | Multilingual.frame = frame | ||
if slang then | if slang then | ||
slang = mw.text.trim( slang ) | slang = mw.text.trim( slang ) | ||
end | end | ||
− | r = Multilingual.getName( | + | r = Multilingual.getName( s, slang ) |
return r or "" | return r or "" | ||
end -- p.getName | end -- p.getName | ||
+ | |||
+ | |||
+ | |||
+ | p.getScriptName = function ( frame ) | ||
+ | -- Retrieve script name from ISO 15924 script code, hopefully linked | ||
+ | -- 1 -- code | ||
+ | -- 2 -- optional additional key | ||
+ | local s1 = mw.text.trim( frame.args[ 1 ] or "????" ) | ||
+ | local s2 = frame.args[ 2 ] | ||
+ | if s2 then | ||
+ | s2 = mw.text.trim( s2 ) | ||
+ | end | ||
+ | return Multilingual.getScriptName( s1, false, s2 ) | ||
+ | end -- p.getScriptName | ||
+ | |||
+ | |||
+ | |||
+ | p.int = function ( frame ) | ||
+ | -- Translated system message | ||
+ | -- 1 -- message ID | ||
+ | -- lang -- language code | ||
+ | -- $1, $2, ... -- parameters | ||
+ | local sysMsg = frame.args[ 1 ] | ||
+ | local r | ||
+ | if sysMsg then | ||
+ | sysMsg = mw.text.trim( sysMsg ) | ||
+ | if sysMsg ~= "" then | ||
+ | local n = 0 | ||
+ | local slang = frame.args.lang | ||
+ | local i, params, s | ||
+ | if slang == "" then | ||
+ | slang = false | ||
+ | end | ||
+ | for k, v in pairs( frame.args ) do | ||
+ | if type( k ) == "string" then | ||
+ | s = k:match( "^%$(%d+)$" ) | ||
+ | if s then | ||
+ | i = tonumber( s ) | ||
+ | if i > n then | ||
+ | n = i | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end -- for k, v | ||
+ | if n > 0 then | ||
+ | local s | ||
+ | params = { } | ||
+ | for i = 1, n do | ||
+ | s = frame.args[ "$" .. tostring( i ) ] or "" | ||
+ | table.insert( params, s ) | ||
+ | end -- for i | ||
+ | end | ||
+ | r = Multilingual.int( sysMsg, slang, params ) | ||
+ | end | ||
+ | end | ||
+ | return r or "" | ||
+ | end -- p.int | ||
Zeile 435: | Zeile 1.103: | ||
-- Could this be an ISO language code? | -- Could this be an ISO language code? | ||
-- 1 -- code | -- 1 -- code | ||
− | local lucky, r = pcall( Multilingual.isLang, | + | local s = mw.text.trim( frame.args[ 1 ] or "" ) |
− | + | local lucky, r = pcall( Multilingual.isLang, s ) | |
return r and "1" or "" | return r and "1" or "" | ||
end -- p.isLang | end -- p.isLang | ||
Zeile 445: | Zeile 1.113: | ||
-- Could this be a Wiki language version? | -- Could this be a Wiki language version? | ||
-- 1 -- code | -- 1 -- code | ||
− | local lucky, r = pcall( Multilingual.isLangWiki, | + | local s = mw.text.trim( frame.args[ 1 ] or "" ) |
− | + | local lucky, r = pcall( Multilingual.isLangWiki, s ) | |
return r and "1" or "" | return r and "1" or "" | ||
end -- p.isLangWiki | end -- p.isLangWiki | ||
Zeile 452: | Zeile 1.120: | ||
− | p. | + | p.isTrans = function ( frame ) |
− | -- | + | -- Check whether valid transcription for context |
− | -- 1 -- code | + | -- 1 -- string, with transcription key |
− | local | + | -- 2 -- string, with language or scripting code |
− | return | + | -- site -- string or nil, with site scripting code |
− | end -- p. | + | local s1 = mw.text.trim( frame.args[ 1 ] or "" ) |
+ | local s2 = mw.text.trim( frame.args[ 2 ] or "" ) | ||
+ | local site = mw.text.trim( frame.args.site or "" ) | ||
+ | return Multilingual.isTrans( s1, s2, site ) and "1" or "" | ||
+ | end -- p.isTrans | ||
Zeile 464: | Zeile 1.136: | ||
-- Which language does the current user prefer? | -- Which language does the current user prefer? | ||
-- 1 -- space separated list of available ISO 639 codes | -- 1 -- space separated list of available ISO 639 codes | ||
− | + | local s = mw.text.trim( frame.args[ 1 ] or "" ) | |
+ | return Multilingual.userLang( s, frame ) | ||
end -- p.userLang | end -- p.userLang | ||
+ | |||
+ | |||
+ | |||
+ | 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 Multilingual.failsafe( since ) or "" | ||
+ | end -- p.failsafe() | ||
Aktuelle Version vom 6. September 2019, 12:54 Uhr
Die Dokumentation für dieses Modul kann unter Modul:Multilingual/Doku erstellt werden
local Multilingual = { suite = "Multilingual",
serial = "2019-06-01",
item = 47541920 }
local User = { sniffer = "showpreview" }
Multilingual.correction = { -- Frequently mistaken language code
aze = "az",
cz = "cs",
deu = "de",
dk = "da",
["en-UK"] = "en-GB",
["en-uk"] = "en-GB",
eng = "en",
ger = "de",
gr = "el",
["in"] = "id",
iw = "he",
jp = "ja",
lat = "la",
se = "sv",
tj = "tg"
}
Multilingual.exotic = { simple = true,
no = true }
local favorite = function ()
-- Postcondition:
-- Returns code of current project language
if not Multilingual.self then
Multilingual.self = mw.language.getContentLanguage():getCode()
:lower()
end
return Multilingual.self
end -- favorite()
function feasible( ask, accept )
-- Is ask to be supported by application?
-- Precondition:
-- ask -- lowercase code
-- accept -- sequence table, with offered lowercase codes
-- Postcondition:
-- nil, or true
local r
for i = 1, #accept do
if accept[ i ] == ask then
r = true
break -- for i
end
end -- for i
return r
end -- feasible()
local fetch = function ( access, allow, ahead )
-- Attach config or library module
-- Precondition:
-- access -- module title
-- allow -- permit non-existence
-- ahead -- name of startup procedure, if not access;
-- false for mw.loadData
-- 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 src = "Module:" .. access
local lucky, got
if ahead == false then
lucky, got = pcall( mw.loadData, src )
else
lucky, got = pcall( require, src )
end
Multilingual.ext[ access ] = false
if type( got ) == "table" then
local startup = ahead or access
Multilingual.ext[ access ] = got
if type( got[ startup ] ) == "function" then
Multilingual.ext[ access ] = got[ startup ]()
end
end
if type( Multilingual.ext[ access ] ) ~= "table" and
not allow then
got = string.format( "Module:%s invalid", access )
error( got, 0 )
end
end
return Multilingual.ext[ access ]
end -- fetch()
local function fill( access, alien, frame )
-- Expand language name template
-- Precondition:
-- access -- string, with language code
-- alien -- language code for which to be generated
-- frame -- frame, if available
-- Postcondition:
-- Returns string
local template = Multilingual.tmplLang
local r
if type( template ) ~= "table" then
local cnf = fetch( "Multilingual/config", true, true )
if type( cnf ) == "table" then
template = cnf.tmplLang
end
end
if type( template ) == "table" then
local source = template.title
local f, lucky, s
Multilingual.tmplLang = template
if type( source ) ~= "string" then
if type( template.namePat ) == "string" and
template.namePat:find( "%s", 1, true ) then
source = string.format( template.namePat, access )
end
end
if type( source ) == "string" then
if not Multilingual.frame then
if frame then
Multilingual.frame = frame
else
Multilingual.frame = mw.getCurrentFrame()
end
end
f = function ( a )
return Multilingual.frame:expandTemplate{ title = a }
end
lucky, s = pcall( f, source )
if lucky then
r = s
end
end
end
return r
end -- fill()
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()
User.favorize = function ( accept, frame )
-- Guess user language
-- Precondition:
-- accept -- sequence table, with offered ISO 639 etc. codes
-- frame -- frame, if available
-- Postcondition:
-- Returns string with best code, or nil
if not ( User.self or User.langs ) then
if not User.trials then
User.tell = mw.message.new( User.sniffer )
if User.tell:exists() then
User.trials = { }
if not Multilingual.frame then
if frame then
Multilingual.frame = frame
else
Multilingual.frame = mw.getCurrentFrame()
end
end
User.sin = Multilingual.frame:callParserFunction( "int",
User.sniffer )
else
User.langs = true
end
end
if User.sin then
local s, sin
for i = 1, #accept do
s = accept[ i ]
if not User.trials[ s ] then
sin = User.tell:inLanguage( s ):plain()
if sin == User.sin then
User.self = s
break -- for i
else
User.trials[ s ] = true
end
end
end -- for i
end
end
return User.self
end -- User.favorize()
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.fallback = function ( able, another )
-- Is another language suitable as replacement?
-- Precondition:
-- able -- language version specifier to be supported
-- another -- language specifier of a possible replacement
-- Postcondition:
-- Returns boolean
local r
if type( able ) == "string" and
type( another ) == "string" then
if able == another then
r = true
else
local s = Multilingual.getBase( able )
if s == another then
r = true
else
local others = mw.language.getFallbacksFor( s )
r = feasible( another, others )
end
end
end
return r or false
end -- Multilingual.fallback()
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.fix = function ( attempt )
-- Fix frequently mistaken language code
-- Precondition:
-- attempt -- string, with presumable language code
-- Postcondition:
-- Returns string with correction, or false if no problem known
return Multilingual.correction[ attempt:lower() ] or false
end -- Multilingual.fix()
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
slot = fill( slang, false, frame )
if slot then
local wlink = fetch( "WLink" )
slot = wlink.getTarget( slot )
else
lapsus = alert
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, r ) then
r = mw.ustring.lower( r )
end
elseif alter == "m" then
if Multilingual.isMinusculable( slang, r ) 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 and alert 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.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
-- .suggest
-- .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
if r.legal then
r.suggest = Multilingual.fix( r.base )
if r.suggest then
r.legal = false
end
end
else
r = { legal = false }
end
if not r.legal then
local cnf = fetch( "Multilingual/config", true, true )
if type( cnf ) == "table" and
type( cnf.scream ) == "string" then
r.scream = cnf.scream
end
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.getScriptName = function ( assigned, alien, add )
-- Retrieve script name, hopefully linked
-- Precondition:
-- assigned -- string, with ISO 15924 script code
-- alien -- string, with ISO language code, or not
-- add -- arbitrary additional information
-- Postcondition:
-- Returns string
local r = assigned
local src = "Multilingual/scripting"
if not Multilingual[ src ] then
Multilingual[ src ] = fetch( src, true, "MultiScript" )
end
if Multilingual[ src ] then
r = Multilingual[ src ].Text.scriptName( assigned, alien, add )
end
return r
end -- Multilingual.getScriptName()
Multilingual.i18n = function ( available, alt, frame )
-- Select translatable message
-- Precondition:
-- available -- table, with mapping language code ./. text
-- alt -- string|nil|false, with fallback
-- frame -- frame, if available
-- Returns
-- 1. string|nil|false, with selected message
-- 2. string|nil|false, with language code
local r1, r2
if type( available ) == "table" then
local codes = { }
local trsl = { }
local slang
for k, v in pairs( available ) do
if type( k ) == "string" and
type( v ) == "string" then
slang = mw.text.trim( k:lower() )
table.insert( codes, slang )
trsl[ slang ] = v
end
end -- for k, v
slang = Multilingual.userLang( codes, frame )
if slang and trsl[ slang ] then
r1 = mw.text.trim( trsl[ slang ] )
if r1 == "" then
r1 = false
else
r2 = slang
end
end
end
if not r1 and type( alt ) == "string" then
r1 = mw.text.trim( alt )
if r1 == "" then
r1 = false
end
end
return r1, r2
end -- Multilingual.i18n()
Multilingual.int = function ( access, alien, apply )
-- Translated system message
-- Precondition:
-- access -- message ID
-- alien -- language code
-- apply -- nil, or sequence table with parameters $1, $2, ...
-- Postcondition:
-- Returns string, or false
local o = mw.message.new( access )
local r
if o:exists() then
if type( alien ) == "string" then
o:inLanguage( alien:lower() )
end
if type( apply ) == "table" then
o:params( apply )
end
r = o:plain()
end
return r or false
end -- Multilingual.int()
Multilingual.isLang = function ( ask, additional )
-- Could this be an ISO language code?
-- Precondition:
-- ask -- language code
-- additional -- true, if Wiki codes like "simple" permitted
-- Postcondition:
-- Returns boolean
local r, s
if additional then
s = ask
else
s = Multilingual.getBase( ask )
end
if s then
r = mw.language.isKnownLanguageTag( s )
if r then
r = not Multilingual.fix( s )
elseif additional then
r = Multilingual.exotic[ s ] or false
end
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 ) or
Multilingual.exotic[ ask ]
else
r = false
end
return r
end -- Multilingual.isLangWiki()
Multilingual.isMinusculable = function ( ask, assigned )
-- Could this language name become downcased?
-- Precondition:
-- ask -- language code, or nil
-- assigned -- language name, or nil
-- Postcondition:
-- Returns boolean
local r = true
if ask then
local cnf = fetch( "Multilingual/config", true, 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 assigned
and type( cnf.seekMinusculization ) == "string"
and cnf.seekMinusculization:find( s, 1, true )
and type( cnf.scanMinusculization ) == "string" then
local scan = assigned:gsub( "[%(%)]", " " ) .. " "
if not scan:find( cnf.scanMinusculization ) then
r = false
end
end
end
end
return r
end -- Multilingual.isMinusculable()
Multilingual.isTrans = function ( ask, assign, about )
-- Check whether valid transcription for context
-- Precondition:
-- ask -- string, with transcription key
-- assign -- string, with language or scripting code
-- about -- string or nil, with site scripting code
-- Postcondition:
-- Returns boolean
local r = false
local t
if type( Multilingual.trans ) ~= "table" then
t = fetch( "Multilingual/scripts", true, false )
if type( t ) == "table" then
Multilingual.trans = t.trans or { }
else
Multilingual.trans = { }
end
end
t = Multilingual.trans[ assign ]
if type( t ) == "table" then
for k, v in pairs( t ) do
if v == ask then
r = true
break -- for i
end
end -- for k, v
end
if not r and about == "Latn" then
r = ( ask == "BGN-PCGN" or ask == "ALA-LC" )
end
return r
end -- Multilingual.isTrans()
Multilingual.userLang = function ( accept, frame )
-- Try to support user language by application
-- Precondition:
-- accept -- string or table
-- space separated list of available ISO 639 codes
-- Default: project language, or English
-- frame -- frame, if available
-- Postcondition:
-- Returns string with appropriate code
local s = type( accept )
local codes, r, slang
if s == "string" then
codes = mw.text.split( accept:lower(), " " )
elseif s == "table" then
codes = { }
for i = 1, #accept do
s = accept[ i ]
if type( s ) == "string" then
table.insert( codes, s:lower() )
end
end -- for i
else
codes = { }
slang = favorite()
if mw.language.isKnownLanguageTag( slang ) then
table.insert( codes, slang )
end
end
slang = User.favorize( codes, frame )
if not slang then
slang = favorite() or "en"
end
if feasible( slang, codes ) then
r = slang
elseif slang:find( "-", 1, true ) then
slang = Multilingual.getBase( slang )
if feasible( slang, codes ) then
r = slang
end
end
if not r then
local others = mw.language.getFallbacksFor( slang )
for i = 1, #others do
slang = others[ i ]
if feasible( slang, codes ) then
r = slang
break -- for i
end
end -- for i
if not r then
if feasible( "en", codes ) then
r = "en"
elseif #codes > 1 and
codes[ 1 ] and
codes[ 1 ]~= "" then
r = codes[ 1 ]
end
end
end
return r or favorite() or "en"
end -- Multilingual.userLang()
Multilingual.userLangCode = function ()
-- Guess a user language code
-- Postcondition:
-- Returns code of current best guess
return User.self or favorite() or "en"
end -- Multilingual.userLangCode()
Multilingual.failsafe = function ( atleast )
-- Retrieve versioning and check for compliance
-- Precondition:
-- atleast -- string, with required version or "wikidata",
-- or false
-- Postcondition:
-- Returns string with appropriate version, or false
local since = atleast
local r
if since == "wikidata" then
local item = Multilingual.item
since = false
if type( item ) == "number" and item > 0 then
local entity = mw.wikibase.getEntity( string.format( "Q%d",
item ) )
if type( entity ) == "table" then
local vsn = entity:formatPropertyValues( "P348" )
if type( vsn ) == "table" and
type( vsn.value ) == "string" and
vsn.value ~= "" then
r = vsn.value
end
end
end
end
if not r then
if not since or since <= Multilingual.serial then
r = Multilingual.serial
else
r = false
end
end
return r
end -- Multilingual.failsafe()
-- Export
local p = { }
p.fair = function ( frame )
-- Format language code
-- 1 -- language code
local s = mw.text.trim( frame.args[ 1 ] or "" )
return Multilingual.fair( s ) or ""
end -- p.fair
p.fallback = function ( frame )
-- Is another language suitable as replacement?
-- 1 -- language version specifier to be supported
-- 2 -- language specifier of a possible replacement
local s1 = mw.text.trim( frame.args[ 1 ] or "" )
local s2 = mw.text.trim( frame.args[ 2 ] or "" )
return Multilingual.fallback( s1, s2 ) and "1" or ""
end -- p.fallback
p.findCode = function ( frame )
-- Retrieve language code from language name
-- 1 -- name in current project language
local s = mw.text.trim( frame.args[ 1 ] or "" )
return Multilingual.findCode( s ) or ""
end -- p.findCode
p.fix = function ( frame )
local r = frame.args[ 1 ]
if r then
r = Multilingual.fix( mw.text.trim( r ) )
end
return r or ""
end -- p.fix
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
local s = mw.text.trim( frame.args[ 1 ] or "" )
return Multilingual.getBase( s ) 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 s = mw.text.trim( frame.args[ 1 ] or "" )
local slang = frame.args[ 2 ]
local r
Multilingual.frame = frame
if slang then
slang = mw.text.trim( slang )
end
r = Multilingual.getName( s, slang )
return r or ""
end -- p.getName
p.getScriptName = function ( frame )
-- Retrieve script name from ISO 15924 script code, hopefully linked
-- 1 -- code
-- 2 -- optional additional key
local s1 = mw.text.trim( frame.args[ 1 ] or "????" )
local s2 = frame.args[ 2 ]
if s2 then
s2 = mw.text.trim( s2 )
end
return Multilingual.getScriptName( s1, false, s2 )
end -- p.getScriptName
p.int = function ( frame )
-- Translated system message
-- 1 -- message ID
-- lang -- language code
-- $1, $2, ... -- parameters
local sysMsg = frame.args[ 1 ]
local r
if sysMsg then
sysMsg = mw.text.trim( sysMsg )
if sysMsg ~= "" then
local n = 0
local slang = frame.args.lang
local i, params, s
if slang == "" then
slang = false
end
for k, v in pairs( frame.args ) do
if type( k ) == "string" then
s = k:match( "^%$(%d+)$" )
if s then
i = tonumber( s )
if i > n then
n = i
end
end
end
end -- for k, v
if n > 0 then
local s
params = { }
for i = 1, n do
s = frame.args[ "$" .. tostring( i ) ] or ""
table.insert( params, s )
end -- for i
end
r = Multilingual.int( sysMsg, slang, params )
end
end
return r or ""
end -- p.int
p.isLang = function ( frame )
-- Could this be an ISO language code?
-- 1 -- code
local s = mw.text.trim( frame.args[ 1 ] or "" )
local lucky, r = pcall( Multilingual.isLang, s )
return r and "1" or ""
end -- p.isLang
p.isLangWiki = function ( frame )
-- Could this be a Wiki language version?
-- 1 -- code
local s = mw.text.trim( frame.args[ 1 ] or "" )
local lucky, r = pcall( Multilingual.isLangWiki, s )
return r and "1" or ""
end -- p.isLangWiki
p.isTrans = function ( frame )
-- Check whether valid transcription for context
-- 1 -- string, with transcription key
-- 2 -- string, with language or scripting code
-- site -- string or nil, with site scripting code
local s1 = mw.text.trim( frame.args[ 1 ] or "" )
local s2 = mw.text.trim( frame.args[ 2 ] or "" )
local site = mw.text.trim( frame.args.site or "" )
return Multilingual.isTrans( s1, s2, site ) and "1" or ""
end -- p.isTrans
p.userLang = function ( frame )
-- Which language does the current user prefer?
-- 1 -- space separated list of available ISO 639 codes
local s = mw.text.trim( frame.args[ 1 ] or "" )
return Multilingual.userLang( s, frame )
end -- p.userLang
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 Multilingual.failsafe( since ) or ""
end -- p.failsafe()
p.Multilingual = function ()
return Multilingual
end -- p.Multilingual
return p