Modul:Multilingual: Unterschied zwischen den Versionen
w>PerfektesChaos (2019-11-01) |
w>PerfektesChaos (2019-11-02) |
||
Zeile 1: | Zeile 1: | ||
local Multilingual = { suite = "Multilingual", | local Multilingual = { suite = "Multilingual", | ||
serial = "2019-11- | serial = "2019-11-02", | ||
item = 47541920, | item = 47541920, | ||
globals = { ISO15924 = 71584769, | globals = { ISO15924 = 71584769, | ||
WLink = 19363224 } | WLink = 19363224 } | ||
} | } | ||
--[=[ | |||
Utilities for multilingual texts and ISO 639 (BCP47) issues etc. | |||
* fair() | |||
* fallback() | |||
* findCode() | |||
* fix() | |||
* format() | |||
* getBase() | |||
* getLang() | |||
* getName() | |||
* i18n() | |||
* int() | |||
* isLang() | |||
* isLangWiki() | |||
* isMinusculable() | |||
* isRTL() | |||
* message() | |||
* sitelink() | |||
* userLang() | |||
* userLangCode() | |||
* wikibase() | |||
loadData: Multilingual/config Multilingual/names | |||
]=] | |||
local Failsafe = Multilingual | local Failsafe = Multilingual | ||
local GlobalMod = Multilingual | local GlobalMod = Multilingual | ||
Zeile 167: | Zeile 190: | ||
if not got and got ~= false then | if not got and got ~= false then | ||
local global = Multilingual.globals[ access ] | local global = Multilingual.globals[ access ] | ||
got = foreignModule( access, | local lib = ( not append or append == "config" ) | ||
got = foreignModule( access, lib, append, global ) | |||
if type( got ) == "table" then | if type( got ) == "table" then | ||
if | if lib then | ||
local startup = got[ access ] | local startup = got[ access ] | ||
if type( startup ) == "function" then | if type( startup ) == "function" then | ||
Zeile 205: | Zeile 229: | ||
local f, lucky, s | local f, lucky, s | ||
Multilingual.tmplLang = template | Multilingual.tmplLang = template | ||
if type( source ) ~= "string" | if type( source ) ~= "string" and | ||
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 type( source ) == "string" then | ||
Zeile 744: | Zeile 767: | ||
Multilingual.getScriptName = function ( assigned, alien, add ) | Multilingual.getScriptName = function ( assigned, alien, add ) | ||
-- OBSOLETE | |||
-- Retrieve script name, hopefully linked | -- Retrieve script name, hopefully linked | ||
-- Precondition: | -- Precondition: | ||
Zeile 883: | Zeile 907: | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns boolean | -- Returns boolean | ||
local r | local r = true | ||
if ask then | if ask then | ||
local cnf = fetch( "Multilingual", "config" ) | local cnf = fetch( "Multilingual", "config" ) | ||
Zeile 905: | Zeile 929: | ||
return r | return r | ||
end -- Multilingual.isMinusculable() | end -- Multilingual.isMinusculable() | ||
Multilingual.isRTL = function ( ask ) | |||
-- Check whether language is written right-to-left | |||
-- Precondition: | |||
-- ask -- string, with language (or script) code | |||
-- Returns true, if right-to-left | |||
local r | |||
Multilingual.rtl = Multilingual.rtl or { } | |||
r = Multilingual.rtl[ ask ] | |||
if type( r ) ~= "boolean" then | |||
local bib = fetch( "ISO15924" ) | |||
if type( bib ) == "table" and | |||
type( bib.isRTL ) == "function" then | |||
r = bib.isRTL( ask ) | |||
else | |||
r = mw.language.new( ask ):isRTL() | |||
end | |||
Multilingual.rtl[ ask ] = r | |||
end | |||
return r | |||
end -- Multilingual.isRTL() | |||
Multilingual.isTrans = function ( ask, assign, about ) | Multilingual.isTrans = function ( ask, assign, about ) | ||
-- OBSOLETE | |||
-- Check whether valid transcription for context | -- Check whether valid transcription for context | ||
-- Precondition: | -- Precondition: | ||
Zeile 1.350: | Zeile 1.398: | ||
-- Could this be a Wiki language version? | -- Could this be a Wiki language version? | ||
-- 1 -- code | -- 1 -- code | ||
-- Returns non-empty, if possibly language version | |||
local s = mw.text.trim( frame.args[ 1 ] or "" ) | local s = mw.text.trim( frame.args[ 1 ] or "" ) | ||
local lucky, r = pcall( Multilingual.isLangWiki, s ) | local lucky, r = pcall( Multilingual.isLangWiki, s ) | ||
return r and "1" or "" | return r and "1" or "" | ||
end -- p.isLangWiki | end -- p.isLangWiki | ||
p.isRTL = function ( frame ) | |||
-- Check whether language is written right-to-left | |||
-- 1 -- string, with language code | |||
-- Returns non-empty, if right-to-left | |||
local s = mw.text.trim( frame.args[ 1 ] or "" ) | |||
return Multilingual.isRTL( s ) and "1" or "" | |||
end -- p.isRTL() | |||
Version vom 4. November 2019, 21:58 Uhr
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 = "2019-11-02", item = 47541920, globals = { ISO15924 = 71584769, WLink = 19363224 } } --[=[ Utilities for multilingual texts and ISO 639 (BCP47) issues etc. * fair() * fallback() * findCode() * fix() * format() * getBase() * getLang() * getName() * i18n() * int() * isLang() * isLangWiki() * isMinusculable() * isRTL() * message() * sitelink() * userLang() * userLangCode() * wikibase() loadData: Multilingual/config Multilingual/names ]=] local Failsafe = Multilingual local GlobalMod = Multilingual local User = { sniffer = "showpreview" } Multilingual.globals.Multilingual = Multilingual.item 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 foreignModule = function ( access, advanced, append, alt, alert ) -- Fetch global module -- Precondition: -- access -- string, with name of base module -- advanced -- true, for require(); else mw.loadData() -- append -- string, with subpage part, if any; or false -- alt -- number, of wikidata item of root; or false -- alert -- true, for throwing error on data problem -- Postcondition: -- Returns whatever, probably table -- 2019-10-29 local storage = access local finer = function () if append then storage = string.format( "%s/%s", storage, append ) end end local fun, lucky, r, suited if advanced then fun = require else fun = mw.loadData end GlobalMod.globalModules = GlobalMod.globalModules or { } suited = GlobalMod.globalModules[ access ] if not suited then finer() lucky, r = pcall( fun, "Module:" .. storage ) end if not lucky then if not suited and type( alt ) == "number" and alt > 0 then suited = string.format( "Q%d", alt ) suited = mw.wikibase.getSitelink( suited ) GlobalMod.globalModules[ access ] = suited or true end if type( suited ) == "string" then storage = suited finer() lucky, r = pcall( fun, storage ) end if not lucky and alert then error( "Missing or invalid page: " .. storage, 0 ) end end return r end -- foreignModule() local favorites = function () -- Provide fallback codes -- Postcondition: -- Returns table with sequence of preferred languages -- * ahead elements -- * user (not yet accessible) -- * page content language (not yet accessible) -- * page name subpage -- * project -- * en local r = Multilingual.polyglott local f = function ( add ) local s = add for i = 1, #r do if r[ i ] == s then s = false break -- for i end end -- for i if s then table.insert( r, s ) end end if not r then local self = mw.language.getContentLanguage():getCode():lower() local sub = mw.title.getCurrentTitle().subpageText r = { } if sub:find( "/", 2, true ) then sub = sub:match( "/(%l%l%l?)$" ) if sub then table.insert( r, sub ) end end f( self ) f( "en" ) Multilingual.polyglott = r end return r end -- favorites() local feasible = function ( 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, append ) -- Attach config or library module -- Precondition: -- access -- module title -- append -- string, with subpage part of this; or false -- Postcondition: -- Returns: table, with library, or false local got, sign if append then sign = string.format( "%s/%s", access, append ) else sign = access end if type( Multilingual.ext ) ~= "table" then Multilingual.ext = { } end got = Multilingual.ext[ sign ] if not got and got ~= false then local global = Multilingual.globals[ access ] local lib = ( not append or append == "config" ) got = foreignModule( access, lib, append, global ) if type( got ) == "table" then if lib then local startup = got[ access ] if type( startup ) == "function" then got = startup() end end else got = false end Multilingual.ext[ sign ] = got end return got end -- fetch() local fill = function ( 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" ) if cnf 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" and type( template.namePat ) == "string" and template.namePat:find( "%s", 1, true ) then source = string.format( template.namePat, access ) 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() local find = function ( 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() local fold = function ( frame ) -- Merge template and #invoke arglist -- Precondition: -- frame -- template frame -- Postcondition: -- table, with combined arglist local r = { } local f = function ( apply ) if type( apply ) == "table" and type( apply.args ) == "table" then for k, v in pairs( apply.args ) do v = mw.text.trim( v ) if v ~= "" then r[ tostring( k ) ] = v end end -- for k, v end end -- f() f( frame:getParent() ) f( frame ) return r end -- fold() 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 local wlink = fetch( "WLink" ) if wlink and type( wlink.getPlain ) == "function" then seek = wlink.getPlain( seek ) end end seek = mw.ustring.lower( seek ) if Multilingual.isLang( seek ) then r = Multilingual.fair( seek ) else local collection = favorites() for i = 1, #collection do r = find( seek, collection[ i ] ) if r then break -- for i end end -- for i 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" ) if wlink and type( wlink.getTarget ) == "function" then slot = wlink.getTarget( slot ) end 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" ) if cnf 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 tLang if slang then if slang == "*" then slang = Multilingual.fair( ask ) elseif slang == "!" then slang = favorites()[ 1 ] 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( "Multilingual", "names" ) 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 ) -- OBSOLETE -- 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 bib = fetch( "ISO15924" ) local r if bib and type( bib.getScriptName ) == "function" then r = bib.getScriptName( assigned, alien, add ) end return r or "" 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" ) 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.isRTL = function ( ask ) -- Check whether language is written right-to-left -- Precondition: -- ask -- string, with language (or script) code -- Returns true, if right-to-left local r Multilingual.rtl = Multilingual.rtl or { } r = Multilingual.rtl[ ask ] if type( r ) ~= "boolean" then local bib = fetch( "ISO15924" ) if type( bib ) == "table" and type( bib.isRTL ) == "function" then r = bib.isRTL( ask ) else r = mw.language.new( ask ):isRTL() end Multilingual.rtl[ ask ] = r end return r end -- Multilingual.isRTL() Multilingual.isTrans = function ( ask, assign, about ) -- OBSOLETE -- 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 bib = fetch( "ISO15924" ) local r if type( bib ) == "table" and type( bib.isTrans ) == "function" then r = bib.isTrans( ask, assign, about ) end return r or false end -- Multilingual.isTrans() Multilingual.message = function ( arglist, frame ) -- Show text in best match of user language like system message -- Precondition: -- arglist -- template arguments -- frame -- frame, if available -- Postcondition: -- Returns string with appropriate text local r if type( arglist ) == "table" then local t = { } local m, p for k, v in pairs( arglist ) do if type( k ) == "string" and type( v ) == "string" then v = mw.text.trim( v ) if v ~= "" then if k:match( "^%l%l" ) then t[ k ] = v elseif k:match( "^%$%d$" ) and k ~= "$0" then p = p or { } k = tonumber( k:match( "^%$(%d)$" ) ) p[ k ] = v if not m or k > m then m = k end end end end end -- for k, v r = Multilingual.i18n( t, nil, frame ) if p and r and r:find( "$", 1, true ) then t = { } for i = 1, m do t[ i ] = p[ i ] or "" end -- for i r = mw.message.newRawMessage( r, t ):plain() end end return r or "" end -- Multilingual.message() Multilingual.sitelink = function ( all, frame ) -- Make link at local or other site with optimal linktext translation -- Precondition: -- all -- string or table or number, item ID or entity -- frame -- frame, if available -- Postcondition: -- Returns string with any helpful internal link, or plain text local s = type( all ) local object, r if s == "table" then object = all elseif s == "string" then object = mw.wikibase.getEntity( all ) elseif s == "number" then object = mw.wikibase.getEntity( string.format( "Q%d", all ) ) end if type( object ) == "table" then local collection = object.sitelinks local entry s = false if type( collection ) == "table" then Multilingual.site = Multilingual.site or mw.wikibase.getGlobalSiteId() entry = collection[ Multilingual.site ] if entry then s = ":" .. entry.title elseif collection.enwiki then s = "w:en:" .. collection.enwiki.title end end r = Multilingual.wikibase( object, "labels", frame ) if s then if s == ":" .. r then r = string.format( "[[%s]]", s ) else r = string.format( "[[%s|%s]]", s, r ) end end end return r or "" end -- Multilingual.sitelink() 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(), "%s+" ) elseif s == "table" then codes = { } for i = 1, #accept do s = accept[ i ] if type( s ) == "string" and s ~= "" then table.insert( codes, s:lower() ) end end -- for i end slang = User.favorize( codes, frame ) if slang then 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 end end if not r then local back = favorites() for i = 1, #back do slang = back[ i ] if feasible( slang, codes ) then r = slang break -- for i end end -- for i if not r and codes[ 1 ] then r = codes[ 1 ] end end return r or favorites()[ 1 ] end -- Multilingual.userLang() Multilingual.userLangCode = function () -- Guess a user language code -- Postcondition: -- Returns code of current best guess return User.self or favorites()[ 1 ] end -- Multilingual.userLangCode() Multilingual.wikibase = function ( all, about, attempt, frame ) -- Optimal translation of wikibase component -- Precondition: -- all -- string or table, object ID or entity -- about -- boolean, true "descriptions" or false "labels" -- attempt -- string or not, code of preferred language -- frame -- frame, if available -- Postcondition: -- Returns string with appropriate code local s = type( all ) local object, r if s == "table" then object = all elseif s == "string" then object = mw.wikibase.getEntity( all ) end if type( object ) == "table" then if about then s = "descriptions" else s = "labels" end object = object[ s ] if type( object ) == "table" then if object[ attempt ] then r = object[ attempt ].value else local poly for k, v in pairs( object ) do poly = poly or { } poly[ k ] = v.value end -- for k, v if poly then r = Multilingual.i18n( poly, nil, frame ) end end end end return r or "" end -- Multilingual.wikibase() Failsafe.failsafe = function ( atleast ) -- Retrieve versioning and check for compliance -- Precondition: -- atleast -- string, with required version or "wikidata" or "~" -- or false -- Postcondition: -- Returns string -- with queried version, also if problem -- false -- if appropriate -- 2019-10-15 local last = ( atleast == "~" ) local since = atleast local r if last or since == "wikidata" then local item = Failsafe.item since = false if type( item ) == "number" and item > 0 then local entity = mw.wikibase.getEntity( string.format( "Q%d", item ) ) if type( entity ) == "table" then local seek = Failsafe.serialProperty or "P348" local vsn = entity:formatPropertyValues( seek ) if type( vsn ) == "table" and type( vsn.value ) == "string" and vsn.value ~= "" then if last and vsn.value == Failsafe.serial then r = false else r = vsn.value end end end end end if type( r ) == "nil" then if not since or since <= Failsafe.serial then r = Failsafe.serial else r = false end end return r end -- Failsafe.failsafe() -- 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 ) -- OBSOLETE -- 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 -- Returns non-empty, if possibly language version 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.isRTL = function ( frame ) -- Check whether language is written right-to-left -- 1 -- string, with language code -- Returns non-empty, if right-to-left local s = mw.text.trim( frame.args[ 1 ] or "" ) return Multilingual.isRTL( s ) and "1" or "" end -- p.isRTL() p.isTrans = function ( frame ) -- OBSOLETE -- 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.message = function ( frame ) -- Translation of text element return Multilingual.message( fold( frame ), frame ) end -- p.message p.sitelink = function ( frame ) -- Make link at local or other site with optimal linktext translation -- 1 -- item ID local s = mw.text.trim( frame.args[ 1 ] or "" ) local r if s:match( "^%d+$") then r = tonumber( s ) elseif s:match( "^Q%d+$") then r = s end if r then r = Multilingual.sitelink( r, frame ) end return r or s end -- p.sitelink 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.wikibase = function ( frame ) -- Optimal translation of wikibase component -- 1 -- object ID -- 2 -- either "descriptions" or "labels" local r local s = mw.text.trim( frame.args[ 1 ] or "" ) if s ~= "" then local s2 = mw.text.trim( frame.args[ 2 ] or "0" ) local slang = mw.text.trim( frame.args.lang or "" ) local large = ( s2 ~= "" and s2 ~= "0" ) if slang == "" then slang = false end r = Multilingual.wikibase( s, large, slang, frame ) end return r or "" end -- p.wikibase p.failsafe = function ( frame ) -- Versioning interface local s = type( frame ) local since if s == "table" then since = frame.args[ 1 ] elseif s == "string" then since = frame end if since then since = mw.text.trim( since ) if since == "" then since = false end end return Failsafe.failsafe( since ) or "" end -- p.failsafe() p.Multilingual = function () return Multilingual end -- p.Multilingual return p