Wikipedii olowiki https://olo.wikipedia.org/wiki/Pi%C3%A4sivu MediaWiki 1.47.0-wmf.1 first-letter Medii Erikoine Pagin Käyttäi Käyttäi pagin Wikipedii Wikipedien paginat Failu Failu pagin MediiWiki MediiWiki pagin Šablonu Šablonu pagin Abu Abu pagin Kategourii Kategourii pagin TimedText TimedText talk Moduuli Keskustelu moduulista Event Event talk Moduuli:Sources 828 307 49875 47873 2026-05-09T18:20:32Z Olksolo 356 для ссылки на Викиданные не нужно : впереди 49875 Scribunto text/plain local p = {}; local i18nDefaultLanguage = 'ru'; local i18nEditors = { fr = '', de = 'Hrsg.: ', es = '', en = '', it = '', ru = 'под ред. ', } local i18nVolume = { fr = 'Vol.', es = 'Vol.', en = 'Vol.', it = 'Vol.', ru = 'Т.', } local i18nPage = { fr = 'P.', de = 'S.', es = 'P.', en = 'P.', it = 'P.', ru = 'С.', } local NORMATIVE_DOCUMENTS = { Q20754888 = 'Закон Российской Федерации', Q20754884 = 'Закон РСФСР', Q2061228 = 'Указ Президента Российской Федерации', } local monthg = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', "сентября", "октября", "ноября", "декабря"}; local PREFIX_CITEREF = "CITEREF_"; function fixAuthorName( fullName ) if ( not fullName ) then return fullName; end mw.log( 'fixAuthorName: «' .. fullName .. '»' ); local f, i, o = mw.ustring.match( fullName, '^%s*(%a+)\,%s(%a+)%s(%a+)%s*$' ); if ( f ) then mw.log( 'fixAuthorName: «' .. fullName .. '»: have «Fa, I. O.» match' ); return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. mw.ustring.sub( o, 1, 1 ) .. '.'; end local i, o, f = mw.ustring.match( fullName, '^%s*(%a)\.%s(%a)\.%s(%a%a+)%s*$'); if ( f ) then mw.log( 'fixAuthorName: «' .. fullName .. '»: have «I. O. Fa» match' ); return f .. '&nbsp;' .. i .. '.&nbsp;' .. o .. '.'; end local i, o, f = mw.ustring.match( fullName, '^%s*(%a+)%s(%a)\.%s(%a+)%s*$'); if ( f ) then mw.log( 'fixAuthorName: «' .. fullName .. '»: have «Im O. Fa» match' ); return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. o .. '.'; end local i, f = mw.ustring.match( fullName, '^%s*(%a%a+)%s(%a%a+)%s*$'); if ( f ) then mw.log( 'fixAuthorName: «' .. fullName .. '»: have «Im Fa» match' ); return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.'; end mw.log( 'Unmatched any pattern: «' .. fullName .. '»' ); return fullName; end local options_authors = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false, formatLabel = fixAuthorName }; local options_authors_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = true , preferids = false, formatLabel = fixAuthorName }; local options_commas = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false }; local options_commas_short = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false, short = true }; local options_commas_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = true, preferids = false }; local options_commas_it = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false }; local options_commas_it_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = true , preferids = false }; local options_citetypes = { separator = ' ', conjunction = ' ', format = function( src ) return 'citetype_' .. src end, nolinks = true , preferids = true }; function assertNotNull( argName, arg ) if ( (not arg) or (arg == nil) ) then error( argName .. ' is not specified' ) end end function isEmpty( str ) return ( not str ) or ( str == nil ) or ( #str == 0 ); end function getEntity( context, entityId ) assertNotNull( 'context', context ); assertNotNull( 'entityId', entityId ); local cached = context.cache[ entityId ]; if ( cached ) then return cached; end; local result = mw.wikibase.getEntity( entityId ); if ( result ) then context.cache[ entityId ] = result; end return result; end function renderSource( src ) mw.logObject( src ); local context = { cache = {}, lang = getLangCode( getSingle( src.lang ) ) or i18nDefaultLanguage, } src.title = src.title or getSingle( src.url ) or '\'\'(unspecified title)\'\'' if ( src.code and not src.url ) then local entity = mw.wikibase.getEntity( src.code ); if ( entity.sitelinks and entity.sitelinks[ context.lang .. 'wikisource'] ) then src.url = ':' .. context.lang .. ':s:' .. entity.sitelinks[ context.lang .. 'wikisource' ].title; else src.url = (mw.wikibase.sitelink( src.code ) or ( 'd:' .. src.code )) src.url = ':' .. src.url; end end if ( not src.year and src.dateOfPublication ) then local date = getSingle( src.dateOfPublication ); src.year = mw.ustring.sub( date, 2, 5 ); end local result = ''; if ( src.author ) then result = result .. toString( context, src.author, options_authors ); end if ( string.len( result ) ~= 0 ) then result = result .. ' '; end if ( src.part ) then if ( src.url ) then local url = getSingle( src.url ); if ( string.sub( url, 1, 1 ) == ':' ) then result = result .. '[[' .. url .. '|' .. toString( context, src.part, options_commas_nolinks ) .. ']]'; else result = result .. '[' .. url .. ' ' .. toString( context, src.part, options_commas_nolinks ) .. ']'; end end result = result .. ' // ' .. toString( context, src.title, options_commas ); else -- title only if ( src.url ) then local url = getSingle( src.url ); if ( string.sub( url, 1, 1 ) == ':' ) then result = result .. '[[' .. url .. '|' .. toString( context, src.title, options_commas_nolinks ) .. ']]'; else result = result .. '[' .. url .. ' ' .. toString( context, src.title, options_commas_nolinks ) .. ']'; end end end if ( src.originaltitle ) then result = result .. ' = ' .. toString( context, src.originaltitle, options_commas ); end if ( src.publication ) then result = result .. ' // ' .. toString( context, src.publication, options_commas_it ); end if ( src.editor ) then local prefix = i18nEditors[ context.lang ] or i18nEditors[ i18nDefaultLanguage ]; result = result .. ' / ' .. prefix .. toString( context, src.editor, options_commas ); end if ( src.place or src.publisher or src.year ) then result = result .. ' — '; if ( src.place ) then result = result .. toString( context, src.place, options_commas_short ); if ( src.publisher or src.year ) then result = result .. ': '; end end if ( src.publisher ) then result = result .. toString( context, src.publisher, options_commas ); if ( src.year ) then result = result .. ', '; end end if ( src.year ) then result = result .. toString( context, src.year, options_commas ); end result = result .. '.'; end if ( src.volume ) then local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.volume, options_commas ) .. '.'; end if ( src.issue ) then result = result .. ' — №&nbsp;' .. toString( context, src.issue, options_commas ) .. '.'; end if ( src.page ) then local letter = i18nPage[ context.lang ] or i18nPage[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.page, options_commas ) .. '.'; end if ( src.isbn ) then result = result .. ' — ISBN ' .. toString( context, src.isbn, options_commas ); end if ( src.issn ) then result = result .. ' — ISSN ' .. toString( context, src.issn, options_commas ); end if ( src.doi ) then result = result .. ' — [http://dx.doi.org/' .. mw.uri.encode( src.doi ) .. ' DOI&nbsp;' .. src.doi .. ']'; end if ( src.entityId ) then if ( src.type and src.entityId ) then -- wrap into span to target from JS result = '<span class="' .. toString( context, src.type, options_citetypes ) .. '" data-entity-id="' .. getSingle( src.entityId ) .. '">' .. result .. '</span>' else result = '<span class="citetype_unknown" data-entity-id="' .. getSingle( src.entityId ) .. '">' .. result .. '</span>' end end -- if ( src.accessdate ) then -- local date = getSingle( src.accessdate ); -- local pattern = "(%-?%d+)%-(%d+)%-(%d+)T"; -- local y, m, d = mw.ustring.match( date , pattern ); -- y,m,d = tonumber(y),tonumber(m),tonumber(d); -- result = result .. " "; --<small>Проверено " .. tostring(d) .. " " .. monthg[m] .. " " .. tostring(y) .. ".</small>"; -- end return {text = result, code = src.code}; end function renderShortReference( src ) context = { cache = {}, lang = getSingle( src.lang ) or i18nDefaultLanguage; }; src.title = src.title or '\'\'(unspecified title)\'\'' local result = '[[#' .. PREFIX_CITEREF .. src.code .. '|'; if ( src.author ) then result = result .. toString( context, src.author, options_authors_nolinks ); else result = result .. toString( context, src.title, options_commas_it_nolinks ); end result = result .. ']]' if ( src.year ) then result = result .. ', ' .. toString( context, src.year, options_commas ); end if ( src.volume ) then local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.volume, options_commas ) .. '.'; end if ( src.issue ) then result = result .. ' — №&nbsp;' .. toString( context, src.issue, options_commas ) .. '.'; end if ( src.page ) then local letter = i18nPage[ context.lang ] or i18nPage[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.page, options_commas ) .. '.'; end end function getSingle( value ) if ( not value ) then return; end if ( type( value ) == 'string' ) then return value; elseif ( type( value ) == 'table' ) then if ( value.id ) then return value.id; end for i, tableValue in pairs( value ) do return getSingle( tableValue ); end end return '(unknown)'; end function fSelf( value ) return value; end function toString( context, value, options ) if ( type( value ) == 'string' ) then return options.format( value ); elseif ( type( value ) == 'table' ) then if ( value.id ) then -- this is link if ( options.preferids ) then return options.format( value.id ); else if ( options.nolinks ) then local formatLabel = options.formatLabel or fSelf; return options.format( formatLabel( value.label or mw.wikibase.label( value.id ) or '\'\'(untranslated title)\'\'' ) ); else return options.format( renderLink( context, value.id, value.label, options ) ); end end end local resultList = {}; for i, tableValue in pairs( value ) do table.insert( resultList, toString( context, tableValue, options ) ); end return mw.text.listToText( resultList, options.separator, options.conjunction); else return options.format( '(unknown type)' ); end return ''; end function renderLink( context, entityId, customTitle, options ) if ( not entityId ) then error("entityId is not specified"); end local formatLabel = options.formatLabel or fSelf; local title = customTitle; if ( isEmpty( title ) ) then local entity = getEntity( context, entityId ); -- official name P1448 -- short name P1813 if ( isEmpty( title ) and options.short ) then if ( entity.claims and entity.claims.P1813 ) then for _, claim in pairs( entity.claims.P1813 ) do mw.log( claim.mainsnak.datavalue.value.language ); mw.log( context.lang ); mw.log( claim.mainsnak.datavalue.value.language == context.lang ); mw.log( claim.mainsnak.datavalue.value.text ); if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value.language == context.lang ) then title = claim.mainsnak.datavalue.value.text; break; end end end end -- person name P1559 -- labels if ( isEmpty( title ) and entity.labels[ context.lang ] ) then title = entity.labels[ context.lang ].value; mw.log('Got title of ' .. entityId .. ' from label: «' .. title .. '»' ) end end local actualText = formatLabel( title or '\'\'(untranslated)\'\'' ); local link = mw.wikibase.sitelink( entityId ) or ( ':d:' .. entityId ) return '[[' .. link .. '|' .. actualText .. ']]'; end -- Expand special types of references when additional data could be found in OTHER entity properties function expandSpecials( currentEntity, reference, data ) if ( reference.snaks.P248 and reference.snaks.P248[1] and reference.snaks.P248[1].datavalue and reference.snaks.P248[1].datavalue.value["numeric-id"]) then local sourceId = "Q" .. reference.snaks.P248[1].datavalue.value["numeric-id"]; -- Gemeinsame Normdatei -- specified by P227 if ( sourceId == 'Q36578' ) then appendSnaks( currentEntity.claims, 'P227', data, 'title', { format = function( gnd ) return 'Record #' .. gnd; end } ); appendSnaks( currentEntity.claims, 'P227', data, 'url', { format = function( gnd ) return 'http://d-nb.info/gnd/' .. gnd .. '/'; end } ); end -- BNF -- specified by P268 if ( sourceId == 'Q15222191' ) then appendSnaks( currentEntity.claims, 'P268', data, 'title', { format = function( id ) return 'Record #' .. id; end } ); appendSnaks( currentEntity.claims, 'P268', data, 'url', { format = function( id ) return 'http://catalogue.bnf.fr/ark:/12148/cb' .. id; end } ); expandSpecialsQualifiers( currentEntity, 'P268', data ); end -- Find a Grave -- specified by P535 if ( sourceId == 'Q63056' ) then appendSnaks( currentEntity.claims, 'P535', data, 'url', { format = function( id ) return 'http://www.findagrave.com/cgi-bin/fg.cgi?page=gr&GRid=' .. id; end } ); expandSpecialsQualifiers( currentEntity, 'P535', data ); end -- Dizionario Biografico degli Italiani -- specified by P1986 if ( sourceId == 'Q1128537' ) then if ( not data.lang ) then data.lang = { id = 'Q652' } end; appendSnaks( currentEntity.claims, 'P1986', data, 'url', { format = function( id ) return 'http://www.treccani.it/enciclopedia/' .. id .. '_%28Dizionario_Biografico%29/' end } ); expandSpecialsQualifiers( currentEntity, 'P1986', data ); end -- Union List of Artist Names -- specified by P245 if ( sourceId == 'Q2494649' ) then appendSnaks( currentEntity.claims, 'P245', data, 'url', { format = function( id ) return 'http://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid=' .. id end } ); expandSpecialsQualifiers( currentEntity, 'P245', data ); end -- Gran Enciclopèdia Catalana -- specified by P1296 if ( sourceId == 'Q2664168' ) then appendSnaks( currentEntity.claims, 'P1296', data, 'url', { format = function( id ) return 'http://www.enciclopedia.cat/enciclop%C3%A8dies/gran-enciclop%C3%A8dia-catalana/EC-GEC-' .. id .. '.xml'; end } ); expandSpecialsQualifiers( currentEntity, 'P1296', data ); end -- Encyclopædia Britannica online -- specified by P1417 if ( sourceId == 'Q5375741' ) then appendSnaks( currentEntity.claims, 'P1417', data, 'url', { format = function( id ) return 'http://global.britannica.com/EBchecked/topic/' .. id .. '/'; end } ); expandSpecialsQualifiers( currentEntity, 'P1417', data ); end -- Electronic Jewish Encyclopedia (Elektronnaja Evrejskaja Entsiklopedia) -- specified by P1438 if ( sourceId == 'Q1967250' ) then appendSnaks( currentEntity.claims, 'P1438', data, 'url', { format = function( id ) return 'http://www.eleven.co.il/article/' .. id; end } ); expandSpecialsQualifiers( currentEntity, 'P1438', data ); end -- sports-reference.com -- specified by P1447 if ( sourceId == 'Q18002875' ) then appendSnaks( currentEntity.claims, 'P1447', data, 'url', { format = function( id ) return 'http://www.sports-reference.com/olympics/athletes/' .. id .. '.html'; end } ); expandSpecialsQualifiers( currentEntity, 'P1447', data ); end -- do we have appropriate record in P1343 ? local claims = findClaimsByValue( currentEntity, 'P1343', sourceId ); if ( claims and #claims ~= 0 ) then appendQualifiers( claims, 'P958', data, 'part', {} ); appendQualifiers( claims, 'P953', data, 'url', {} ); appendQualifiers( claims, 'P854', data, 'url', {} ); appendQualifiers( claims, 'P357', data, 'title', {} ); -- obsolete appendQualifiers( claims, 'P1476', data, 'title', {} ); appendQualifiers( claims, 'P478', data, 'volume', {} ); end end end function expandSpecialsQualifiers( entity, propertyId, result ) if ( entity.claims and entity.claims[propertyId] ) then local claims = entity.claims[propertyId]; appendQualifiers( claims, 'P958', result, 'part', {} ); appendQualifiers( claims, 'P953', result, 'url', {} ); appendQualifiers( claims, 'P854', result, 'url', {} ); appendQualifiers( claims, 'P357', result, 'title', {} ); -- obsolete appendQualifiers( claims, 'P1476', result, 'title', {} ); appendQualifiers( claims, 'P478', result, 'volume', {} ); end end function findClaimsByValue( entity, propertyId, value ) local result = {}; if ( entity and entity.claims and entity.claims[propertyId] ) then for i, claim in pairs( entity.claims[propertyId] ) do if ( claim.mainsnak and claim.mainsnak.datavalue ) then local datavalue = claim.mainsnak.datavalue; if ( datavalue.type == "string" and datavalue.value == value or datavalue.type == "wikibase-entityid" and datavalue.value["entity-type"] == "item" and tostring( datavalue.value["numeric-id"] ) == mw.ustring.sub( value, 2 ) ) then table.insert( result, claim ); end end end end return result; end function appendSnaks( allSnaks, snakPropertyId, result, property, options ) -- do not populate twice if ( result[property] ) then return result end; if ( allSnaks and allSnaks[ snakPropertyId ] ) then for k, snak in pairs( allSnaks[ snakPropertyId ] ) do if ( snak and snak.mainsnak and snak.mainsnak.datavalue ) then --it's a claim appendImpl( snak.mainsnak.datavalue, result, property, options ); elseif ( snak and snak.datavalue ) then -- it's a snak appendImpl( snak.datavalue, result, property, options ); end end end end function appendQualifiers( claims, qualifierPropertyId, result, property, options ) -- do not populate twice if ( result[property] ) then return result end; for i, claim in pairs( claims ) do if ( claim.qualifiers and claim.qualifiers[ qualifierPropertyId ] ) then for k, qualifier in pairs( claim.qualifiers[ qualifierPropertyId ] ) do if ( qualifier and qualifier.datavalue ) then appendImpl( qualifier.datavalue, result, property, options ); end end end end end function appendImpl( datavalue, result, property, options ) if ( datavalue.type == 'string' ) then local value = datavalue.value; if ( options.format ) then value = options.format( value ); end if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], value); elseif ( datavalue.type == 'monolingualtext' ) then local value = datavalue.value.text; if ( options.format ) then value = options.format( value ); end if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], value); elseif ( datavalue.type == 'wikibase-entityid' ) then local value = datavalue.value; if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], { id = 'Q' .. value["numeric-id"] }); elseif datavalue.type == 'time' then local value = datavalue.value; if ( options.format ) then value = options.format( value ); end if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], tostring( value.time )); end end function expandPublication( data ) local publication = data.publication; -- use only first one if ( type( publication ) == 'table' and publication[1] and publication[1].id ) then data.publication = publication[1]; publication = data.publication; end if ( publication and publication.id ) then populateSourceData( publication.id, data ); end end function populateSourceData( entityId, plainData ) return populateSourceDataImpl( mw.wikibase.getEntity( entityId ), plainData ); end function populateSourceDataImpl( entity, plainData ) populateDataFromClaims( entity.claims, plainData ); local normativeTitle = getNormativeTitle( entity ) if ( normativeTitle ) then local y, m, d = mw.ustring.match( getSingle( plainData.dateOfCreation ) , "(%-?%d+)%-(%d+)%-(%d+)T" ); y,m,d = tonumber(y),tonumber(m),tonumber(d); plainData.title = { normativeTitle .. " от&nbsp;" .. tostring(d) .. "&nbsp;" .. monthg[m] .. " " .. tostring(y) .. "&nbsp;г. №&nbsp;" .. getSingle( plainData.docNumber ) .. ' «' .. getSingle( plainData.title ) .. '»' } end if ( not plainData.title ) then if ( entity.labels and entity.labels.ru and entity.labels.ru.value ) then plainData.title = { entity.labels.ru.value }; end end return plainData; end function populateDataFromClaims( claims, data ) appendSnaks( claims, 'P50', data, 'author', {} ); appendSnaks( claims, 'P407', data, 'lang', {} ); appendSnaks( claims, 'P364', data, 'lang', {} ); appendSnaks( claims, 'P958', data, 'part', {} ); appendSnaks( claims, 'P357', data, 'title', {} ); -- obsolete appendSnaks( claims, 'P1476', data, 'title', {} ); appendSnaks( claims, 'P953', data, 'url', {} ); appendSnaks( claims, 'P854', data, 'url', {} ); appendSnaks( claims, 'P856', data, 'url', {} ); appendSnaks( claims, 'P1433', data, 'publication', {} ); appendSnaks( claims, 'P123', data, 'publisher', {} ); appendSnaks( claims, 'P291', data, 'place', {} ); appendSnaks( claims, 'P304', data, 'page', {} ); appendSnaks( claims, 'P478', data, 'volume', {} ); appendSnaks( claims, 'P571', data, 'dateOfCreation', {} ); appendSnaks( claims, 'P577', data, 'dateOfPublication', {} ); appendSnaks( claims, 'P212', data, 'isbn', {} ); -- ISBN-13 appendSnaks( claims, 'P957', data, 'isbn', {} ); -- ISBN-10 -- web appendSnaks( claims, 'P813', data, 'accessdate', {} ); -- docs appendSnaks( claims, 'P1545', data, 'docNumber', {} ); -- other appendSnaks( claims, 'P31', data, 'type', {} ); return src; end function updateWithRef( reference, src ) -- specified if ( reference.snaks.O662 ) then local cid = reference.snaks.P662[1].datavalue.value; src.code = src.code .. '-cid:' .. cid; src.title = 'Compound Summary for: CID ' .. cid; src.url = 'http://pubchem.ncbi.nlm.nih.gov/summary/summary.cgi?cid=' .. cid; src.publication = { id = 'Q278487', label = 'PubChem' }; end populateDataFromClaims(reference.snaks, src); return src; end function p.renderSource( frame ) local arg = frame.args[1]; return p.renderSourceImpl( mw.text.trim( arg ) ); end function p.renderSourceImpl( entityId ) assertNotNull('entityId', entityId) if ( mw.ustring.sub( entityId, 1, 1 ) ~= 'Q' ) then error( 'Incorrect entity ID: «' .. entityId .. '»' ); end; local value = {}; value["numeric-id"] = string.sub( entityId , 2); local snak = { datavalue = { value = value } }; local properties = {}; properties[1] = snak; local rendered = renderReferenceImpl( mw.wikibase.getEntity(), { snaks = { P248 = properties } } ); if ( rendered ) then return rendered.text end; end function p.renderReference( frame, currentEntity, reference ) -- template call if ( frame and not currentEntity and not reference ) then local value = {}; value["numeric-id"] = string.sub( frame.args[1] , 2); local snak = { datavalue = { value =value } }; local properties = {}; properties[1] = snak; currentEntity = mw.wikibase.getEntity(); reference = { snaks = { P248 = properties } }; end local rendered = renderReferenceImpl( currentEntity, reference ); if ( not rendered ) then return ''; end local result; local code = rendered.code or mw.text.encode( rendered.text ); result = frame:extensionTag( 'ref', rendered.text, {name = code} ) .. '[[Category:Википедия:Статьи с источниками из Викиданных]]'; if ( not rendered.found ) then result = result .. '[[Category:Википедия:Статьи с неоформленными источниками из Викиданных]]'; end return result; end function renderReferenceImpl( currentEntity, reference ) if ( not reference.snaks ) then return nil; end -- данные в простом формате, согласованном с модулями формирования библиографического описания local data = {}; local entityId, sourceEntity; if ( reference and reference.snaks and reference.snaks.P248 ) then for _, snak in pairs ( reference.snaks.P248 ) do if ( snak.datavalue ) then entityId = 'Q' .. snak.datavalue.value["numeric-id"]; sourceEntity = mw.wikibase.getEntity( entityId ); data.code = entityId; data.entityId = entityId; break; end end end updateWithRef( reference, data ); expandSpecials( currentEntity, reference, data ); if ( sourceEntity ) then populateSourceDataImpl( sourceEntity, data ); end expandPublication( data ); local rendered; if ( p.short ) then rendered = renderShortReference( data ); else rendered = renderSource( data ); end if ( mw.ustring.len( rendered.text ) == 0 ) then return nil; end rendered.found = found; return rendered; end function getNormativeTitle( entity ) if ( not entity or not entity.claims or not entity.claims.P31 ) then return; end for _, claim in pairs( entity.claims.P31 ) do if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value["numeric-id"] ) then local classId = 'Q' .. claim.mainsnak.datavalue.value["numeric-id"]; local title = NORMATIVE_DOCUMENTS[ classId ]; if ( title ) then return title; end end end return; end local LANG_CACHE = { Q150 = 'fr', Q188 = 'de', Q1321 = 'es', Q1860 = 'en', Q652 = 'it', Q7737 = 'ru', } function getLangCode( langEntityId ) if ( not langEntityId ) then return; end -- small optimization local cached = LANG_CACHE[ langEntityId ]; if ( cached ) then return cached; end local langEntity = mw.wikibase.getEntity( langEntityId ); if ( not langEntity ) then mw.log( '[getLangCode] Missing entity ' .. langEntityId ); else if ( langEntity.claims and langEntity.claims.P424 ) then for _, claim in pairs( langEntity.claims.P424 ) do if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value ) then return '' .. claim.mainsnak.datavalue.value; end end end end return; end return p; tfue9smsjelu9o7zhplaf9x4jqja127 49893 49875 2026-05-10T10:36:30Z Olksolo 356 P958 (section) создаёт проблемы при генерировании ссылок на источники 49893 Scribunto text/plain local p = {}; local i18nDefaultLanguage = 'ru'; local i18nEditors = { fr = '', de = 'Hrsg.: ', es = '', en = '', it = '', ru = 'под ред. ', } local i18nVolume = { fr = 'Vol.', es = 'Vol.', en = 'Vol.', it = 'Vol.', ru = 'Т.', } local i18nPage = { fr = 'P.', de = 'S.', es = 'P.', en = 'P.', it = 'P.', ru = 'С.', } local NORMATIVE_DOCUMENTS = { Q20754888 = 'Закон Российской Федерации', Q20754884 = 'Закон РСФСР', Q2061228 = 'Указ Президента Российской Федерации', } local monthg = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', "сентября", "октября", "ноября", "декабря"}; local PREFIX_CITEREF = "CITEREF_"; function fixAuthorName( fullName ) if ( not fullName ) then return fullName; end mw.log( 'fixAuthorName: «' .. fullName .. '»' ); local f, i, o = mw.ustring.match( fullName, '^%s*(%a+)\,%s(%a+)%s(%a+)%s*$' ); if ( f ) then mw.log( 'fixAuthorName: «' .. fullName .. '»: have «Fa, I. O.» match' ); return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. mw.ustring.sub( o, 1, 1 ) .. '.'; end local i, o, f = mw.ustring.match( fullName, '^%s*(%a)\.%s(%a)\.%s(%a%a+)%s*$'); if ( f ) then mw.log( 'fixAuthorName: «' .. fullName .. '»: have «I. O. Fa» match' ); return f .. '&nbsp;' .. i .. '.&nbsp;' .. o .. '.'; end local i, o, f = mw.ustring.match( fullName, '^%s*(%a+)%s(%a)\.%s(%a+)%s*$'); if ( f ) then mw.log( 'fixAuthorName: «' .. fullName .. '»: have «Im O. Fa» match' ); return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. o .. '.'; end local i, f = mw.ustring.match( fullName, '^%s*(%a%a+)%s(%a%a+)%s*$'); if ( f ) then mw.log( 'fixAuthorName: «' .. fullName .. '»: have «Im Fa» match' ); return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.'; end mw.log( 'Unmatched any pattern: «' .. fullName .. '»' ); return fullName; end local options_authors = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false, formatLabel = fixAuthorName }; local options_authors_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = true , preferids = false, formatLabel = fixAuthorName }; local options_commas = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false }; local options_commas_short = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false, short = true }; local options_commas_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = true, preferids = false }; local options_commas_it = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false }; local options_commas_it_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = true , preferids = false }; local options_citetypes = { separator = ' ', conjunction = ' ', format = function( src ) return 'citetype_' .. src end, nolinks = true , preferids = true }; function assertNotNull( argName, arg ) if ( (not arg) or (arg == nil) ) then error( argName .. ' is not specified' ) end end function isEmpty( str ) return ( not str ) or ( str == nil ) or ( #str == 0 ); end function getEntity( context, entityId ) assertNotNull( 'context', context ); assertNotNull( 'entityId', entityId ); local cached = context.cache[ entityId ]; if ( cached ) then return cached; end; local result = mw.wikibase.getEntity( entityId ); if ( result ) then context.cache[ entityId ] = result; end return result; end function renderSource( src ) mw.logObject( src ); local context = { cache = {}, lang = getLangCode( getSingle( src.lang ) ) or i18nDefaultLanguage, } src.title = src.title or getSingle( src.url ) or '\'\'(unspecified title)\'\'' if ( src.code and not src.url ) then local entity = mw.wikibase.getEntity( src.code ); if ( entity.sitelinks and entity.sitelinks[ context.lang .. 'wikisource'] ) then src.url = ':' .. context.lang .. ':s:' .. entity.sitelinks[ context.lang .. 'wikisource' ].title; else src.url = (mw.wikibase.sitelink( src.code ) or ( 'd:' .. src.code )) src.url = ':' .. src.url; end end if ( not src.year and src.dateOfPublication ) then local date = getSingle( src.dateOfPublication ); src.year = mw.ustring.sub( date, 2, 5 ); end local result = ''; if ( src.author ) then result = result .. toString( context, src.author, options_authors ); end if ( string.len( result ) ~= 0 ) then result = result .. ' '; end if ( src.part ) then if ( src.url ) then local url = getSingle( src.url ); if ( string.sub( url, 1, 1 ) == ':' ) then result = result .. '[[' .. url .. '|' .. toString( context, src.part, options_commas_nolinks ) .. ']]'; else result = result .. '[' .. url .. ' ' .. toString( context, src.part, options_commas_nolinks ) .. ']'; end end result = result .. ' // ' .. toString( context, src.title, options_commas ); else -- title only if ( src.url ) then local url = getSingle( src.url ); if ( string.sub( url, 1, 1 ) == ':' ) then result = result .. '[[' .. url .. '|' .. toString( context, src.title, options_commas_nolinks ) .. ']]'; else result = result .. '[' .. url .. ' ' .. toString( context, src.title, options_commas_nolinks ) .. ']'; end end end if ( src.originaltitle ) then result = result .. ' = ' .. toString( context, src.originaltitle, options_commas ); end if ( src.publication ) then result = result .. ' // ' .. toString( context, src.publication, options_commas_it ); end if ( src.editor ) then local prefix = i18nEditors[ context.lang ] or i18nEditors[ i18nDefaultLanguage ]; result = result .. ' / ' .. prefix .. toString( context, src.editor, options_commas ); end if ( src.place or src.publisher or src.year ) then result = result .. ' — '; if ( src.place ) then result = result .. toString( context, src.place, options_commas_short ); if ( src.publisher or src.year ) then result = result .. ': '; end end if ( src.publisher ) then result = result .. toString( context, src.publisher, options_commas ); if ( src.year ) then result = result .. ', '; end end if ( src.year ) then result = result .. toString( context, src.year, options_commas ); end result = result .. '.'; end if ( src.volume ) then local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.volume, options_commas ) .. '.'; end if ( src.issue ) then result = result .. ' — №&nbsp;' .. toString( context, src.issue, options_commas ) .. '.'; end if ( src.page ) then local letter = i18nPage[ context.lang ] or i18nPage[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.page, options_commas ) .. '.'; end if ( src.isbn ) then result = result .. ' — ISBN ' .. toString( context, src.isbn, options_commas ); end if ( src.issn ) then result = result .. ' — ISSN ' .. toString( context, src.issn, options_commas ); end if ( src.doi ) then result = result .. ' — [http://dx.doi.org/' .. mw.uri.encode( src.doi ) .. ' DOI&nbsp;' .. src.doi .. ']'; end if ( src.entityId ) then if ( src.type and src.entityId ) then -- wrap into span to target from JS result = '<span class="' .. toString( context, src.type, options_citetypes ) .. '" data-entity-id="' .. getSingle( src.entityId ) .. '">' .. result .. '</span>' else result = '<span class="citetype_unknown" data-entity-id="' .. getSingle( src.entityId ) .. '">' .. result .. '</span>' end end -- if ( src.accessdate ) then -- local date = getSingle( src.accessdate ); -- local pattern = "(%-?%d+)%-(%d+)%-(%d+)T"; -- local y, m, d = mw.ustring.match( date , pattern ); -- y,m,d = tonumber(y),tonumber(m),tonumber(d); -- result = result .. " "; --<small>Проверено " .. tostring(d) .. " " .. monthg[m] .. " " .. tostring(y) .. ".</small>"; -- end return {text = result, code = src.code}; end function renderShortReference( src ) context = { cache = {}, lang = getSingle( src.lang ) or i18nDefaultLanguage; }; src.title = src.title or '\'\'(unspecified title)\'\'' local result = '[[#' .. PREFIX_CITEREF .. src.code .. '|'; if ( src.author ) then result = result .. toString( context, src.author, options_authors_nolinks ); else result = result .. toString( context, src.title, options_commas_it_nolinks ); end result = result .. ']]' if ( src.year ) then result = result .. ', ' .. toString( context, src.year, options_commas ); end if ( src.volume ) then local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.volume, options_commas ) .. '.'; end if ( src.issue ) then result = result .. ' — №&nbsp;' .. toString( context, src.issue, options_commas ) .. '.'; end if ( src.page ) then local letter = i18nPage[ context.lang ] or i18nPage[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.page, options_commas ) .. '.'; end end function getSingle( value ) if ( not value ) then return; end if ( type( value ) == 'string' ) then return value; elseif ( type( value ) == 'table' ) then if ( value.id ) then return value.id; end for i, tableValue in pairs( value ) do return getSingle( tableValue ); end end return '(unknown)'; end function fSelf( value ) return value; end function toString( context, value, options ) if ( type( value ) == 'string' ) then return options.format( value ); elseif ( type( value ) == 'table' ) then if ( value.id ) then -- this is link if ( options.preferids ) then return options.format( value.id ); else if ( options.nolinks ) then local formatLabel = options.formatLabel or fSelf; return options.format( formatLabel( value.label or mw.wikibase.label( value.id ) or '\'\'(untranslated title)\'\'' ) ); else return options.format( renderLink( context, value.id, value.label, options ) ); end end end local resultList = {}; for i, tableValue in pairs( value ) do table.insert( resultList, toString( context, tableValue, options ) ); end return mw.text.listToText( resultList, options.separator, options.conjunction); else return options.format( '(unknown type)' ); end return ''; end function renderLink( context, entityId, customTitle, options ) if ( not entityId ) then error("entityId is not specified"); end local formatLabel = options.formatLabel or fSelf; local title = customTitle; if ( isEmpty( title ) ) then local entity = getEntity( context, entityId ); -- official name P1448 -- short name P1813 if ( isEmpty( title ) and options.short ) then if ( entity.claims and entity.claims.P1813 ) then for _, claim in pairs( entity.claims.P1813 ) do mw.log( claim.mainsnak.datavalue.value.language ); mw.log( context.lang ); mw.log( claim.mainsnak.datavalue.value.language == context.lang ); mw.log( claim.mainsnak.datavalue.value.text ); if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value.language == context.lang ) then title = claim.mainsnak.datavalue.value.text; break; end end end end -- person name P1559 -- labels if ( isEmpty( title ) and entity.labels[ context.lang ] ) then title = entity.labels[ context.lang ].value; mw.log('Got title of ' .. entityId .. ' from label: «' .. title .. '»' ) end end local actualText = formatLabel( title or '\'\'(untranslated)\'\'' ); local link = mw.wikibase.sitelink( entityId ) or ( ':d:' .. entityId ) return '[[' .. link .. '|' .. actualText .. ']]'; end -- Expand special types of references when additional data could be found in OTHER entity properties function expandSpecials( currentEntity, reference, data ) if ( reference.snaks.P248 and reference.snaks.P248[1] and reference.snaks.P248[1].datavalue and reference.snaks.P248[1].datavalue.value["numeric-id"]) then local sourceId = "Q" .. reference.snaks.P248[1].datavalue.value["numeric-id"]; -- Gemeinsame Normdatei -- specified by P227 if ( sourceId == 'Q36578' ) then appendSnaks( currentEntity.claims, 'P227', data, 'title', { format = function( gnd ) return 'Record #' .. gnd; end } ); appendSnaks( currentEntity.claims, 'P227', data, 'url', { format = function( gnd ) return 'http://d-nb.info/gnd/' .. gnd .. '/'; end } ); end -- BNF -- specified by P268 if ( sourceId == 'Q15222191' ) then appendSnaks( currentEntity.claims, 'P268', data, 'title', { format = function( id ) return 'Record #' .. id; end } ); appendSnaks( currentEntity.claims, 'P268', data, 'url', { format = function( id ) return 'http://catalogue.bnf.fr/ark:/12148/cb' .. id; end } ); expandSpecialsQualifiers( currentEntity, 'P268', data ); end -- Find a Grave -- specified by P535 if ( sourceId == 'Q63056' ) then appendSnaks( currentEntity.claims, 'P535', data, 'url', { format = function( id ) return 'http://www.findagrave.com/cgi-bin/fg.cgi?page=gr&GRid=' .. id; end } ); expandSpecialsQualifiers( currentEntity, 'P535', data ); end -- Dizionario Biografico degli Italiani -- specified by P1986 if ( sourceId == 'Q1128537' ) then if ( not data.lang ) then data.lang = { id = 'Q652' } end; appendSnaks( currentEntity.claims, 'P1986', data, 'url', { format = function( id ) return 'http://www.treccani.it/enciclopedia/' .. id .. '_%28Dizionario_Biografico%29/' end } ); expandSpecialsQualifiers( currentEntity, 'P1986', data ); end -- Union List of Artist Names -- specified by P245 if ( sourceId == 'Q2494649' ) then appendSnaks( currentEntity.claims, 'P245', data, 'url', { format = function( id ) return 'http://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid=' .. id end } ); expandSpecialsQualifiers( currentEntity, 'P245', data ); end -- Gran Enciclopèdia Catalana -- specified by P1296 if ( sourceId == 'Q2664168' ) then appendSnaks( currentEntity.claims, 'P1296', data, 'url', { format = function( id ) return 'http://www.enciclopedia.cat/enciclop%C3%A8dies/gran-enciclop%C3%A8dia-catalana/EC-GEC-' .. id .. '.xml'; end } ); expandSpecialsQualifiers( currentEntity, 'P1296', data ); end -- Encyclopædia Britannica online -- specified by P1417 if ( sourceId == 'Q5375741' ) then appendSnaks( currentEntity.claims, 'P1417', data, 'url', { format = function( id ) return 'http://global.britannica.com/EBchecked/topic/' .. id .. '/'; end } ); expandSpecialsQualifiers( currentEntity, 'P1417', data ); end -- Electronic Jewish Encyclopedia (Elektronnaja Evrejskaja Entsiklopedia) -- specified by P1438 if ( sourceId == 'Q1967250' ) then appendSnaks( currentEntity.claims, 'P1438', data, 'url', { format = function( id ) return 'http://www.eleven.co.il/article/' .. id; end } ); expandSpecialsQualifiers( currentEntity, 'P1438', data ); end -- sports-reference.com -- specified by P1447 if ( sourceId == 'Q18002875' ) then appendSnaks( currentEntity.claims, 'P1447', data, 'url', { format = function( id ) return 'http://www.sports-reference.com/olympics/athletes/' .. id .. '.html'; end } ); expandSpecialsQualifiers( currentEntity, 'P1447', data ); end -- do we have appropriate record in P1343 ? local claims = findClaimsByValue( currentEntity, 'P1343', sourceId ); if ( claims and #claims ~= 0 ) then -- appendQualifiers( claims, 'P958', data, 'part', {} ); appendQualifiers( claims, 'P953', data, 'url', {} ); appendQualifiers( claims, 'P854', data, 'url', {} ); appendQualifiers( claims, 'P357', data, 'title', {} ); -- obsolete appendQualifiers( claims, 'P1476', data, 'title', {} ); appendQualifiers( claims, 'P478', data, 'volume', {} ); end end end function expandSpecialsQualifiers( entity, propertyId, result ) if ( entity.claims and entity.claims[propertyId] ) then local claims = entity.claims[propertyId]; -- appendQualifiers( claims, 'P958', result, 'part', {} ); appendQualifiers( claims, 'P953', result, 'url', {} ); appendQualifiers( claims, 'P854', result, 'url', {} ); appendQualifiers( claims, 'P357', result, 'title', {} ); -- obsolete appendQualifiers( claims, 'P1476', result, 'title', {} ); appendQualifiers( claims, 'P478', result, 'volume', {} ); end end function findClaimsByValue( entity, propertyId, value ) local result = {}; if ( entity and entity.claims and entity.claims[propertyId] ) then for i, claim in pairs( entity.claims[propertyId] ) do if ( claim.mainsnak and claim.mainsnak.datavalue ) then local datavalue = claim.mainsnak.datavalue; if ( datavalue.type == "string" and datavalue.value == value or datavalue.type == "wikibase-entityid" and datavalue.value["entity-type"] == "item" and tostring( datavalue.value["numeric-id"] ) == mw.ustring.sub( value, 2 ) ) then table.insert( result, claim ); end end end end return result; end function appendSnaks( allSnaks, snakPropertyId, result, property, options ) -- do not populate twice if ( result[property] ) then return result end; if ( allSnaks and allSnaks[ snakPropertyId ] ) then for k, snak in pairs( allSnaks[ snakPropertyId ] ) do if ( snak and snak.mainsnak and snak.mainsnak.datavalue ) then --it's a claim appendImpl( snak.mainsnak.datavalue, result, property, options ); elseif ( snak and snak.datavalue ) then -- it's a snak appendImpl( snak.datavalue, result, property, options ); end end end end function appendQualifiers( claims, qualifierPropertyId, result, property, options ) -- do not populate twice if ( result[property] ) then return result end; for i, claim in pairs( claims ) do if ( claim.qualifiers and claim.qualifiers[ qualifierPropertyId ] ) then for k, qualifier in pairs( claim.qualifiers[ qualifierPropertyId ] ) do if ( qualifier and qualifier.datavalue ) then appendImpl( qualifier.datavalue, result, property, options ); end end end end end function appendImpl( datavalue, result, property, options ) if ( datavalue.type == 'string' ) then local value = datavalue.value; if ( options.format ) then value = options.format( value ); end if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], value); elseif ( datavalue.type == 'monolingualtext' ) then local value = datavalue.value.text; if ( options.format ) then value = options.format( value ); end if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], value); elseif ( datavalue.type == 'wikibase-entityid' ) then local value = datavalue.value; if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], { id = 'Q' .. value["numeric-id"] }); elseif datavalue.type == 'time' then local value = datavalue.value; if ( options.format ) then value = options.format( value ); end if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], tostring( value.time )); end end function expandPublication( data ) local publication = data.publication; -- use only first one if ( type( publication ) == 'table' and publication[1] and publication[1].id ) then data.publication = publication[1]; publication = data.publication; end if ( publication and publication.id ) then populateSourceData( publication.id, data ); end end function populateSourceData( entityId, plainData ) return populateSourceDataImpl( mw.wikibase.getEntity( entityId ), plainData ); end function populateSourceDataImpl( entity, plainData ) populateDataFromClaims( entity.claims, plainData ); local normativeTitle = getNormativeTitle( entity ) if ( normativeTitle ) then local y, m, d = mw.ustring.match( getSingle( plainData.dateOfCreation ) , "(%-?%d+)%-(%d+)%-(%d+)T" ); y,m,d = tonumber(y),tonumber(m),tonumber(d); plainData.title = { normativeTitle .. " от&nbsp;" .. tostring(d) .. "&nbsp;" .. monthg[m] .. " " .. tostring(y) .. "&nbsp;г. №&nbsp;" .. getSingle( plainData.docNumber ) .. ' «' .. getSingle( plainData.title ) .. '»' } end if ( not plainData.title ) then if ( entity.labels and entity.labels.ru and entity.labels.ru.value ) then plainData.title = { entity.labels.ru.value }; end end return plainData; end function populateDataFromClaims( claims, data ) appendSnaks( claims, 'P50', data, 'author', {} ); appendSnaks( claims, 'P407', data, 'lang', {} ); appendSnaks( claims, 'P364', data, 'lang', {} ); -- appendSnaks( claims, 'P958', data, 'part', {} ); appendSnaks( claims, 'P357', data, 'title', {} ); -- obsolete appendSnaks( claims, 'P1476', data, 'title', {} ); appendSnaks( claims, 'P953', data, 'url', {} ); appendSnaks( claims, 'P854', data, 'url', {} ); appendSnaks( claims, 'P856', data, 'url', {} ); appendSnaks( claims, 'P1433', data, 'publication', {} ); appendSnaks( claims, 'P123', data, 'publisher', {} ); appendSnaks( claims, 'P291', data, 'place', {} ); appendSnaks( claims, 'P304', data, 'page', {} ); appendSnaks( claims, 'P478', data, 'volume', {} ); appendSnaks( claims, 'P571', data, 'dateOfCreation', {} ); appendSnaks( claims, 'P577', data, 'dateOfPublication', {} ); appendSnaks( claims, 'P212', data, 'isbn', {} ); -- ISBN-13 appendSnaks( claims, 'P957', data, 'isbn', {} ); -- ISBN-10 -- web appendSnaks( claims, 'P813', data, 'accessdate', {} ); -- docs appendSnaks( claims, 'P1545', data, 'docNumber', {} ); -- other appendSnaks( claims, 'P31', data, 'type', {} ); return src; end function updateWithRef( reference, src ) -- specified if ( reference.snaks.O662 ) then local cid = reference.snaks.P662[1].datavalue.value; src.code = src.code .. '-cid:' .. cid; src.title = 'Compound Summary for: CID ' .. cid; src.url = 'http://pubchem.ncbi.nlm.nih.gov/summary/summary.cgi?cid=' .. cid; src.publication = { id = 'Q278487', label = 'PubChem' }; end populateDataFromClaims(reference.snaks, src); return src; end function p.renderSource( frame ) local arg = frame.args[1]; return p.renderSourceImpl( mw.text.trim( arg ) ); end function p.renderSourceImpl( entityId ) assertNotNull('entityId', entityId) if ( mw.ustring.sub( entityId, 1, 1 ) ~= 'Q' ) then error( 'Incorrect entity ID: «' .. entityId .. '»' ); end; local value = {}; value["numeric-id"] = string.sub( entityId , 2); local snak = { datavalue = { value = value } }; local properties = {}; properties[1] = snak; local rendered = renderReferenceImpl( mw.wikibase.getEntity(), { snaks = { P248 = properties } } ); if ( rendered ) then return rendered.text end; end function p.renderReference( frame, currentEntity, reference ) -- template call if ( frame and not currentEntity and not reference ) then local value = {}; value["numeric-id"] = string.sub( frame.args[1] , 2); local snak = { datavalue = { value =value } }; local properties = {}; properties[1] = snak; currentEntity = mw.wikibase.getEntity(); reference = { snaks = { P248 = properties } }; end local rendered = renderReferenceImpl( currentEntity, reference ); if ( not rendered ) then return ''; end local result; local code = rendered.code or mw.text.encode( rendered.text ); result = frame:extensionTag( 'ref', rendered.text, {name = code} ) .. '[[Category:Википедия:Статьи с источниками из Викиданных]]'; if ( not rendered.found ) then result = result .. '[[Category:Википедия:Статьи с неоформленными источниками из Викиданных]]'; end return result; end function renderReferenceImpl( currentEntity, reference ) if ( not reference.snaks ) then return nil; end -- данные в простом формате, согласованном с модулями формирования библиографического описания local data = {}; local entityId, sourceEntity; if ( reference and reference.snaks and reference.snaks.P248 ) then for _, snak in pairs ( reference.snaks.P248 ) do if ( snak.datavalue ) then entityId = 'Q' .. snak.datavalue.value["numeric-id"]; sourceEntity = mw.wikibase.getEntity( entityId ); data.code = entityId; data.entityId = entityId; break; end end end updateWithRef( reference, data ); expandSpecials( currentEntity, reference, data ); if ( sourceEntity ) then populateSourceDataImpl( sourceEntity, data ); end expandPublication( data ); local rendered; if ( p.short ) then rendered = renderShortReference( data ); else rendered = renderSource( data ); end if ( mw.ustring.len( rendered.text ) == 0 ) then return nil; end rendered.found = found; return rendered; end function getNormativeTitle( entity ) if ( not entity or not entity.claims or not entity.claims.P31 ) then return; end for _, claim in pairs( entity.claims.P31 ) do if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value["numeric-id"] ) then local classId = 'Q' .. claim.mainsnak.datavalue.value["numeric-id"]; local title = NORMATIVE_DOCUMENTS[ classId ]; if ( title ) then return title; end end end return; end local LANG_CACHE = { Q150 = 'fr', Q188 = 'de', Q1321 = 'es', Q1860 = 'en', Q652 = 'it', Q7737 = 'ru', } function getLangCode( langEntityId ) if ( not langEntityId ) then return; end -- small optimization local cached = LANG_CACHE[ langEntityId ]; if ( cached ) then return cached; end local langEntity = mw.wikibase.getEntity( langEntityId ); if ( not langEntity ) then mw.log( '[getLangCode] Missing entity ' .. langEntityId ); else if ( langEntity.claims and langEntity.claims.P424 ) then for _, claim in pairs( langEntity.claims.P424 ) do if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value ) then return '' .. claim.mainsnak.datavalue.value; end end end end return; end return p; l1r21u5p5jsybvoqj3s5j66uos0tfse Moduuli:Wikidata 828 309 49873 47866 2026-05-09T17:11:21Z Olksolo 356 P558 was deprecated -> P5061 49873 Scribunto text/plain local i18n = { ["errors"] = { ["property-param-not-provided"] = "Не дан параметр свойства", ["entity-not-found"] = "Сущность не найдена.", ["unknown-claim-type"] = "Неизвестный тип заявления.", ["unknown-snak-type"] = "Неизвестный тип снэка.", ["unknown-datavalue-type"] = "Неизвестный тип значения данных.", ["unknown-entity-type"] = "Неизвестный тип сущности.", ["unknown-property-module"] = "Вы должны установить и property-module, и property-function.", ["unknown-claim-module"] = "Вы должны установить и claim-module, и claim-function.", ["unknown-value-module"] = "Вы должны установить и value-module, и value-function.", ["property-module-not-found"] = "Модуль для отображения свойства не найден", ["property-function-not-found"] = "Функция для отображения свойства не найдена", ["claim-module-not-found"] = "Модуль для отображения утверждения не найден.", ["claim-function-not-found"] = "Функция для отображения утверждения не найдена.", ["value-module-not-found"] = "Модуль для отображения значения не найден.", ["value-function-not-found"] = "Функция для отображения значения не найдена." }, ["somevalue"] = "''неизвестно''", ["novalue"] = "", ["circa"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="около, приблизительно">прибл. </span>', ["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="предположительно">предп. </span>', } -- settings, may differ from project to project local categoryLinksToEntitiesWithMissingLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без подписи]]'; local categoryLinksToEntitiesWithWikibaseError = '[[Category:Википедия:Страницы с ошибками скриптов, использующих Викиданные]]'; local categoryLinksToEntitiesWithMissingLocalLanguageLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без русской подписи]]'; local categoryLocalValuePresent = '[[Category:Википедия:Статьи с переопределением значения из Викиданных]]'; local fileDefaultSize = '267x400px'; local outputReferences = true; -- sources that shall be omitted if any preffered sources exists local deprecatedSources = { Q36578 = true, -- Gemeinsame Normdatei Q63056 = true, -- Find a Grave Q15222191 = true, -- BNF }; local preferredSources = { Q5375741 = true, -- Encyclopædia Britannica Online Q17378135 = true, -- Great Soviet Encyclopedia (1969—1978) }; -- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании) local moduleSources = require( 'Module:Sources' ) local WDS = require( 'Module:WikidataSelectors' ); -- Константы local contentLanguageCode = mw.getContentLanguage():getCode(); local p = {} local formatDatavalue, formatEntityId, formatRefs, formatSnak, formatStatement, formatStatementDefault, formatProperty, getSourcingCircumstances, getPropertyDatatype, getPropertyParams, throwError, toBoolean; local function copyTo( obj, target ) for k, v in pairs( obj ) do target[k] = v end return target; end local function min( prev, next ) if ( prev == nil ) then return next; elseif ( prev > next ) then return next; else return prev; end end local function max( prev, next ) if ( prev == nil ) then return next; elseif ( prev < next ) then return next; else return prev; end end local function splitISO8601(str) if 'table' == type(str) then if str.args and str.args[1] then str = '' .. str.args[1] else return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str ) end end local Y, M, D = (function(str) local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" local Y, M, D = mw.ustring.match( str, pattern ) return tonumber(Y), tonumber(M), tonumber(D) end) (str); local h, m, s = (function(str) local pattern = "T(%d+):(%d+):(%d+)%Z"; local H, M, S = mw.ustring.match( str, pattern); return tonumber(H), tonumber(M), tonumber(S); end) (str); local oh,om = ( function(str) if str:sub(-1)=="Z" then return 0,0 end; -- ends with Z, Zulu time -- matches ±hh:mm, ±hhmm or ±hh; else returns nils local pattern = "([-+])(%d%d):?(%d?%d?)$"; local sign, oh, om = mw.ustring.match( str, pattern); sign, oh, om = sign or "+", oh or "00", om or "00"; return tonumber(sign .. oh), tonumber(sign .. om); end )(str) return {year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}; end local function parseTimeBoundaries( time, precision ) local s = splitISO8601( time ); if (not s) then return nil; end if ( precision >= 0 and precision <= 8 ) then local powers = { 1000000000 , 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 } local power = powers[ precision + 1 ]; local left = s.year - ( s.year % power ); return { tonumber(os.time( {year=left, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=left + power - 1, month=12, day=31, hour=29, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 9 ) then return { tonumber(os.time( {year=s.year, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=12, day=31, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 10 ) then local lastDays = {31, 28.25, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local lastDay = lastDays[s.month]; return { tonumber(os.time( {year=s.year, month=s.month, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=lastDay, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 11 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 12 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=59, sec=58} )) * 1000 + 19991999 }; end if ( precision == 13 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=58} )) * 1000 + 1999 }; end if ( precision == 14 ) then local t = tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} ) ); return { t * 1000, t * 1000 + 999 }; end error('Unsupported precision: ' .. precision ); end --[[ Преобразует строку в булевое значение Принимает: строковое значение (может отсутствовать) Возвращает: булевое значение true или false, если получается распознать значение, или defaultValue во всех остальных случаях ]] local function toBoolean( valueToParse, defaultValue ) if ( valueToParse ~= nil ) then if valueToParse == false or valueToParse == '' or valueToParse == 'false' or valueToParse == '0' then return false end return true end return defaultValue; end --[[ Функция для получения сущности (еntity) для текущей страницы Подробнее о сущностях см. d:Wikidata:Glossary/ru Принимает: строковый индентификатор (типа P18, Q42) Возвращает: объект таблицу, элементы которой индексируются с нуля ]] local function getEntityFromId( id ) local entity; local wbStatus; if id then wbStatus, entity = pcall( mw.wikibase.getEntityObject, id ) end wbStatus, entity = pcall( mw.wikibase.getEntityObject ); return entity; end --[[ Внутрення функция для формирования сообщения об ошибке Принимает: ключ элемента в таблице i18n (например entity-not-found) Возвращает: строку сообщения ]] local function throwError( key ) error( i18n.errors[key] ); end --[[ Функция для получения идентификатора сущностей Принимает: объект таблицу сущности Возвращает: строковый индентификатор (типа P18, Q42) ]] local function getEntityIdFromValue( value ) local prefix = '' if value['entity-type'] == 'item' then prefix = 'Q' elseif value['entity-type'] == 'property' then prefix = 'P' else throwError( 'unknown-entity-type' ) end return prefix .. value['numeric-id'] end -- проверка на наличие специилизированной функции в опциях local function getUserFunction( options, prefix, defaultFunction ) -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове if options[ prefix .. '-module' ] or options[ prefix .. '-function' ] then -- проверка на пустые строки в параметрах или их отсутствие if not options[ prefix .. '-module' ] or not options[ prefix .. '-function' ] then throwError( 'unknown-' .. prefix .. '-module' ); end -- динамическая загруза модуля с обработчиком указанным в параметре local formatter = require ('Module:' .. options[ prefix .. '-module' ]); if formatter == nil then throwError( prefix .. '-module-not-found' ) end local fun = formatter[ options[ prefix .. '-function' ] ] if fun == nil then throwError( prefix .. '-function-not-found' ) end return fun; end return defaultFunction; end -- Выбирает свойства по property id, дополнительно фильтруя их по рангу local function selectClaims( context, options, propertySelector ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity is missing' ); end; if ( not propertySelector ) then error( 'propertySelector not specified' ); end; result = WDS.filter( options.entity.claims, propertySelector ); if ( not result or #result == 0 ) then return nil; end if options.limit and options.limit ~= '' and options.limit ~= '-' then local limit = tonumber( options.limit, 10 ); while #result > limit do table.remove( result ); end end return result; end --[[ Функция для получения значения свойства элемента в заданный момент времени. Принимает: контекст, элемент, временные границы, таблица ID свойства Возвращает: таблицу соответствующих значений свойства ]] local function getPropertyInBoundaries( context, entity, boundaries, propertyIds ) local results = {}; if not propertyIds or #propertyIds == 0 then return results; end if entity.claims then for _, propertyId in ipairs( propertyIds ) do local filteredClaims = WDS.filter( entity.claims, propertyId .. '[rank:preferred, rank:normal]' ); if filteredClaims then for _, claim in pairs( filteredClaims ) do if not boundaries or not propertyIds or #propertyIds == 0 then table.insert( results, claim.mainsnak ); else local startBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P580' ); local endBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P582' ); if ( (startBoundaries == nil or ( startBoundaries[2] <= boundaries[1])) and (endBoundaries == nil or ( endBoundaries[1] >= boundaries[2]))) then table.insert( results, claim.mainsnak ); end end end end if #results > 0 then break; end end end return results; end --[[ TODO ]] function p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ) -- only support exact date so far, but need improvment local left = nil; local right = nil; if ( statement.qualifiers and statement.qualifiers[qualifierId] ) then for _, qualifier in pairs( statement.qualifiers[qualifierId] ) do local boundaries = context.parseTimeBoundariesFromSnak( qualifier ); if ( not boundaries ) then return nil; end left = min( left, boundaries[1] ); right = max( right, boundaries[2] ); end end if ( not left or not right ) then return nil; end return { left, right }; end --[[ TODO ]] function p.getTimeBoundariesFromQualifiers( frame, context, statement, qualifierIds ) if not qualifierIds then qualifierIds = { 'P582', 'P580', 'P585' }; end for _, qualifierId in ipairs( qualifierIds ) do local result = p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ); if result then return result; end end return nil; end --[[ Функция для получения метки элемента в заданный момент времени. Принимает: контекст, элемент, временные границы Возвращает: текстовую метку элемента, язык метки ]] function getLabelWithLang( context, options, entity, boundaries, propertyIds ) if not entity then return nil; end local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); -- name from label local label = nil; if ( options.text and options.text ~= '' ) then label = options.text; else label, langCode = entity:getLabelWithLang(); if not langCode then return nil; end if not propertyIds then propertyIds = { 'P1813[language:' .. langCode .. ']', 'P1448[language:' .. langCode .. ']', 'P1705[language:' .. langCode .. ']' }; end -- name from properties local results = getPropertyInBoundaries( context, entity, boundaries, propertyIds ); for _, result in pairs( results ) do if result.datavalue and result.datavalue.value then if result.datavalue.type == 'monolingualtext' and result.datavalue.value.text then label = result.datavalue.value.text; lang = result.datavalue.value.language; break; elseif result.datavalue.type == 'string' then label = result.datavalue.value; break; end end end end return label, langCode; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] local function formatProperty( options ) -- Получение сущности по идентификатору local entity = getEntityFromId( options.entityId ) if not entity then return -- throwError( 'entity-not-found' ) end -- проверка на присутсвие у сущности заявлений (claim) -- подробнее о заявлениях см. d:Викиданные:Глоссарий if (entity.claims == nil) then return '' --TODO error? end -- improve options options.frame = g_frame; options.entity = entity; options.extends = function( self, newOptions ) return copyTo( newOptions, copyTo( self, {} ) ) end if ( options.i18n ) then options.i18n = copyTo( options.i18n, copyTo( i18n, {} ) ); else options.i18n = i18n; end -- create context local context = { entity = options.entity, formatSnak = formatSnak, formatPropertyDefault = formatPropertyDefault, formatStatementDefault = formatStatementDefault } context.cloneOptions = function( options ) local entity = options.entity; options.entity = nil; newOptions = mw.clone( options ); options.entity = entity; newOptions.entity = entity; return newOptions; end; context.formatProperty = function( options ) local func = getUserFunction( options, 'property', context.formatPropertyDefault ); return func( context, options ) end; context.formatStatement = function( options, statement ) return formatStatement( context, options, statement ) end; context.formatSnak = function( options, snak, circumstances ) return formatSnak( context, options, snak, circumstances ) end; context.formatRefs = function( options, statement ) return formatRefs( context, options, statement ) end; context.parseTimeFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then return tonumber(os.time( splitISO8601( tostring( snak.datavalue.value.time ) ) ) ) * 1000; end return nil; end context.parseTimeBoundariesFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time and snak.datavalue.value.precision ) then return parseTimeBoundaries( snak.datavalue.value.time, snak.datavalue.value.precision ); end return nil; end context.getSourcingCircumstances = function( statement ) return getSourcingCircumstances( statement ) end; context.selectClaims = function( options, propertyId ) return selectClaims( context, options, propertyId ) end; return context.formatProperty( options ); end function formatPropertyDefault( context, options ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; local claims; if options.property then -- TODO: Почему тут может не быть property? claims = context.selectClaims( options, options.property ); end if claims == nil then return '' --TODO error? end -- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных -- заявлений в таблице local formattedClaims = {} for i, claim in ipairs(claims) do local formattedStatement = context.formatStatement( options, claim ) -- здесь может вернуться либо оформленный текст заявления -- либо строка ошибки nil похоже никогда не возвращается if (formattedStatement) then formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( options.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>' table.insert( formattedClaims, formattedStatement ) end end -- создание текстовой строки со списком оформленых заявлений из таблицы local out = mw.text.listToText( formattedClaims, options.separator, options.conjunction ) if out ~= '' then if options.before then out = options.before .. out end if options.after then out = out .. options.after end end return out end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение и таблицу параметров Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatement( context, options, statement ) if ( not statement ) then error( 'statement is not specified or nil' ); end if not statement.type or statement.type ~= 'statement' then throwError( 'unknown-claim-type' ) end local functionToCall = getUserFunction( options, 'claim', context.formatStatementDefault ); return functionToCall( context, options, statement ); end function getSourcingCircumstances( statement ) if (not statement) then error('statement is not specified') end; local circumstances = {}; if ( statement.qualifiers and statement.qualifiers.P1480 ) then for i, qualifier in pairs( statement.qualifiers.P1480 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'wikibase-entityid' and qualifier.datavalue.value and qualifier.datavalue.value['entity-type'] == 'item' ) then local circumstance = qualifier.datavalue.value.id; if ( 'Q5727902' == circumstance ) then circumstances.circa = true; end if ( 'Q18122778' == circumstance ) then circumstances.presumably = true; end end end end return circumstances; end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение, таблицу параметров, объект-функцию оформления внутренних структур утверждения (snak) и объект-функцию оформления ссылки на источники (reference) Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatementDefault( context, options, statement ) if (not context) then error('context is not specified') end; if (not options) then error('options is not specified') end; if (not statement) then error('statement is not specified') end; local circumstances = context.getSourcingCircumstances( statement ); options.qualifiers = statement.qualifiers; if ( options.references ) then return context.formatSnak( options, statement.mainsnak, circumstances ) .. context.formatRefs( options, statement ); else return context.formatSnak( options, statement.mainsnak, circumstances ); end end --[[ Функция для оформления части утверждения (snak) Подробнее о snak см. d:Викиданные:Глоссарий Принимает: таблицу snak объекта (main snak или же snak от квалификатора) и таблицу опций Возвращает: строку оформленного викитекста ]] function formatSnak( context, options, snak, circumstances ) circumstances = circumstances or {}; local hash = ''; local mainSnakClass = ''; if ( snak.hash ) then hash = ' data-wikidata-hash="' .. snak.hash .. '"'; else mainSnakClass = ' wikidata-main-snak'; end local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>' local after = '</span>' if snak.snaktype == 'somevalue' then if ( options['somevalue'] and options['somevalue'] ~= '' ) then return before .. options['somevalue'] .. after; end return before .. options.i18n['somevalue'] .. after; elseif snak.snaktype == 'novalue' then if ( options['novalue'] and options['novalue'] ~= '' ) then return before .. options['novalue'] .. after; end return before .. options.i18n['novalue'] .. after; elseif snak.snaktype == 'value' then if ( circumstances.presumably ) then before = before .. options.i18n.presumably; end if ( circumstances.circa ) then before = before .. options.i18n.circa; end return before .. formatDatavalue( context, options, snak.datavalue, snak.datatype ) .. after; else throwError( 'unknown-snak-type' ); end end --[[ Функция для оформления объектов-значений с географическими координатами Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatGlobeCoordinate( value, options ) -- проверка на требование в параметрах вызова на возврат сырого значения if options['subvalue'] == 'latitude' then -- широты return value['latitude'] elseif options['subvalue'] == 'longitude' then -- долготы return value['longitude'] elseif options['nocoord'] and options['nocoord'] ~= '' then -- если передан параметр nocoord, то не выводить координаты -- обычно это делается при использовании нескольких карточек на странице return '' else -- в противном случае формируются параметры для вызова шаблона {{coord}} -- нужно дописать в документации шаблона, что он отсюда вызывается, и что -- любое изменние его парамеров должно быть согласовано с кодом тут local eps = 0.0000001 -- < 1/360000 local globe = options.globe or '' -- TODO local lat = {} lat['abs'] = math.abs(value['latitude']) lat['ns'] = value['latitude'] >= 0 and 'N' or 'S' lat['d'] = math.floor(lat['abs'] + eps) lat['m'] = math.floor((lat['abs'] - lat['d']) * 60 + eps) lat['s'] = math.max(0, ((lat['abs'] - lat['d']) * 60 - lat['m']) * 60 + eps) local lon = {} lon['abs'] = math.abs(value['longitude']) lon['ew'] = value['longitude'] >= 0 and 'E' or 'W' lon['d'] = math.floor(lon['abs'] + eps) lon['m'] = math.floor((lon['abs'] - lon['d']) * 60 + eps) lon['s'] = math.max(0, ((lon['abs'] - lon['d']) * 60 - lon['m']) * 60 + eps) -- TODO: round seconds with precision local coord = '{{coord' if (value['precision'] == nil) or (value['precision'] < 1/60) then -- по умолчанию с точностью до секунды coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['s'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['s'] .. '|' .. lon['ew'] elseif value['precision'] < 1 then coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['ew'] else coord = coord .. '|' .. lat['d'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['ew'] end coord = coord .. '|globe:' .. globe if options['type'] and options['type'] ~= '' then coord = coord .. '|type=' .. options.type end if options['display'] and options['display'] ~= '' then coord = coord .. '|display=' .. options.display else coord = coord .. '|display=title' end coord = coord .. '}}' return g_frame:preprocess(coord) end end --[[ Функция для оформления объектов-значений с файлами с Викисклада Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatCommonsMedia( value, options ) local image = value local caption = '' if options['caption'] and options['caption'] ~= '' then caption = options['caption'] elseif options['description'] and options['description'] ~= '' then caption = options['description'] end if caption ~= '' then caption = '<span data-wikidata-qualifier-id="P2096" style="display:block">' .. caption .. '</span>' end if not string.find( value, '[%[%]%{%}]' ) then image = '[[File:' .. value if options['border'] and options['border'] ~= '' then image = image .. '|border' end local size = options['size'] if size and size ~= '' then if not string.match( size, 'px$' ) and not string.match( size, 'пкс$' ) -- TODO: использовать перевод для языка вики then size = size .. 'px' end else size = fileDefaultSize; end image = image .. '|' .. size if options['alt'] and options['alt'] ~= '' then image = image .. '|' .. options['alt'] end image = image .. ']]' if caption ~= '' then image = image .. '<br>' .. caption end else image = image .. caption end return image end --[[ Функция для оформления внешних идентификаторов Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatExternalId( value, options ) local formatter = options.formatter if not formatter or formatter == '' then local wbStatus, entity = pcall( mw.wikibase.getEntity, options.property:upper() ) if wbStatus == true and entity then local statements = entity:getBestStatements( 'P1630' ) for _, statement in pairs( statements ) do if statement.mainsnak.snaktype == 'value' then formatter = statement.mainsnak.datavalue.value break end end end end if formatter and formatter ~= '' then local link = mw.ustring.gsub( formatter, '$1', value ) local title = options.title if not title or title == '' then title = '$1' end title = mw.ustring.gsub( title, '$1', value ) return '[' .. link .. ' ' .. title .. ']' end return value end --[[ Функция для оформления числовых значений Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatQuantity( value, options ) -- диапазон значений local amount = string.gsub( value['amount'], '^%+', '' ); local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); local function formatNum( number ) -- округление до 13 знаков после запятой, на 14-м возникает ошибка в точности local mult = 10^13 number = math.floor( number * mult + 0.5 ) / mult return lang:formatNum( number ) end local out = formatNum( tonumber( amount ) ); if value.upperBound then local diff = tonumber( value.upperBound ) - tonumber( amount ) if diff > 0 then -- временная провека, пока у большинства значений не будет убрано ±0 out = out .. '±' .. formatNum( diff ) end end if options.unit and options.unit ~= '' then if options.unit ~= '-' then out = out .. ' ' .. options.unit end elseif value.unit and string.match( value.unit, 'http://www.wikidata.org/entity/' ) then local unitEntityId = string.gsub( value.unit, 'http://www.wikidata.org/entity/', '' ); local wbStatus, unitEntity = pcall( mw.wikibase.getEntity, unitEntityId ); if wbStatus == true and unitEntity then local writingSystemElementId = 'Q8209'; local langElementId = 'Q7737'; local label = getLabelWithLang( context, options, unitEntity, nil, { 'P5061[language:' .. langCode .. ']', 'P558[P282:' .. writingSystemElementId .. ', P407:' .. langElementId .. ']', 'P558[!P282][!P407]' } ); out = out .. ' ' .. label; end end return out; end --[[ Get property datatype by ID. @param string Property ID, e.g. 'P123'. @return string Property datatype, e.g. 'commonsMedia', 'time' or 'url'. ]] local function getPropertyDatatype( propertyId ) if not propertyId or not string.match( propertyId, '^P%d+$' ) then return nil; end local wbStatus, propertyEntity = pcall( mw.wikibase.getEntity, propertyId ); if wbStatus ~= true or not propertyEntity then return nil; end return propertyEntity.datatype; end local function getDefaultValueFunction( datavalue, datatype ) -- вызов обработчиков по умолчанию для известных типов значений if datavalue.type == 'wikibase-entityid' then -- Entity ID return function( context, options, value ) return formatEntityId( context, options, getEntityIdFromValue( value ) ) end; elseif datavalue.type == 'string' then -- String if datatype and datatype == 'commonsMedia' then -- Media return function( context, options, value ) if ( not options.caption or options.caption == '' ) and ( not options.description or options.description == '' ) and options.qualifiers and options.qualifiers.P2096 then for i, qualifier in pairs( options.qualifiers.P2096 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'monolingualtext' and qualifier.datavalue.value and qualifier.datavalue.value.language == contentLanguageCode ) then options.caption = qualifier.datavalue.value.text options.description = qualifier.datavalue.value.text break end end end return formatCommonsMedia( value, options ) end; elseif datatype and datatype == 'external-id' then -- External ID return function( context, options, value ) return formatExternalId( value, options ) end elseif datatype and datatype == 'url' then -- URL return function( context, options, value ) local moduleUrl = require( 'Module:URL' ) if not options.length or options.length == '' then options.length = 25 end return moduleUrl.formatUrlSingle( context, options, value ); end end return function( context, options, value ) return value end; elseif datavalue.type == 'monolingualtext' then -- моноязычный текст (строка с указанием языка) return function( context, options, value ) if ( options.monolingualLangTemplate == 'lang' ) then return options.frame:expandTemplate{ title = 'lang-' .. value.language, args = { value.text } }; elseif ( options.monolingualLangTemplate == 'ref' ) then return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>' .. options.frame:expandTemplate{ title = 'ref-' .. value.language }; else return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>'; end end; elseif datavalue.type == 'globecoordinate' then -- географические координаты return function( context, options, value ) return formatGlobeCoordinate( value, options ) end; elseif datavalue.type == 'quantity' then return function( context, options, value ) return formatQuantity( value, options ) end; elseif datavalue.type == 'time' then return function( context, options, value ) local moduleDate = require( 'Module:Wikidata/date' ) return moduleDate.formatDate( context, options, value ); end; else -- во всех стальных случаях возвращаем ошибку throwError( 'unknown-datavalue-type' ) end end --[[ Функция для оформления значений (value) Подробнее о значениях см. d:Wikidata:Glossary/ru Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatDatavalue( context, options, datavalue, datatype ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not datavalue ) then error( 'datavalue not specified' ); end; if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end; -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове context.formatValueDefault = getDefaultValueFunction( datavalue, datatype ); local functionToCall = getUserFunction( options, 'value', context.formatValueDefault ); return functionToCall( context, options, datavalue.value ); end --[[ Функция для оформления идентификатора сущности Принимает: строку индентификатора (типа Q42) и таблицу параметров, Возвращает: строку оформленного текста ]] function formatEntityId( context, options, entityId ) -- получение локализованного названия local wbStatus, entity = pcall( mw.wikibase.getEntity, entityId ) if wbStatus ~= true then return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="color:#b32424; border-bottom: 1px dotted #b32424; cursor: help; white-space: nowrap" title="Ошибка получения элемента из Викиданных.">×</span>' .. categoryLinksToEntitiesWithWikibaseError; end local boundaries = nil if options.qualifiers then boundaries = p.getTimeBoundariesFromQualifiers( frame, context, { qualifiers = options.qualifiers } ) end local label, labelLanguageCode = getLabelWithLang( context, options, entity, boundaries ) -- определение соответствующей показываемому элементу категории local category = '' if ( options.category ) then local claims = WDS.filter( entity.claims, options.category ); if ( claims ) then for _, claim in pairs( claims ) do if ( claim.mainsnak and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.type == 'wikibase-entityid' ) then local catEntityId = claim.mainsnak.datavalue.value.id; local wbStatus, catEntity = pcall( mw.wikibase.getEntity, catEntityId ); if ( wbStatus == true and catEntity and catEntity:getSitelink() ) then category = '[[' .. catEntity:getSitelink() .. ']]'; end end end end end -- получение ссылки по идентификатору local link = mw.wikibase.sitelink( entityId ) if link then -- ссылка на категорию, а не добавление страницы в неё if mw.ustring.match( link, '^' .. mw.site.namespaces[ 14 ].name .. ':' ) then link = ':' .. link end if label then if ( contentLanguageCode ~= labelLanguageCode ) then return '[[' .. link .. '|' .. label .. ']]' .. categoryLinksToEntitiesWithMissingLocalLanguageLabel .. category; else return '[[' .. link .. '|' .. label .. ']]' .. category; end else return '[[' .. link .. ']]' .. category; end end if label then -- красная ссылка -- TODO: разобраться, почему не всегда есть options.frame local title = mw.title.new( label ); if title and not title.exists and options.frame then return '[[' .. label .. ']]<sup>[[:d:' .. entityId .. '|[d]]]</sup>' .. category; end -- TODO: перенести до проверки на существование статьи local sup = ''; if ( not options.format or options.format ~= 'text' ) and entityId ~= 'Q6581072' and entityId ~= 'Q6581097' -- TODO: переписать на format=text then sup = '<sup class="plainlinks noprint">[//www.wikidata.org/wiki/' .. entityId .. '?uselang=' .. contentLanguageCode .. ' [d&#x5d;]</sup>' end -- одноимённая статья уже существует - выводится текст и ссылка на ВД return '<span class="iw" data-title="' .. label .. '">' .. label .. sup .. '</span>' .. category end -- сообщение об отсутвии локализованного названия -- not good, but better than nothing return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="В Викиданных нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>' .. categoryLinksToEntitiesWithMissingLabel .. category; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] -- устаревшее имя, не использовать function p.formatStatements( frame ) return p.formatProperty( frame ); end --[[ Получение параметров, которые обычно используются для вывода свойства. ]] function getPropertyParams( propertyId, datatype, params ) local config = require( 'Module:Wikidata/config' ); if not config then return {}; end -- Различные уровни настройки параметров, по убыванию приоритета local propertyParams = {}; -- 1. Параметры, указанные явно при вызове if params then local tplParams = mw.clone( params ); for key, value in pairs( tplParams ) do if value ~= '' then propertyParams[key] = value; end end end -- 2. Настройки конкретного параметра if config['properties'] and config['properties'][propertyId] then local selfParams = mw.clone( config['properties'][propertyId] ); for key, value in pairs( selfParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 3. Указанный пресет настроек if propertyParams['preset'] and config['presets'] and config['presets'][propertyParams['preset']] then local presetParams = mw.clone( config['presets'][propertyParams['preset']] ); for key, value in pairs( presetParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 4. Настройки для типа данных if datatype and config['datatypes'] and config['datatypes'][datatype] then local datatypeParams = mw.clone( config['datatypes'][datatype] ); for key, value in pairs( datatypeParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 5. Общие настройки для всех свойств if config['global'] then local globalParams = mw.clone( config['global'] ); for key, value in pairs( globalParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end return propertyParams; end function p.formatProperty( frame ) local args = frame.args -- проверка на отсутствие обязательного параметра property if not args.property then throwError( 'property-param-not-provided' ) end local propertyId = mw.language.getContentLanguage():ucfirst( string.gsub( args.property, '%[.*$', '' ) ) local datatype = getPropertyDatatype( propertyId ); args = getPropertyParams( propertyId, datatype, args ); -- проброс всех параметров из шаблона {wikidata} local p_frame = frame:getParent(); if p_frame and p_frame:getTitle() == mw.site.namespaces[10].name .. ':Wikidata' then copyTo( p_frame.args, args ); end args.plain = toBoolean( args.plain, false ); args.nocat = toBoolean( args.nocat, false ); args.references = toBoolean( args.references, true ); -- если значение передано в параметрах вызова то выводим только его if args.value and args.value ~= '' then -- специальное значение для скрытия Викиданных if args.value == '-' then return '' end local value = args.value -- опция, запрещающая оформление значения, поэтому никак не трогаем if args.plain then return value end -- обработчики по типу значения local wrapperExtraArgs = '' if args['value-module'] and args['value-function'] and not string.find( value, '[%[%]%{%}]' ) then local func = getUserFunction( args, 'value' ); value = func( {}, args, value ); elseif datatype == 'commonsMedia' then value = formatCommonsMedia( value, args ); elseif datatype == 'external-id' and not string.find( value, '[%[%]%{%}]' ) then wrapperExtraArgs = wrapperExtraArgs .. ' data-wikidata-external-id="' .. mw.text.encode( value ).. '"'; value = formatExternalId( value, args ); elseif datatype == 'url' then local moduleUrl = require( 'Module:URL' ); value = moduleUrl.formatUrlSingle( nil, args, value ); end -- оборачиваем в тег для JS-функций if string.match( propertyId, '^P%d+$' ) then value = mw.text.trim( value ) -- временная штрафная категория для исправления табличных вставок if ( propertyId ~= 'P166' and string.match( value, '<t[dr][ >]' ) and not string.match( value, '<table >]' ) and not string.match( value, '^%{%|' ) ) then value = value .. '[[Category:Википедия:Статьи с табличной вставкой в карточке]]' else -- значений с блочными тегами остаются блоком, текст встраиваем в строку if ( string.match( value, '\n' ) or string.match( value, '<t[dhr][ >]' ) or string.match( value, '<div[ >]' ) ) then value = '<div class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">\n' .. value .. '</div>' else value = '<span class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">' .. value .. '</span>' end end end -- добавляем категорию-маркер if not args.nocat then local pageTitle = mw.title.getCurrentTitle(); if pageTitle.namespace == 0 then value = value .. categoryLocalValuePresent; end end return value end if ( args.plain ) then -- вызова стандартного обработчика без оформления, если передана опция plain return frame:callParserFunction( '#property', propertyId ); end g_frame = frame -- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений) return formatProperty( args ) end --[[ Функция оформления ссылок на источники (reference) Подробнее о ссылках на источники см. d:Wikidata:Glossary/ru Экспортируется в качестве зарезервированной точки для вызова из функций-расширения вида claim-module/claim-function через context Вызов из других модулей напрямую осуществляться не должен (используйте frame:expandTemplate вместе с одним из специлизированных шаблонов вывода значения свойства). Принимает: объект-таблицу утверждение Возвращает: строку оформленных ссылок для отображения в статье ]] function formatRefs( context, options, statement ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; if ( not statement ) then error( 'statement not specified' ); end; if ( not outputReferences ) then return ''; end local result = ''; if ( statement.references ) then local allReferences = statement.references; -- local hasPreferred = false; -- for _, reference in pairs( statement.references ) do -- if ( reference.snaks -- and reference.snaks.P248 -- and reference.snaks.P248[1] -- and reference.snaks.P248[1].datavalue -- and reference.snaks.P248[1].datavalue.value.id ) then -- local entityId = reference.snaks.P248[1].datavalue.value.id; -- if ( preferredSources[entityId] ) then -- hasPreferred = true; -- end -- end -- end for _, reference in pairs( statement.references ) do local display = false; -- if ( hasPreferred ) then if ( reference.snaks and reference.snaks.P248 and reference.snaks.P248[1] and reference.snaks.P248[1].datavalue and reference.snaks.P248[1].datavalue.value.id ) then local entityId = reference.snaks.P248[1].datavalue.value.id; if (not deprecatedSources[entityId] ) then display = true; end end -- end if ( display ) then result = result .. moduleSources.renderReference( g_frame, options.entity, reference ); end end end return result end return p 1q9r8q9ssbni9ij7p8sp5jipuzhw51d 49874 49873 2026-05-09T17:19:52Z Olksolo 356 + P5061[language:mul] 49874 Scribunto text/plain local i18n = { ["errors"] = { ["property-param-not-provided"] = "Не дан параметр свойства", ["entity-not-found"] = "Сущность не найдена.", ["unknown-claim-type"] = "Неизвестный тип заявления.", ["unknown-snak-type"] = "Неизвестный тип снэка.", ["unknown-datavalue-type"] = "Неизвестный тип значения данных.", ["unknown-entity-type"] = "Неизвестный тип сущности.", ["unknown-property-module"] = "Вы должны установить и property-module, и property-function.", ["unknown-claim-module"] = "Вы должны установить и claim-module, и claim-function.", ["unknown-value-module"] = "Вы должны установить и value-module, и value-function.", ["property-module-not-found"] = "Модуль для отображения свойства не найден", ["property-function-not-found"] = "Функция для отображения свойства не найдена", ["claim-module-not-found"] = "Модуль для отображения утверждения не найден.", ["claim-function-not-found"] = "Функция для отображения утверждения не найдена.", ["value-module-not-found"] = "Модуль для отображения значения не найден.", ["value-function-not-found"] = "Функция для отображения значения не найдена." }, ["somevalue"] = "''неизвестно''", ["novalue"] = "", ["circa"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="около, приблизительно">прибл. </span>', ["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="предположительно">предп. </span>', } -- settings, may differ from project to project local categoryLinksToEntitiesWithMissingLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без подписи]]'; local categoryLinksToEntitiesWithWikibaseError = '[[Category:Википедия:Страницы с ошибками скриптов, использующих Викиданные]]'; local categoryLinksToEntitiesWithMissingLocalLanguageLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без русской подписи]]'; local categoryLocalValuePresent = '[[Category:Википедия:Статьи с переопределением значения из Викиданных]]'; local fileDefaultSize = '267x400px'; local outputReferences = true; -- sources that shall be omitted if any preffered sources exists local deprecatedSources = { Q36578 = true, -- Gemeinsame Normdatei Q63056 = true, -- Find a Grave Q15222191 = true, -- BNF }; local preferredSources = { Q5375741 = true, -- Encyclopædia Britannica Online Q17378135 = true, -- Great Soviet Encyclopedia (1969—1978) }; -- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании) local moduleSources = require( 'Module:Sources' ) local WDS = require( 'Module:WikidataSelectors' ); -- Константы local contentLanguageCode = mw.getContentLanguage():getCode(); local p = {} local formatDatavalue, formatEntityId, formatRefs, formatSnak, formatStatement, formatStatementDefault, formatProperty, getSourcingCircumstances, getPropertyDatatype, getPropertyParams, throwError, toBoolean; local function copyTo( obj, target ) for k, v in pairs( obj ) do target[k] = v end return target; end local function min( prev, next ) if ( prev == nil ) then return next; elseif ( prev > next ) then return next; else return prev; end end local function max( prev, next ) if ( prev == nil ) then return next; elseif ( prev < next ) then return next; else return prev; end end local function splitISO8601(str) if 'table' == type(str) then if str.args and str.args[1] then str = '' .. str.args[1] else return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str ) end end local Y, M, D = (function(str) local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" local Y, M, D = mw.ustring.match( str, pattern ) return tonumber(Y), tonumber(M), tonumber(D) end) (str); local h, m, s = (function(str) local pattern = "T(%d+):(%d+):(%d+)%Z"; local H, M, S = mw.ustring.match( str, pattern); return tonumber(H), tonumber(M), tonumber(S); end) (str); local oh,om = ( function(str) if str:sub(-1)=="Z" then return 0,0 end; -- ends with Z, Zulu time -- matches ±hh:mm, ±hhmm or ±hh; else returns nils local pattern = "([-+])(%d%d):?(%d?%d?)$"; local sign, oh, om = mw.ustring.match( str, pattern); sign, oh, om = sign or "+", oh or "00", om or "00"; return tonumber(sign .. oh), tonumber(sign .. om); end )(str) return {year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}; end local function parseTimeBoundaries( time, precision ) local s = splitISO8601( time ); if (not s) then return nil; end if ( precision >= 0 and precision <= 8 ) then local powers = { 1000000000 , 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 } local power = powers[ precision + 1 ]; local left = s.year - ( s.year % power ); return { tonumber(os.time( {year=left, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=left + power - 1, month=12, day=31, hour=29, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 9 ) then return { tonumber(os.time( {year=s.year, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=12, day=31, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 10 ) then local lastDays = {31, 28.25, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local lastDay = lastDays[s.month]; return { tonumber(os.time( {year=s.year, month=s.month, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=lastDay, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 11 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 12 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=59, sec=58} )) * 1000 + 19991999 }; end if ( precision == 13 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=58} )) * 1000 + 1999 }; end if ( precision == 14 ) then local t = tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} ) ); return { t * 1000, t * 1000 + 999 }; end error('Unsupported precision: ' .. precision ); end --[[ Преобразует строку в булевое значение Принимает: строковое значение (может отсутствовать) Возвращает: булевое значение true или false, если получается распознать значение, или defaultValue во всех остальных случаях ]] local function toBoolean( valueToParse, defaultValue ) if ( valueToParse ~= nil ) then if valueToParse == false or valueToParse == '' or valueToParse == 'false' or valueToParse == '0' then return false end return true end return defaultValue; end --[[ Функция для получения сущности (еntity) для текущей страницы Подробнее о сущностях см. d:Wikidata:Glossary/ru Принимает: строковый индентификатор (типа P18, Q42) Возвращает: объект таблицу, элементы которой индексируются с нуля ]] local function getEntityFromId( id ) local entity; local wbStatus; if id then wbStatus, entity = pcall( mw.wikibase.getEntityObject, id ) end wbStatus, entity = pcall( mw.wikibase.getEntityObject ); return entity; end --[[ Внутрення функция для формирования сообщения об ошибке Принимает: ключ элемента в таблице i18n (например entity-not-found) Возвращает: строку сообщения ]] local function throwError( key ) error( i18n.errors[key] ); end --[[ Функция для получения идентификатора сущностей Принимает: объект таблицу сущности Возвращает: строковый индентификатор (типа P18, Q42) ]] local function getEntityIdFromValue( value ) local prefix = '' if value['entity-type'] == 'item' then prefix = 'Q' elseif value['entity-type'] == 'property' then prefix = 'P' else throwError( 'unknown-entity-type' ) end return prefix .. value['numeric-id'] end -- проверка на наличие специилизированной функции в опциях local function getUserFunction( options, prefix, defaultFunction ) -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове if options[ prefix .. '-module' ] or options[ prefix .. '-function' ] then -- проверка на пустые строки в параметрах или их отсутствие if not options[ prefix .. '-module' ] or not options[ prefix .. '-function' ] then throwError( 'unknown-' .. prefix .. '-module' ); end -- динамическая загруза модуля с обработчиком указанным в параметре local formatter = require ('Module:' .. options[ prefix .. '-module' ]); if formatter == nil then throwError( prefix .. '-module-not-found' ) end local fun = formatter[ options[ prefix .. '-function' ] ] if fun == nil then throwError( prefix .. '-function-not-found' ) end return fun; end return defaultFunction; end -- Выбирает свойства по property id, дополнительно фильтруя их по рангу local function selectClaims( context, options, propertySelector ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity is missing' ); end; if ( not propertySelector ) then error( 'propertySelector not specified' ); end; result = WDS.filter( options.entity.claims, propertySelector ); if ( not result or #result == 0 ) then return nil; end if options.limit and options.limit ~= '' and options.limit ~= '-' then local limit = tonumber( options.limit, 10 ); while #result > limit do table.remove( result ); end end return result; end --[[ Функция для получения значения свойства элемента в заданный момент времени. Принимает: контекст, элемент, временные границы, таблица ID свойства Возвращает: таблицу соответствующих значений свойства ]] local function getPropertyInBoundaries( context, entity, boundaries, propertyIds ) local results = {}; if not propertyIds or #propertyIds == 0 then return results; end if entity.claims then for _, propertyId in ipairs( propertyIds ) do local filteredClaims = WDS.filter( entity.claims, propertyId .. '[rank:preferred, rank:normal]' ); if filteredClaims then for _, claim in pairs( filteredClaims ) do if not boundaries or not propertyIds or #propertyIds == 0 then table.insert( results, claim.mainsnak ); else local startBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P580' ); local endBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P582' ); if ( (startBoundaries == nil or ( startBoundaries[2] <= boundaries[1])) and (endBoundaries == nil or ( endBoundaries[1] >= boundaries[2]))) then table.insert( results, claim.mainsnak ); end end end end if #results > 0 then break; end end end return results; end --[[ TODO ]] function p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ) -- only support exact date so far, but need improvment local left = nil; local right = nil; if ( statement.qualifiers and statement.qualifiers[qualifierId] ) then for _, qualifier in pairs( statement.qualifiers[qualifierId] ) do local boundaries = context.parseTimeBoundariesFromSnak( qualifier ); if ( not boundaries ) then return nil; end left = min( left, boundaries[1] ); right = max( right, boundaries[2] ); end end if ( not left or not right ) then return nil; end return { left, right }; end --[[ TODO ]] function p.getTimeBoundariesFromQualifiers( frame, context, statement, qualifierIds ) if not qualifierIds then qualifierIds = { 'P582', 'P580', 'P585' }; end for _, qualifierId in ipairs( qualifierIds ) do local result = p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ); if result then return result; end end return nil; end --[[ Функция для получения метки элемента в заданный момент времени. Принимает: контекст, элемент, временные границы Возвращает: текстовую метку элемента, язык метки ]] function getLabelWithLang( context, options, entity, boundaries, propertyIds ) if not entity then return nil; end local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); -- name from label local label = nil; if ( options.text and options.text ~= '' ) then label = options.text; else label, langCode = entity:getLabelWithLang(); if not langCode then return nil; end if not propertyIds then propertyIds = { 'P1813[language:' .. langCode .. ']', 'P1448[language:' .. langCode .. ']', 'P1705[language:' .. langCode .. ']' }; end -- name from properties local results = getPropertyInBoundaries( context, entity, boundaries, propertyIds ); for _, result in pairs( results ) do if result.datavalue and result.datavalue.value then if result.datavalue.type == 'monolingualtext' and result.datavalue.value.text then label = result.datavalue.value.text; lang = result.datavalue.value.language; break; elseif result.datavalue.type == 'string' then label = result.datavalue.value; break; end end end end return label, langCode; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] local function formatProperty( options ) -- Получение сущности по идентификатору local entity = getEntityFromId( options.entityId ) if not entity then return -- throwError( 'entity-not-found' ) end -- проверка на присутсвие у сущности заявлений (claim) -- подробнее о заявлениях см. d:Викиданные:Глоссарий if (entity.claims == nil) then return '' --TODO error? end -- improve options options.frame = g_frame; options.entity = entity; options.extends = function( self, newOptions ) return copyTo( newOptions, copyTo( self, {} ) ) end if ( options.i18n ) then options.i18n = copyTo( options.i18n, copyTo( i18n, {} ) ); else options.i18n = i18n; end -- create context local context = { entity = options.entity, formatSnak = formatSnak, formatPropertyDefault = formatPropertyDefault, formatStatementDefault = formatStatementDefault } context.cloneOptions = function( options ) local entity = options.entity; options.entity = nil; newOptions = mw.clone( options ); options.entity = entity; newOptions.entity = entity; return newOptions; end; context.formatProperty = function( options ) local func = getUserFunction( options, 'property', context.formatPropertyDefault ); return func( context, options ) end; context.formatStatement = function( options, statement ) return formatStatement( context, options, statement ) end; context.formatSnak = function( options, snak, circumstances ) return formatSnak( context, options, snak, circumstances ) end; context.formatRefs = function( options, statement ) return formatRefs( context, options, statement ) end; context.parseTimeFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then return tonumber(os.time( splitISO8601( tostring( snak.datavalue.value.time ) ) ) ) * 1000; end return nil; end context.parseTimeBoundariesFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time and snak.datavalue.value.precision ) then return parseTimeBoundaries( snak.datavalue.value.time, snak.datavalue.value.precision ); end return nil; end context.getSourcingCircumstances = function( statement ) return getSourcingCircumstances( statement ) end; context.selectClaims = function( options, propertyId ) return selectClaims( context, options, propertyId ) end; return context.formatProperty( options ); end function formatPropertyDefault( context, options ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; local claims; if options.property then -- TODO: Почему тут может не быть property? claims = context.selectClaims( options, options.property ); end if claims == nil then return '' --TODO error? end -- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных -- заявлений в таблице local formattedClaims = {} for i, claim in ipairs(claims) do local formattedStatement = context.formatStatement( options, claim ) -- здесь может вернуться либо оформленный текст заявления -- либо строка ошибки nil похоже никогда не возвращается if (formattedStatement) then formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( options.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>' table.insert( formattedClaims, formattedStatement ) end end -- создание текстовой строки со списком оформленых заявлений из таблицы local out = mw.text.listToText( formattedClaims, options.separator, options.conjunction ) if out ~= '' then if options.before then out = options.before .. out end if options.after then out = out .. options.after end end return out end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение и таблицу параметров Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatement( context, options, statement ) if ( not statement ) then error( 'statement is not specified or nil' ); end if not statement.type or statement.type ~= 'statement' then throwError( 'unknown-claim-type' ) end local functionToCall = getUserFunction( options, 'claim', context.formatStatementDefault ); return functionToCall( context, options, statement ); end function getSourcingCircumstances( statement ) if (not statement) then error('statement is not specified') end; local circumstances = {}; if ( statement.qualifiers and statement.qualifiers.P1480 ) then for i, qualifier in pairs( statement.qualifiers.P1480 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'wikibase-entityid' and qualifier.datavalue.value and qualifier.datavalue.value['entity-type'] == 'item' ) then local circumstance = qualifier.datavalue.value.id; if ( 'Q5727902' == circumstance ) then circumstances.circa = true; end if ( 'Q18122778' == circumstance ) then circumstances.presumably = true; end end end end return circumstances; end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение, таблицу параметров, объект-функцию оформления внутренних структур утверждения (snak) и объект-функцию оформления ссылки на источники (reference) Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatementDefault( context, options, statement ) if (not context) then error('context is not specified') end; if (not options) then error('options is not specified') end; if (not statement) then error('statement is not specified') end; local circumstances = context.getSourcingCircumstances( statement ); options.qualifiers = statement.qualifiers; if ( options.references ) then return context.formatSnak( options, statement.mainsnak, circumstances ) .. context.formatRefs( options, statement ); else return context.formatSnak( options, statement.mainsnak, circumstances ); end end --[[ Функция для оформления части утверждения (snak) Подробнее о snak см. d:Викиданные:Глоссарий Принимает: таблицу snak объекта (main snak или же snak от квалификатора) и таблицу опций Возвращает: строку оформленного викитекста ]] function formatSnak( context, options, snak, circumstances ) circumstances = circumstances or {}; local hash = ''; local mainSnakClass = ''; if ( snak.hash ) then hash = ' data-wikidata-hash="' .. snak.hash .. '"'; else mainSnakClass = ' wikidata-main-snak'; end local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>' local after = '</span>' if snak.snaktype == 'somevalue' then if ( options['somevalue'] and options['somevalue'] ~= '' ) then return before .. options['somevalue'] .. after; end return before .. options.i18n['somevalue'] .. after; elseif snak.snaktype == 'novalue' then if ( options['novalue'] and options['novalue'] ~= '' ) then return before .. options['novalue'] .. after; end return before .. options.i18n['novalue'] .. after; elseif snak.snaktype == 'value' then if ( circumstances.presumably ) then before = before .. options.i18n.presumably; end if ( circumstances.circa ) then before = before .. options.i18n.circa; end return before .. formatDatavalue( context, options, snak.datavalue, snak.datatype ) .. after; else throwError( 'unknown-snak-type' ); end end --[[ Функция для оформления объектов-значений с географическими координатами Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatGlobeCoordinate( value, options ) -- проверка на требование в параметрах вызова на возврат сырого значения if options['subvalue'] == 'latitude' then -- широты return value['latitude'] elseif options['subvalue'] == 'longitude' then -- долготы return value['longitude'] elseif options['nocoord'] and options['nocoord'] ~= '' then -- если передан параметр nocoord, то не выводить координаты -- обычно это делается при использовании нескольких карточек на странице return '' else -- в противном случае формируются параметры для вызова шаблона {{coord}} -- нужно дописать в документации шаблона, что он отсюда вызывается, и что -- любое изменние его парамеров должно быть согласовано с кодом тут local eps = 0.0000001 -- < 1/360000 local globe = options.globe or '' -- TODO local lat = {} lat['abs'] = math.abs(value['latitude']) lat['ns'] = value['latitude'] >= 0 and 'N' or 'S' lat['d'] = math.floor(lat['abs'] + eps) lat['m'] = math.floor((lat['abs'] - lat['d']) * 60 + eps) lat['s'] = math.max(0, ((lat['abs'] - lat['d']) * 60 - lat['m']) * 60 + eps) local lon = {} lon['abs'] = math.abs(value['longitude']) lon['ew'] = value['longitude'] >= 0 and 'E' or 'W' lon['d'] = math.floor(lon['abs'] + eps) lon['m'] = math.floor((lon['abs'] - lon['d']) * 60 + eps) lon['s'] = math.max(0, ((lon['abs'] - lon['d']) * 60 - lon['m']) * 60 + eps) -- TODO: round seconds with precision local coord = '{{coord' if (value['precision'] == nil) or (value['precision'] < 1/60) then -- по умолчанию с точностью до секунды coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['s'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['s'] .. '|' .. lon['ew'] elseif value['precision'] < 1 then coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['ew'] else coord = coord .. '|' .. lat['d'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['ew'] end coord = coord .. '|globe:' .. globe if options['type'] and options['type'] ~= '' then coord = coord .. '|type=' .. options.type end if options['display'] and options['display'] ~= '' then coord = coord .. '|display=' .. options.display else coord = coord .. '|display=title' end coord = coord .. '}}' return g_frame:preprocess(coord) end end --[[ Функция для оформления объектов-значений с файлами с Викисклада Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatCommonsMedia( value, options ) local image = value local caption = '' if options['caption'] and options['caption'] ~= '' then caption = options['caption'] elseif options['description'] and options['description'] ~= '' then caption = options['description'] end if caption ~= '' then caption = '<span data-wikidata-qualifier-id="P2096" style="display:block">' .. caption .. '</span>' end if not string.find( value, '[%[%]%{%}]' ) then image = '[[File:' .. value if options['border'] and options['border'] ~= '' then image = image .. '|border' end local size = options['size'] if size and size ~= '' then if not string.match( size, 'px$' ) and not string.match( size, 'пкс$' ) -- TODO: использовать перевод для языка вики then size = size .. 'px' end else size = fileDefaultSize; end image = image .. '|' .. size if options['alt'] and options['alt'] ~= '' then image = image .. '|' .. options['alt'] end image = image .. ']]' if caption ~= '' then image = image .. '<br>' .. caption end else image = image .. caption end return image end --[[ Функция для оформления внешних идентификаторов Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatExternalId( value, options ) local formatter = options.formatter if not formatter or formatter == '' then local wbStatus, entity = pcall( mw.wikibase.getEntity, options.property:upper() ) if wbStatus == true and entity then local statements = entity:getBestStatements( 'P1630' ) for _, statement in pairs( statements ) do if statement.mainsnak.snaktype == 'value' then formatter = statement.mainsnak.datavalue.value break end end end end if formatter and formatter ~= '' then local link = mw.ustring.gsub( formatter, '$1', value ) local title = options.title if not title or title == '' then title = '$1' end title = mw.ustring.gsub( title, '$1', value ) return '[' .. link .. ' ' .. title .. ']' end return value end --[[ Функция для оформления числовых значений Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatQuantity( value, options ) -- диапазон значений local amount = string.gsub( value['amount'], '^%+', '' ); local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); local function formatNum( number ) -- округление до 13 знаков после запятой, на 14-м возникает ошибка в точности local mult = 10^13 number = math.floor( number * mult + 0.5 ) / mult return lang:formatNum( number ) end local out = formatNum( tonumber( amount ) ); if value.upperBound then local diff = tonumber( value.upperBound ) - tonumber( amount ) if diff > 0 then -- временная провека, пока у большинства значений не будет убрано ±0 out = out .. '±' .. formatNum( diff ) end end if options.unit and options.unit ~= '' then if options.unit ~= '-' then out = out .. ' ' .. options.unit end elseif value.unit and string.match( value.unit, 'http://www.wikidata.org/entity/' ) then local unitEntityId = string.gsub( value.unit, 'http://www.wikidata.org/entity/', '' ); local wbStatus, unitEntity = pcall( mw.wikibase.getEntity, unitEntityId ); if wbStatus == true and unitEntity then local writingSystemElementId = 'Q8209'; local langElementId = 'Q7737'; local label = getLabelWithLang( context, options, unitEntity, nil, { 'P5061[language:' .. langCode .. ']', 'P5061[language:mul]', 'P558[P282:' .. writingSystemElementId .. ', P407:' .. langElementId .. ']', 'P558[!P282][!P407]' } ); out = out .. ' ' .. label; end end return out; end --[[ Get property datatype by ID. @param string Property ID, e.g. 'P123'. @return string Property datatype, e.g. 'commonsMedia', 'time' or 'url'. ]] local function getPropertyDatatype( propertyId ) if not propertyId or not string.match( propertyId, '^P%d+$' ) then return nil; end local wbStatus, propertyEntity = pcall( mw.wikibase.getEntity, propertyId ); if wbStatus ~= true or not propertyEntity then return nil; end return propertyEntity.datatype; end local function getDefaultValueFunction( datavalue, datatype ) -- вызов обработчиков по умолчанию для известных типов значений if datavalue.type == 'wikibase-entityid' then -- Entity ID return function( context, options, value ) return formatEntityId( context, options, getEntityIdFromValue( value ) ) end; elseif datavalue.type == 'string' then -- String if datatype and datatype == 'commonsMedia' then -- Media return function( context, options, value ) if ( not options.caption or options.caption == '' ) and ( not options.description or options.description == '' ) and options.qualifiers and options.qualifiers.P2096 then for i, qualifier in pairs( options.qualifiers.P2096 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'monolingualtext' and qualifier.datavalue.value and qualifier.datavalue.value.language == contentLanguageCode ) then options.caption = qualifier.datavalue.value.text options.description = qualifier.datavalue.value.text break end end end return formatCommonsMedia( value, options ) end; elseif datatype and datatype == 'external-id' then -- External ID return function( context, options, value ) return formatExternalId( value, options ) end elseif datatype and datatype == 'url' then -- URL return function( context, options, value ) local moduleUrl = require( 'Module:URL' ) if not options.length or options.length == '' then options.length = 25 end return moduleUrl.formatUrlSingle( context, options, value ); end end return function( context, options, value ) return value end; elseif datavalue.type == 'monolingualtext' then -- моноязычный текст (строка с указанием языка) return function( context, options, value ) if ( options.monolingualLangTemplate == 'lang' ) then return options.frame:expandTemplate{ title = 'lang-' .. value.language, args = { value.text } }; elseif ( options.monolingualLangTemplate == 'ref' ) then return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>' .. options.frame:expandTemplate{ title = 'ref-' .. value.language }; else return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>'; end end; elseif datavalue.type == 'globecoordinate' then -- географические координаты return function( context, options, value ) return formatGlobeCoordinate( value, options ) end; elseif datavalue.type == 'quantity' then return function( context, options, value ) return formatQuantity( value, options ) end; elseif datavalue.type == 'time' then return function( context, options, value ) local moduleDate = require( 'Module:Wikidata/date' ) return moduleDate.formatDate( context, options, value ); end; else -- во всех стальных случаях возвращаем ошибку throwError( 'unknown-datavalue-type' ) end end --[[ Функция для оформления значений (value) Подробнее о значениях см. d:Wikidata:Glossary/ru Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatDatavalue( context, options, datavalue, datatype ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not datavalue ) then error( 'datavalue not specified' ); end; if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end; -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове context.formatValueDefault = getDefaultValueFunction( datavalue, datatype ); local functionToCall = getUserFunction( options, 'value', context.formatValueDefault ); return functionToCall( context, options, datavalue.value ); end --[[ Функция для оформления идентификатора сущности Принимает: строку индентификатора (типа Q42) и таблицу параметров, Возвращает: строку оформленного текста ]] function formatEntityId( context, options, entityId ) -- получение локализованного названия local wbStatus, entity = pcall( mw.wikibase.getEntity, entityId ) if wbStatus ~= true then return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="color:#b32424; border-bottom: 1px dotted #b32424; cursor: help; white-space: nowrap" title="Ошибка получения элемента из Викиданных.">×</span>' .. categoryLinksToEntitiesWithWikibaseError; end local boundaries = nil if options.qualifiers then boundaries = p.getTimeBoundariesFromQualifiers( frame, context, { qualifiers = options.qualifiers } ) end local label, labelLanguageCode = getLabelWithLang( context, options, entity, boundaries ) -- определение соответствующей показываемому элементу категории local category = '' if ( options.category ) then local claims = WDS.filter( entity.claims, options.category ); if ( claims ) then for _, claim in pairs( claims ) do if ( claim.mainsnak and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.type == 'wikibase-entityid' ) then local catEntityId = claim.mainsnak.datavalue.value.id; local wbStatus, catEntity = pcall( mw.wikibase.getEntity, catEntityId ); if ( wbStatus == true and catEntity and catEntity:getSitelink() ) then category = '[[' .. catEntity:getSitelink() .. ']]'; end end end end end -- получение ссылки по идентификатору local link = mw.wikibase.sitelink( entityId ) if link then -- ссылка на категорию, а не добавление страницы в неё if mw.ustring.match( link, '^' .. mw.site.namespaces[ 14 ].name .. ':' ) then link = ':' .. link end if label then if ( contentLanguageCode ~= labelLanguageCode ) then return '[[' .. link .. '|' .. label .. ']]' .. categoryLinksToEntitiesWithMissingLocalLanguageLabel .. category; else return '[[' .. link .. '|' .. label .. ']]' .. category; end else return '[[' .. link .. ']]' .. category; end end if label then -- красная ссылка -- TODO: разобраться, почему не всегда есть options.frame local title = mw.title.new( label ); if title and not title.exists and options.frame then return '[[' .. label .. ']]<sup>[[:d:' .. entityId .. '|[d]]]</sup>' .. category; end -- TODO: перенести до проверки на существование статьи local sup = ''; if ( not options.format or options.format ~= 'text' ) and entityId ~= 'Q6581072' and entityId ~= 'Q6581097' -- TODO: переписать на format=text then sup = '<sup class="plainlinks noprint">[//www.wikidata.org/wiki/' .. entityId .. '?uselang=' .. contentLanguageCode .. ' [d&#x5d;]</sup>' end -- одноимённая статья уже существует - выводится текст и ссылка на ВД return '<span class="iw" data-title="' .. label .. '">' .. label .. sup .. '</span>' .. category end -- сообщение об отсутвии локализованного названия -- not good, but better than nothing return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="В Викиданных нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>' .. categoryLinksToEntitiesWithMissingLabel .. category; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] -- устаревшее имя, не использовать function p.formatStatements( frame ) return p.formatProperty( frame ); end --[[ Получение параметров, которые обычно используются для вывода свойства. ]] function getPropertyParams( propertyId, datatype, params ) local config = require( 'Module:Wikidata/config' ); if not config then return {}; end -- Различные уровни настройки параметров, по убыванию приоритета local propertyParams = {}; -- 1. Параметры, указанные явно при вызове if params then local tplParams = mw.clone( params ); for key, value in pairs( tplParams ) do if value ~= '' then propertyParams[key] = value; end end end -- 2. Настройки конкретного параметра if config['properties'] and config['properties'][propertyId] then local selfParams = mw.clone( config['properties'][propertyId] ); for key, value in pairs( selfParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 3. Указанный пресет настроек if propertyParams['preset'] and config['presets'] and config['presets'][propertyParams['preset']] then local presetParams = mw.clone( config['presets'][propertyParams['preset']] ); for key, value in pairs( presetParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 4. Настройки для типа данных if datatype and config['datatypes'] and config['datatypes'][datatype] then local datatypeParams = mw.clone( config['datatypes'][datatype] ); for key, value in pairs( datatypeParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 5. Общие настройки для всех свойств if config['global'] then local globalParams = mw.clone( config['global'] ); for key, value in pairs( globalParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end return propertyParams; end function p.formatProperty( frame ) local args = frame.args -- проверка на отсутствие обязательного параметра property if not args.property then throwError( 'property-param-not-provided' ) end local propertyId = mw.language.getContentLanguage():ucfirst( string.gsub( args.property, '%[.*$', '' ) ) local datatype = getPropertyDatatype( propertyId ); args = getPropertyParams( propertyId, datatype, args ); -- проброс всех параметров из шаблона {wikidata} local p_frame = frame:getParent(); if p_frame and p_frame:getTitle() == mw.site.namespaces[10].name .. ':Wikidata' then copyTo( p_frame.args, args ); end args.plain = toBoolean( args.plain, false ); args.nocat = toBoolean( args.nocat, false ); args.references = toBoolean( args.references, true ); -- если значение передано в параметрах вызова то выводим только его if args.value and args.value ~= '' then -- специальное значение для скрытия Викиданных if args.value == '-' then return '' end local value = args.value -- опция, запрещающая оформление значения, поэтому никак не трогаем if args.plain then return value end -- обработчики по типу значения local wrapperExtraArgs = '' if args['value-module'] and args['value-function'] and not string.find( value, '[%[%]%{%}]' ) then local func = getUserFunction( args, 'value' ); value = func( {}, args, value ); elseif datatype == 'commonsMedia' then value = formatCommonsMedia( value, args ); elseif datatype == 'external-id' and not string.find( value, '[%[%]%{%}]' ) then wrapperExtraArgs = wrapperExtraArgs .. ' data-wikidata-external-id="' .. mw.text.encode( value ).. '"'; value = formatExternalId( value, args ); elseif datatype == 'url' then local moduleUrl = require( 'Module:URL' ); value = moduleUrl.formatUrlSingle( nil, args, value ); end -- оборачиваем в тег для JS-функций if string.match( propertyId, '^P%d+$' ) then value = mw.text.trim( value ) -- временная штрафная категория для исправления табличных вставок if ( propertyId ~= 'P166' and string.match( value, '<t[dr][ >]' ) and not string.match( value, '<table >]' ) and not string.match( value, '^%{%|' ) ) then value = value .. '[[Category:Википедия:Статьи с табличной вставкой в карточке]]' else -- значений с блочными тегами остаются блоком, текст встраиваем в строку if ( string.match( value, '\n' ) or string.match( value, '<t[dhr][ >]' ) or string.match( value, '<div[ >]' ) ) then value = '<div class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">\n' .. value .. '</div>' else value = '<span class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">' .. value .. '</span>' end end end -- добавляем категорию-маркер if not args.nocat then local pageTitle = mw.title.getCurrentTitle(); if pageTitle.namespace == 0 then value = value .. categoryLocalValuePresent; end end return value end if ( args.plain ) then -- вызова стандартного обработчика без оформления, если передана опция plain return frame:callParserFunction( '#property', propertyId ); end g_frame = frame -- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений) return formatProperty( args ) end --[[ Функция оформления ссылок на источники (reference) Подробнее о ссылках на источники см. d:Wikidata:Glossary/ru Экспортируется в качестве зарезервированной точки для вызова из функций-расширения вида claim-module/claim-function через context Вызов из других модулей напрямую осуществляться не должен (используйте frame:expandTemplate вместе с одним из специлизированных шаблонов вывода значения свойства). Принимает: объект-таблицу утверждение Возвращает: строку оформленных ссылок для отображения в статье ]] function formatRefs( context, options, statement ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; if ( not statement ) then error( 'statement not specified' ); end; if ( not outputReferences ) then return ''; end local result = ''; if ( statement.references ) then local allReferences = statement.references; -- local hasPreferred = false; -- for _, reference in pairs( statement.references ) do -- if ( reference.snaks -- and reference.snaks.P248 -- and reference.snaks.P248[1] -- and reference.snaks.P248[1].datavalue -- and reference.snaks.P248[1].datavalue.value.id ) then -- local entityId = reference.snaks.P248[1].datavalue.value.id; -- if ( preferredSources[entityId] ) then -- hasPreferred = true; -- end -- end -- end for _, reference in pairs( statement.references ) do local display = false; -- if ( hasPreferred ) then if ( reference.snaks and reference.snaks.P248 and reference.snaks.P248[1] and reference.snaks.P248[1].datavalue and reference.snaks.P248[1].datavalue.value.id ) then local entityId = reference.snaks.P248[1].datavalue.value.id; if (not deprecatedSources[entityId] ) then display = true; end end -- end if ( display ) then result = result .. moduleSources.renderReference( g_frame, options.entity, reference ); end end end return result end return p aiah0th7us0h010nkwxylbxy2yqdtzh 49876 49874 2026-05-09T18:45:02Z Olksolo 356 особые типы значений 49876 Scribunto text/plain local i18n = { ["errors"] = { ["property-param-not-provided"] = "Не дан параметр свойства", ["entity-not-found"] = "Сущность не найдена.", ["unknown-claim-type"] = "Неизвестный тип заявления.", ["unknown-snak-type"] = "Неизвестный тип снэка.", ["unknown-datavalue-type"] = "Неизвестный тип значения данных.", ["unknown-entity-type"] = "Неизвестный тип сущности.", ["unknown-property-module"] = "Вы должны установить и property-module, и property-function.", ["unknown-claim-module"] = "Вы должны установить и claim-module, и claim-function.", ["unknown-value-module"] = "Вы должны установить и value-module, и value-function.", ["property-module-not-found"] = "Модуль для отображения свойства не найден", ["property-function-not-found"] = "Функция для отображения свойства не найдена", ["claim-module-not-found"] = "Модуль для отображения утверждения не найден.", ["claim-function-not-found"] = "Функция для отображения утверждения не найдена.", ["value-module-not-found"] = "Модуль для отображения значения не найден.", ["value-function-not-found"] = "Функция для отображения значения не найдена." }, ["somevalue"] = "''tiedämätöi''", ["novalue"] = "", ["circa"] = 'läs&nbsp;', ["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="huaveillen">h.&nbsp;</span>', } -- settings, may differ from project to project local categoryLinksToEntitiesWithMissingLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без подписи]]'; local categoryLinksToEntitiesWithWikibaseError = '[[Category:Википедия:Страницы с ошибками скриптов, использующих Викиданные]]'; local categoryLinksToEntitiesWithMissingLocalLanguageLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без русской подписи]]'; local categoryLocalValuePresent = '[[Category:Википедия:Статьи с переопределением значения из Викиданных]]'; local fileDefaultSize = '267x400px'; local outputReferences = true; -- sources that shall be omitted if any preffered sources exists local deprecatedSources = { Q36578 = true, -- Gemeinsame Normdatei Q63056 = true, -- Find a Grave Q15222191 = true, -- BNF }; local preferredSources = { Q5375741 = true, -- Encyclopædia Britannica Online Q17378135 = true, -- Great Soviet Encyclopedia (1969—1978) }; -- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании) local moduleSources = require( 'Module:Sources' ) local WDS = require( 'Module:WikidataSelectors' ); -- Константы local contentLanguageCode = mw.getContentLanguage():getCode(); local p = {} local formatDatavalue, formatEntityId, formatRefs, formatSnak, formatStatement, formatStatementDefault, formatProperty, getSourcingCircumstances, getPropertyDatatype, getPropertyParams, throwError, toBoolean; local function copyTo( obj, target ) for k, v in pairs( obj ) do target[k] = v end return target; end local function min( prev, next ) if ( prev == nil ) then return next; elseif ( prev > next ) then return next; else return prev; end end local function max( prev, next ) if ( prev == nil ) then return next; elseif ( prev < next ) then return next; else return prev; end end local function splitISO8601(str) if 'table' == type(str) then if str.args and str.args[1] then str = '' .. str.args[1] else return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str ) end end local Y, M, D = (function(str) local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" local Y, M, D = mw.ustring.match( str, pattern ) return tonumber(Y), tonumber(M), tonumber(D) end) (str); local h, m, s = (function(str) local pattern = "T(%d+):(%d+):(%d+)%Z"; local H, M, S = mw.ustring.match( str, pattern); return tonumber(H), tonumber(M), tonumber(S); end) (str); local oh,om = ( function(str) if str:sub(-1)=="Z" then return 0,0 end; -- ends with Z, Zulu time -- matches ±hh:mm, ±hhmm or ±hh; else returns nils local pattern = "([-+])(%d%d):?(%d?%d?)$"; local sign, oh, om = mw.ustring.match( str, pattern); sign, oh, om = sign or "+", oh or "00", om or "00"; return tonumber(sign .. oh), tonumber(sign .. om); end )(str) return {year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}; end local function parseTimeBoundaries( time, precision ) local s = splitISO8601( time ); if (not s) then return nil; end if ( precision >= 0 and precision <= 8 ) then local powers = { 1000000000 , 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 } local power = powers[ precision + 1 ]; local left = s.year - ( s.year % power ); return { tonumber(os.time( {year=left, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=left + power - 1, month=12, day=31, hour=29, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 9 ) then return { tonumber(os.time( {year=s.year, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=12, day=31, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 10 ) then local lastDays = {31, 28.25, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local lastDay = lastDays[s.month]; return { tonumber(os.time( {year=s.year, month=s.month, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=lastDay, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 11 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 12 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=59, sec=58} )) * 1000 + 19991999 }; end if ( precision == 13 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=58} )) * 1000 + 1999 }; end if ( precision == 14 ) then local t = tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} ) ); return { t * 1000, t * 1000 + 999 }; end error('Unsupported precision: ' .. precision ); end --[[ Преобразует строку в булевое значение Принимает: строковое значение (может отсутствовать) Возвращает: булевое значение true или false, если получается распознать значение, или defaultValue во всех остальных случаях ]] local function toBoolean( valueToParse, defaultValue ) if ( valueToParse ~= nil ) then if valueToParse == false or valueToParse == '' or valueToParse == 'false' or valueToParse == '0' then return false end return true end return defaultValue; end --[[ Функция для получения сущности (еntity) для текущей страницы Подробнее о сущностях см. d:Wikidata:Glossary/ru Принимает: строковый индентификатор (типа P18, Q42) Возвращает: объект таблицу, элементы которой индексируются с нуля ]] local function getEntityFromId( id ) local entity; local wbStatus; if id then wbStatus, entity = pcall( mw.wikibase.getEntityObject, id ) end wbStatus, entity = pcall( mw.wikibase.getEntityObject ); return entity; end --[[ Внутрення функция для формирования сообщения об ошибке Принимает: ключ элемента в таблице i18n (например entity-not-found) Возвращает: строку сообщения ]] local function throwError( key ) error( i18n.errors[key] ); end --[[ Функция для получения идентификатора сущностей Принимает: объект таблицу сущности Возвращает: строковый индентификатор (типа P18, Q42) ]] local function getEntityIdFromValue( value ) local prefix = '' if value['entity-type'] == 'item' then prefix = 'Q' elseif value['entity-type'] == 'property' then prefix = 'P' else throwError( 'unknown-entity-type' ) end return prefix .. value['numeric-id'] end -- проверка на наличие специилизированной функции в опциях local function getUserFunction( options, prefix, defaultFunction ) -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове if options[ prefix .. '-module' ] or options[ prefix .. '-function' ] then -- проверка на пустые строки в параметрах или их отсутствие if not options[ prefix .. '-module' ] or not options[ prefix .. '-function' ] then throwError( 'unknown-' .. prefix .. '-module' ); end -- динамическая загруза модуля с обработчиком указанным в параметре local formatter = require ('Module:' .. options[ prefix .. '-module' ]); if formatter == nil then throwError( prefix .. '-module-not-found' ) end local fun = formatter[ options[ prefix .. '-function' ] ] if fun == nil then throwError( prefix .. '-function-not-found' ) end return fun; end return defaultFunction; end -- Выбирает свойства по property id, дополнительно фильтруя их по рангу local function selectClaims( context, options, propertySelector ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity is missing' ); end; if ( not propertySelector ) then error( 'propertySelector not specified' ); end; result = WDS.filter( options.entity.claims, propertySelector ); if ( not result or #result == 0 ) then return nil; end if options.limit and options.limit ~= '' and options.limit ~= '-' then local limit = tonumber( options.limit, 10 ); while #result > limit do table.remove( result ); end end return result; end --[[ Функция для получения значения свойства элемента в заданный момент времени. Принимает: контекст, элемент, временные границы, таблица ID свойства Возвращает: таблицу соответствующих значений свойства ]] local function getPropertyInBoundaries( context, entity, boundaries, propertyIds ) local results = {}; if not propertyIds or #propertyIds == 0 then return results; end if entity.claims then for _, propertyId in ipairs( propertyIds ) do local filteredClaims = WDS.filter( entity.claims, propertyId .. '[rank:preferred, rank:normal]' ); if filteredClaims then for _, claim in pairs( filteredClaims ) do if not boundaries or not propertyIds or #propertyIds == 0 then table.insert( results, claim.mainsnak ); else local startBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P580' ); local endBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P582' ); if ( (startBoundaries == nil or ( startBoundaries[2] <= boundaries[1])) and (endBoundaries == nil or ( endBoundaries[1] >= boundaries[2]))) then table.insert( results, claim.mainsnak ); end end end end if #results > 0 then break; end end end return results; end --[[ TODO ]] function p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ) -- only support exact date so far, but need improvment local left = nil; local right = nil; if ( statement.qualifiers and statement.qualifiers[qualifierId] ) then for _, qualifier in pairs( statement.qualifiers[qualifierId] ) do local boundaries = context.parseTimeBoundariesFromSnak( qualifier ); if ( not boundaries ) then return nil; end left = min( left, boundaries[1] ); right = max( right, boundaries[2] ); end end if ( not left or not right ) then return nil; end return { left, right }; end --[[ TODO ]] function p.getTimeBoundariesFromQualifiers( frame, context, statement, qualifierIds ) if not qualifierIds then qualifierIds = { 'P582', 'P580', 'P585' }; end for _, qualifierId in ipairs( qualifierIds ) do local result = p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ); if result then return result; end end return nil; end --[[ Функция для получения метки элемента в заданный момент времени. Принимает: контекст, элемент, временные границы Возвращает: текстовую метку элемента, язык метки ]] function getLabelWithLang( context, options, entity, boundaries, propertyIds ) if not entity then return nil; end local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); -- name from label local label = nil; if ( options.text and options.text ~= '' ) then label = options.text; else label, langCode = entity:getLabelWithLang(); if not langCode then return nil; end if not propertyIds then propertyIds = { 'P1813[language:' .. langCode .. ']', 'P1448[language:' .. langCode .. ']', 'P1705[language:' .. langCode .. ']' }; end -- name from properties local results = getPropertyInBoundaries( context, entity, boundaries, propertyIds ); for _, result in pairs( results ) do if result.datavalue and result.datavalue.value then if result.datavalue.type == 'monolingualtext' and result.datavalue.value.text then label = result.datavalue.value.text; lang = result.datavalue.value.language; break; elseif result.datavalue.type == 'string' then label = result.datavalue.value; break; end end end end return label, langCode; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] local function formatProperty( options ) -- Получение сущности по идентификатору local entity = getEntityFromId( options.entityId ) if not entity then return -- throwError( 'entity-not-found' ) end -- проверка на присутсвие у сущности заявлений (claim) -- подробнее о заявлениях см. d:Викиданные:Глоссарий if (entity.claims == nil) then return '' --TODO error? end -- improve options options.frame = g_frame; options.entity = entity; options.extends = function( self, newOptions ) return copyTo( newOptions, copyTo( self, {} ) ) end if ( options.i18n ) then options.i18n = copyTo( options.i18n, copyTo( i18n, {} ) ); else options.i18n = i18n; end -- create context local context = { entity = options.entity, formatSnak = formatSnak, formatPropertyDefault = formatPropertyDefault, formatStatementDefault = formatStatementDefault } context.cloneOptions = function( options ) local entity = options.entity; options.entity = nil; newOptions = mw.clone( options ); options.entity = entity; newOptions.entity = entity; return newOptions; end; context.formatProperty = function( options ) local func = getUserFunction( options, 'property', context.formatPropertyDefault ); return func( context, options ) end; context.formatStatement = function( options, statement ) return formatStatement( context, options, statement ) end; context.formatSnak = function( options, snak, circumstances ) return formatSnak( context, options, snak, circumstances ) end; context.formatRefs = function( options, statement ) return formatRefs( context, options, statement ) end; context.parseTimeFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then return tonumber(os.time( splitISO8601( tostring( snak.datavalue.value.time ) ) ) ) * 1000; end return nil; end context.parseTimeBoundariesFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time and snak.datavalue.value.precision ) then return parseTimeBoundaries( snak.datavalue.value.time, snak.datavalue.value.precision ); end return nil; end context.getSourcingCircumstances = function( statement ) return getSourcingCircumstances( statement ) end; context.selectClaims = function( options, propertyId ) return selectClaims( context, options, propertyId ) end; return context.formatProperty( options ); end function formatPropertyDefault( context, options ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; local claims; if options.property then -- TODO: Почему тут может не быть property? claims = context.selectClaims( options, options.property ); end if claims == nil then return '' --TODO error? end -- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных -- заявлений в таблице local formattedClaims = {} for i, claim in ipairs(claims) do local formattedStatement = context.formatStatement( options, claim ) -- здесь может вернуться либо оформленный текст заявления -- либо строка ошибки nil похоже никогда не возвращается if (formattedStatement) then formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( options.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>' table.insert( formattedClaims, formattedStatement ) end end -- создание текстовой строки со списком оформленых заявлений из таблицы local out = mw.text.listToText( formattedClaims, options.separator, options.conjunction ) if out ~= '' then if options.before then out = options.before .. out end if options.after then out = out .. options.after end end return out end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение и таблицу параметров Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatement( context, options, statement ) if ( not statement ) then error( 'statement is not specified or nil' ); end if not statement.type or statement.type ~= 'statement' then throwError( 'unknown-claim-type' ) end local functionToCall = getUserFunction( options, 'claim', context.formatStatementDefault ); return functionToCall( context, options, statement ); end function getSourcingCircumstances( statement ) if (not statement) then error('statement is not specified') end; local circumstances = {}; if ( statement.qualifiers and statement.qualifiers.P1480 ) then for i, qualifier in pairs( statement.qualifiers.P1480 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'wikibase-entityid' and qualifier.datavalue.value and qualifier.datavalue.value['entity-type'] == 'item' ) then local circumstance = qualifier.datavalue.value.id; if ( 'Q5727902' == circumstance ) then circumstances.circa = true; end if ( 'Q18122778' == circumstance ) then circumstances.presumably = true; end end end end return circumstances; end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение, таблицу параметров, объект-функцию оформления внутренних структур утверждения (snak) и объект-функцию оформления ссылки на источники (reference) Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatementDefault( context, options, statement ) if (not context) then error('context is not specified') end; if (not options) then error('options is not specified') end; if (not statement) then error('statement is not specified') end; local circumstances = context.getSourcingCircumstances( statement ); options.qualifiers = statement.qualifiers; if ( options.references ) then return context.formatSnak( options, statement.mainsnak, circumstances ) .. context.formatRefs( options, statement ); else return context.formatSnak( options, statement.mainsnak, circumstances ); end end --[[ Функция для оформления части утверждения (snak) Подробнее о snak см. d:Викиданные:Глоссарий Принимает: таблицу snak объекта (main snak или же snak от квалификатора) и таблицу опций Возвращает: строку оформленного викитекста ]] function formatSnak( context, options, snak, circumstances ) circumstances = circumstances or {}; local hash = ''; local mainSnakClass = ''; if ( snak.hash ) then hash = ' data-wikidata-hash="' .. snak.hash .. '"'; else mainSnakClass = ' wikidata-main-snak'; end local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>' local after = '</span>' if snak.snaktype == 'somevalue' then if ( options['somevalue'] and options['somevalue'] ~= '' ) then return before .. options['somevalue'] .. after; end return before .. options.i18n['somevalue'] .. after; elseif snak.snaktype == 'novalue' then if ( options['novalue'] and options['novalue'] ~= '' ) then return before .. options['novalue'] .. after; end return before .. options.i18n['novalue'] .. after; elseif snak.snaktype == 'value' then if ( circumstances.presumably ) then before = before .. options.i18n.presumably; end if ( circumstances.circa ) then before = before .. options.i18n.circa; end return before .. formatDatavalue( context, options, snak.datavalue, snak.datatype ) .. after; else throwError( 'unknown-snak-type' ); end end --[[ Функция для оформления объектов-значений с географическими координатами Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatGlobeCoordinate( value, options ) -- проверка на требование в параметрах вызова на возврат сырого значения if options['subvalue'] == 'latitude' then -- широты return value['latitude'] elseif options['subvalue'] == 'longitude' then -- долготы return value['longitude'] elseif options['nocoord'] and options['nocoord'] ~= '' then -- если передан параметр nocoord, то не выводить координаты -- обычно это делается при использовании нескольких карточек на странице return '' else -- в противном случае формируются параметры для вызова шаблона {{coord}} -- нужно дописать в документации шаблона, что он отсюда вызывается, и что -- любое изменние его парамеров должно быть согласовано с кодом тут local eps = 0.0000001 -- < 1/360000 local globe = options.globe or '' -- TODO local lat = {} lat['abs'] = math.abs(value['latitude']) lat['ns'] = value['latitude'] >= 0 and 'N' or 'S' lat['d'] = math.floor(lat['abs'] + eps) lat['m'] = math.floor((lat['abs'] - lat['d']) * 60 + eps) lat['s'] = math.max(0, ((lat['abs'] - lat['d']) * 60 - lat['m']) * 60 + eps) local lon = {} lon['abs'] = math.abs(value['longitude']) lon['ew'] = value['longitude'] >= 0 and 'E' or 'W' lon['d'] = math.floor(lon['abs'] + eps) lon['m'] = math.floor((lon['abs'] - lon['d']) * 60 + eps) lon['s'] = math.max(0, ((lon['abs'] - lon['d']) * 60 - lon['m']) * 60 + eps) -- TODO: round seconds with precision local coord = '{{coord' if (value['precision'] == nil) or (value['precision'] < 1/60) then -- по умолчанию с точностью до секунды coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['s'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['s'] .. '|' .. lon['ew'] elseif value['precision'] < 1 then coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['ew'] else coord = coord .. '|' .. lat['d'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['ew'] end coord = coord .. '|globe:' .. globe if options['type'] and options['type'] ~= '' then coord = coord .. '|type=' .. options.type end if options['display'] and options['display'] ~= '' then coord = coord .. '|display=' .. options.display else coord = coord .. '|display=title' end coord = coord .. '}}' return g_frame:preprocess(coord) end end --[[ Функция для оформления объектов-значений с файлами с Викисклада Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatCommonsMedia( value, options ) local image = value local caption = '' if options['caption'] and options['caption'] ~= '' then caption = options['caption'] elseif options['description'] and options['description'] ~= '' then caption = options['description'] end if caption ~= '' then caption = '<span data-wikidata-qualifier-id="P2096" style="display:block">' .. caption .. '</span>' end if not string.find( value, '[%[%]%{%}]' ) then image = '[[File:' .. value if options['border'] and options['border'] ~= '' then image = image .. '|border' end local size = options['size'] if size and size ~= '' then if not string.match( size, 'px$' ) and not string.match( size, 'пкс$' ) -- TODO: использовать перевод для языка вики then size = size .. 'px' end else size = fileDefaultSize; end image = image .. '|' .. size if options['alt'] and options['alt'] ~= '' then image = image .. '|' .. options['alt'] end image = image .. ']]' if caption ~= '' then image = image .. '<br>' .. caption end else image = image .. caption end return image end --[[ Функция для оформления внешних идентификаторов Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatExternalId( value, options ) local formatter = options.formatter if not formatter or formatter == '' then local wbStatus, entity = pcall( mw.wikibase.getEntity, options.property:upper() ) if wbStatus == true and entity then local statements = entity:getBestStatements( 'P1630' ) for _, statement in pairs( statements ) do if statement.mainsnak.snaktype == 'value' then formatter = statement.mainsnak.datavalue.value break end end end end if formatter and formatter ~= '' then local link = mw.ustring.gsub( formatter, '$1', value ) local title = options.title if not title or title == '' then title = '$1' end title = mw.ustring.gsub( title, '$1', value ) return '[' .. link .. ' ' .. title .. ']' end return value end --[[ Функция для оформления числовых значений Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatQuantity( value, options ) -- диапазон значений local amount = string.gsub( value['amount'], '^%+', '' ); local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); local function formatNum( number ) -- округление до 13 знаков после запятой, на 14-м возникает ошибка в точности local mult = 10^13 number = math.floor( number * mult + 0.5 ) / mult return lang:formatNum( number ) end local out = formatNum( tonumber( amount ) ); if value.upperBound then local diff = tonumber( value.upperBound ) - tonumber( amount ) if diff > 0 then -- временная провека, пока у большинства значений не будет убрано ±0 out = out .. '±' .. formatNum( diff ) end end if options.unit and options.unit ~= '' then if options.unit ~= '-' then out = out .. ' ' .. options.unit end elseif value.unit and string.match( value.unit, 'http://www.wikidata.org/entity/' ) then local unitEntityId = string.gsub( value.unit, 'http://www.wikidata.org/entity/', '' ); local wbStatus, unitEntity = pcall( mw.wikibase.getEntity, unitEntityId ); if wbStatus == true and unitEntity then local writingSystemElementId = 'Q8209'; local langElementId = 'Q7737'; local label = getLabelWithLang( context, options, unitEntity, nil, { 'P5061[language:' .. langCode .. ']', 'P5061[language:mul]', 'P558[P282:' .. writingSystemElementId .. ', P407:' .. langElementId .. ']', 'P558[!P282][!P407]' } ); out = out .. ' ' .. label; end end return out; end --[[ Get property datatype by ID. @param string Property ID, e.g. 'P123'. @return string Property datatype, e.g. 'commonsMedia', 'time' or 'url'. ]] local function getPropertyDatatype( propertyId ) if not propertyId or not string.match( propertyId, '^P%d+$' ) then return nil; end local wbStatus, propertyEntity = pcall( mw.wikibase.getEntity, propertyId ); if wbStatus ~= true or not propertyEntity then return nil; end return propertyEntity.datatype; end local function getDefaultValueFunction( datavalue, datatype ) -- вызов обработчиков по умолчанию для известных типов значений if datavalue.type == 'wikibase-entityid' then -- Entity ID return function( context, options, value ) return formatEntityId( context, options, getEntityIdFromValue( value ) ) end; elseif datavalue.type == 'string' then -- String if datatype and datatype == 'commonsMedia' then -- Media return function( context, options, value ) if ( not options.caption or options.caption == '' ) and ( not options.description or options.description == '' ) and options.qualifiers and options.qualifiers.P2096 then for i, qualifier in pairs( options.qualifiers.P2096 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'monolingualtext' and qualifier.datavalue.value and qualifier.datavalue.value.language == contentLanguageCode ) then options.caption = qualifier.datavalue.value.text options.description = qualifier.datavalue.value.text break end end end return formatCommonsMedia( value, options ) end; elseif datatype and datatype == 'external-id' then -- External ID return function( context, options, value ) return formatExternalId( value, options ) end elseif datatype and datatype == 'url' then -- URL return function( context, options, value ) local moduleUrl = require( 'Module:URL' ) if not options.length or options.length == '' then options.length = 25 end return moduleUrl.formatUrlSingle( context, options, value ); end end return function( context, options, value ) return value end; elseif datavalue.type == 'monolingualtext' then -- моноязычный текст (строка с указанием языка) return function( context, options, value ) if ( options.monolingualLangTemplate == 'lang' ) then return options.frame:expandTemplate{ title = 'lang-' .. value.language, args = { value.text } }; elseif ( options.monolingualLangTemplate == 'ref' ) then return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>' .. options.frame:expandTemplate{ title = 'ref-' .. value.language }; else return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>'; end end; elseif datavalue.type == 'globecoordinate' then -- географические координаты return function( context, options, value ) return formatGlobeCoordinate( value, options ) end; elseif datavalue.type == 'quantity' then return function( context, options, value ) return formatQuantity( value, options ) end; elseif datavalue.type == 'time' then return function( context, options, value ) local moduleDate = require( 'Module:Wikidata/date' ) return moduleDate.formatDate( context, options, value ); end; else -- во всех стальных случаях возвращаем ошибку throwError( 'unknown-datavalue-type' ) end end --[[ Функция для оформления значений (value) Подробнее о значениях см. d:Wikidata:Glossary/ru Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatDatavalue( context, options, datavalue, datatype ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not datavalue ) then error( 'datavalue not specified' ); end; if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end; -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове context.formatValueDefault = getDefaultValueFunction( datavalue, datatype ); local functionToCall = getUserFunction( options, 'value', context.formatValueDefault ); return functionToCall( context, options, datavalue.value ); end --[[ Функция для оформления идентификатора сущности Принимает: строку индентификатора (типа Q42) и таблицу параметров, Возвращает: строку оформленного текста ]] function formatEntityId( context, options, entityId ) -- получение локализованного названия local wbStatus, entity = pcall( mw.wikibase.getEntity, entityId ) if wbStatus ~= true then return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="color:#b32424; border-bottom: 1px dotted #b32424; cursor: help; white-space: nowrap" title="Ошибка получения элемента из Викиданных.">×</span>' .. categoryLinksToEntitiesWithWikibaseError; end local boundaries = nil if options.qualifiers then boundaries = p.getTimeBoundariesFromQualifiers( frame, context, { qualifiers = options.qualifiers } ) end local label, labelLanguageCode = getLabelWithLang( context, options, entity, boundaries ) -- определение соответствующей показываемому элементу категории local category = '' if ( options.category ) then local claims = WDS.filter( entity.claims, options.category ); if ( claims ) then for _, claim in pairs( claims ) do if ( claim.mainsnak and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.type == 'wikibase-entityid' ) then local catEntityId = claim.mainsnak.datavalue.value.id; local wbStatus, catEntity = pcall( mw.wikibase.getEntity, catEntityId ); if ( wbStatus == true and catEntity and catEntity:getSitelink() ) then category = '[[' .. catEntity:getSitelink() .. ']]'; end end end end end -- получение ссылки по идентификатору local link = mw.wikibase.sitelink( entityId ) if link then -- ссылка на категорию, а не добавление страницы в неё if mw.ustring.match( link, '^' .. mw.site.namespaces[ 14 ].name .. ':' ) then link = ':' .. link end if label then if ( contentLanguageCode ~= labelLanguageCode ) then return '[[' .. link .. '|' .. label .. ']]' .. categoryLinksToEntitiesWithMissingLocalLanguageLabel .. category; else return '[[' .. link .. '|' .. label .. ']]' .. category; end else return '[[' .. link .. ']]' .. category; end end if label then -- красная ссылка -- TODO: разобраться, почему не всегда есть options.frame local title = mw.title.new( label ); if title and not title.exists and options.frame then return '[[' .. label .. ']]<sup>[[:d:' .. entityId .. '|[d]]]</sup>' .. category; end -- TODO: перенести до проверки на существование статьи local sup = ''; if ( not options.format or options.format ~= 'text' ) and entityId ~= 'Q6581072' and entityId ~= 'Q6581097' -- TODO: переписать на format=text then sup = '<sup class="plainlinks noprint">[//www.wikidata.org/wiki/' .. entityId .. '?uselang=' .. contentLanguageCode .. ' [d&#x5d;]</sup>' end -- одноимённая статья уже существует - выводится текст и ссылка на ВД return '<span class="iw" data-title="' .. label .. '">' .. label .. sup .. '</span>' .. category end -- сообщение об отсутвии локализованного названия -- not good, but better than nothing return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="В Викиданных нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>' .. categoryLinksToEntitiesWithMissingLabel .. category; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] -- устаревшее имя, не использовать function p.formatStatements( frame ) return p.formatProperty( frame ); end --[[ Получение параметров, которые обычно используются для вывода свойства. ]] function getPropertyParams( propertyId, datatype, params ) local config = require( 'Module:Wikidata/config' ); if not config then return {}; end -- Различные уровни настройки параметров, по убыванию приоритета local propertyParams = {}; -- 1. Параметры, указанные явно при вызове if params then local tplParams = mw.clone( params ); for key, value in pairs( tplParams ) do if value ~= '' then propertyParams[key] = value; end end end -- 2. Настройки конкретного параметра if config['properties'] and config['properties'][propertyId] then local selfParams = mw.clone( config['properties'][propertyId] ); for key, value in pairs( selfParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 3. Указанный пресет настроек if propertyParams['preset'] and config['presets'] and config['presets'][propertyParams['preset']] then local presetParams = mw.clone( config['presets'][propertyParams['preset']] ); for key, value in pairs( presetParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 4. Настройки для типа данных if datatype and config['datatypes'] and config['datatypes'][datatype] then local datatypeParams = mw.clone( config['datatypes'][datatype] ); for key, value in pairs( datatypeParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 5. Общие настройки для всех свойств if config['global'] then local globalParams = mw.clone( config['global'] ); for key, value in pairs( globalParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end return propertyParams; end function p.formatProperty( frame ) local args = frame.args -- проверка на отсутствие обязательного параметра property if not args.property then throwError( 'property-param-not-provided' ) end local propertyId = mw.language.getContentLanguage():ucfirst( string.gsub( args.property, '%[.*$', '' ) ) local datatype = getPropertyDatatype( propertyId ); args = getPropertyParams( propertyId, datatype, args ); -- проброс всех параметров из шаблона {wikidata} local p_frame = frame:getParent(); if p_frame and p_frame:getTitle() == mw.site.namespaces[10].name .. ':Wikidata' then copyTo( p_frame.args, args ); end args.plain = toBoolean( args.plain, false ); args.nocat = toBoolean( args.nocat, false ); args.references = toBoolean( args.references, true ); -- если значение передано в параметрах вызова то выводим только его if args.value and args.value ~= '' then -- специальное значение для скрытия Викиданных if args.value == '-' then return '' end local value = args.value -- опция, запрещающая оформление значения, поэтому никак не трогаем if args.plain then return value end -- обработчики по типу значения local wrapperExtraArgs = '' if args['value-module'] and args['value-function'] and not string.find( value, '[%[%]%{%}]' ) then local func = getUserFunction( args, 'value' ); value = func( {}, args, value ); elseif datatype == 'commonsMedia' then value = formatCommonsMedia( value, args ); elseif datatype == 'external-id' and not string.find( value, '[%[%]%{%}]' ) then wrapperExtraArgs = wrapperExtraArgs .. ' data-wikidata-external-id="' .. mw.text.encode( value ).. '"'; value = formatExternalId( value, args ); elseif datatype == 'url' then local moduleUrl = require( 'Module:URL' ); value = moduleUrl.formatUrlSingle( nil, args, value ); end -- оборачиваем в тег для JS-функций if string.match( propertyId, '^P%d+$' ) then value = mw.text.trim( value ) -- временная штрафная категория для исправления табличных вставок if ( propertyId ~= 'P166' and string.match( value, '<t[dr][ >]' ) and not string.match( value, '<table >]' ) and not string.match( value, '^%{%|' ) ) then value = value .. '[[Category:Википедия:Статьи с табличной вставкой в карточке]]' else -- значений с блочными тегами остаются блоком, текст встраиваем в строку if ( string.match( value, '\n' ) or string.match( value, '<t[dhr][ >]' ) or string.match( value, '<div[ >]' ) ) then value = '<div class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">\n' .. value .. '</div>' else value = '<span class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">' .. value .. '</span>' end end end -- добавляем категорию-маркер if not args.nocat then local pageTitle = mw.title.getCurrentTitle(); if pageTitle.namespace == 0 then value = value .. categoryLocalValuePresent; end end return value end if ( args.plain ) then -- вызова стандартного обработчика без оформления, если передана опция plain return frame:callParserFunction( '#property', propertyId ); end g_frame = frame -- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений) return formatProperty( args ) end --[[ Функция оформления ссылок на источники (reference) Подробнее о ссылках на источники см. d:Wikidata:Glossary/ru Экспортируется в качестве зарезервированной точки для вызова из функций-расширения вида claim-module/claim-function через context Вызов из других модулей напрямую осуществляться не должен (используйте frame:expandTemplate вместе с одним из специлизированных шаблонов вывода значения свойства). Принимает: объект-таблицу утверждение Возвращает: строку оформленных ссылок для отображения в статье ]] function formatRefs( context, options, statement ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; if ( not statement ) then error( 'statement not specified' ); end; if ( not outputReferences ) then return ''; end local result = ''; if ( statement.references ) then local allReferences = statement.references; -- local hasPreferred = false; -- for _, reference in pairs( statement.references ) do -- if ( reference.snaks -- and reference.snaks.P248 -- and reference.snaks.P248[1] -- and reference.snaks.P248[1].datavalue -- and reference.snaks.P248[1].datavalue.value.id ) then -- local entityId = reference.snaks.P248[1].datavalue.value.id; -- if ( preferredSources[entityId] ) then -- hasPreferred = true; -- end -- end -- end for _, reference in pairs( statement.references ) do local display = false; -- if ( hasPreferred ) then if ( reference.snaks and reference.snaks.P248 and reference.snaks.P248[1] and reference.snaks.P248[1].datavalue and reference.snaks.P248[1].datavalue.value.id ) then local entityId = reference.snaks.P248[1].datavalue.value.id; if (not deprecatedSources[entityId] ) then display = true; end end -- end if ( display ) then result = result .. moduleSources.renderReference( g_frame, options.entity, reference ); end end end return result end return p c9vw0rtcubx94zwj6asv390mu5a76d8 49884 49876 2026-05-09T20:50:20Z Olksolo 356 изменена функция formatQuantity для поддержки более компактной экспоненциальной записи физических величин 49884 Scribunto text/plain local i18n = { ["errors"] = { ["property-param-not-provided"] = "Не дан параметр свойства", ["entity-not-found"] = "Сущность не найдена.", ["unknown-claim-type"] = "Неизвестный тип заявления.", ["unknown-snak-type"] = "Неизвестный тип снэка.", ["unknown-datavalue-type"] = "Неизвестный тип значения данных.", ["unknown-entity-type"] = "Неизвестный тип сущности.", ["unknown-property-module"] = "Вы должны установить и property-module, и property-function.", ["unknown-claim-module"] = "Вы должны установить и claim-module, и claim-function.", ["unknown-value-module"] = "Вы должны установить и value-module, и value-function.", ["property-module-not-found"] = "Модуль для отображения свойства не найден", ["property-function-not-found"] = "Функция для отображения свойства не найдена", ["claim-module-not-found"] = "Модуль для отображения утверждения не найден.", ["claim-function-not-found"] = "Функция для отображения утверждения не найдена.", ["value-module-not-found"] = "Модуль для отображения значения не найден.", ["value-function-not-found"] = "Функция для отображения значения не найдена." }, ["somevalue"] = "''tiedämätöi''", ["novalue"] = "", ["circa"] = 'läs&nbsp;', ["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="huaveillen">h.&nbsp;</span>', } -- settings, may differ from project to project local categoryLinksToEntitiesWithMissingLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без подписи]]'; local categoryLinksToEntitiesWithWikibaseError = '[[Category:Википедия:Страницы с ошибками скриптов, использующих Викиданные]]'; local categoryLinksToEntitiesWithMissingLocalLanguageLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без русской подписи]]'; local categoryLocalValuePresent = '[[Category:Википедия:Статьи с переопределением значения из Викиданных]]'; local fileDefaultSize = '267x400px'; local outputReferences = true; -- sources that shall be omitted if any preffered sources exists local deprecatedSources = { Q36578 = true, -- Gemeinsame Normdatei Q63056 = true, -- Find a Grave Q15222191 = true, -- BNF }; local preferredSources = { Q5375741 = true, -- Encyclopædia Britannica Online Q17378135 = true, -- Great Soviet Encyclopedia (1969—1978) }; -- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании) local moduleSources = require( 'Module:Sources' ) local WDS = require( 'Module:WikidataSelectors' ); -- Константы local contentLanguageCode = mw.getContentLanguage():getCode(); local p = {} local formatDatavalue, formatEntityId, formatRefs, formatSnak, formatStatement, formatStatementDefault, formatProperty, getSourcingCircumstances, getPropertyDatatype, getPropertyParams, throwError, toBoolean; local function copyTo( obj, target ) for k, v in pairs( obj ) do target[k] = v end return target; end local function min( prev, next ) if ( prev == nil ) then return next; elseif ( prev > next ) then return next; else return prev; end end local function max( prev, next ) if ( prev == nil ) then return next; elseif ( prev < next ) then return next; else return prev; end end local function splitISO8601(str) if 'table' == type(str) then if str.args and str.args[1] then str = '' .. str.args[1] else return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str ) end end local Y, M, D = (function(str) local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" local Y, M, D = mw.ustring.match( str, pattern ) return tonumber(Y), tonumber(M), tonumber(D) end) (str); local h, m, s = (function(str) local pattern = "T(%d+):(%d+):(%d+)%Z"; local H, M, S = mw.ustring.match( str, pattern); return tonumber(H), tonumber(M), tonumber(S); end) (str); local oh,om = ( function(str) if str:sub(-1)=="Z" then return 0,0 end; -- ends with Z, Zulu time -- matches ±hh:mm, ±hhmm or ±hh; else returns nils local pattern = "([-+])(%d%d):?(%d?%d?)$"; local sign, oh, om = mw.ustring.match( str, pattern); sign, oh, om = sign or "+", oh or "00", om or "00"; return tonumber(sign .. oh), tonumber(sign .. om); end )(str) return {year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}; end local function parseTimeBoundaries( time, precision ) local s = splitISO8601( time ); if (not s) then return nil; end if ( precision >= 0 and precision <= 8 ) then local powers = { 1000000000 , 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 } local power = powers[ precision + 1 ]; local left = s.year - ( s.year % power ); return { tonumber(os.time( {year=left, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=left + power - 1, month=12, day=31, hour=29, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 9 ) then return { tonumber(os.time( {year=s.year, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=12, day=31, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 10 ) then local lastDays = {31, 28.25, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local lastDay = lastDays[s.month]; return { tonumber(os.time( {year=s.year, month=s.month, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=lastDay, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 11 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 12 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=59, sec=58} )) * 1000 + 19991999 }; end if ( precision == 13 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=58} )) * 1000 + 1999 }; end if ( precision == 14 ) then local t = tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} ) ); return { t * 1000, t * 1000 + 999 }; end error('Unsupported precision: ' .. precision ); end --[[ Преобразует строку в булевое значение Принимает: строковое значение (может отсутствовать) Возвращает: булевое значение true или false, если получается распознать значение, или defaultValue во всех остальных случаях ]] local function toBoolean( valueToParse, defaultValue ) if ( valueToParse ~= nil ) then if valueToParse == false or valueToParse == '' or valueToParse == 'false' or valueToParse == '0' then return false end return true end return defaultValue; end --[[ Функция для получения сущности (еntity) для текущей страницы Подробнее о сущностях см. d:Wikidata:Glossary/ru Принимает: строковый индентификатор (типа P18, Q42) Возвращает: объект таблицу, элементы которой индексируются с нуля ]] local function getEntityFromId( id ) local entity; local wbStatus; if id then wbStatus, entity = pcall( mw.wikibase.getEntityObject, id ) end wbStatus, entity = pcall( mw.wikibase.getEntityObject ); return entity; end --[[ Внутрення функция для формирования сообщения об ошибке Принимает: ключ элемента в таблице i18n (например entity-not-found) Возвращает: строку сообщения ]] local function throwError( key ) error( i18n.errors[key] ); end --[[ Функция для получения идентификатора сущностей Принимает: объект таблицу сущности Возвращает: строковый индентификатор (типа P18, Q42) ]] local function getEntityIdFromValue( value ) local prefix = '' if value['entity-type'] == 'item' then prefix = 'Q' elseif value['entity-type'] == 'property' then prefix = 'P' else throwError( 'unknown-entity-type' ) end return prefix .. value['numeric-id'] end -- проверка на наличие специилизированной функции в опциях local function getUserFunction( options, prefix, defaultFunction ) -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове if options[ prefix .. '-module' ] or options[ prefix .. '-function' ] then -- проверка на пустые строки в параметрах или их отсутствие if not options[ prefix .. '-module' ] or not options[ prefix .. '-function' ] then throwError( 'unknown-' .. prefix .. '-module' ); end -- динамическая загруза модуля с обработчиком указанным в параметре local formatter = require ('Module:' .. options[ prefix .. '-module' ]); if formatter == nil then throwError( prefix .. '-module-not-found' ) end local fun = formatter[ options[ prefix .. '-function' ] ] if fun == nil then throwError( prefix .. '-function-not-found' ) end return fun; end return defaultFunction; end -- Выбирает свойства по property id, дополнительно фильтруя их по рангу local function selectClaims( context, options, propertySelector ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity is missing' ); end; if ( not propertySelector ) then error( 'propertySelector not specified' ); end; result = WDS.filter( options.entity.claims, propertySelector ); if ( not result or #result == 0 ) then return nil; end if options.limit and options.limit ~= '' and options.limit ~= '-' then local limit = tonumber( options.limit, 10 ); while #result > limit do table.remove( result ); end end return result; end --[[ Функция для получения значения свойства элемента в заданный момент времени. Принимает: контекст, элемент, временные границы, таблица ID свойства Возвращает: таблицу соответствующих значений свойства ]] local function getPropertyInBoundaries( context, entity, boundaries, propertyIds ) local results = {}; if not propertyIds or #propertyIds == 0 then return results; end if entity.claims then for _, propertyId in ipairs( propertyIds ) do local filteredClaims = WDS.filter( entity.claims, propertyId .. '[rank:preferred, rank:normal]' ); if filteredClaims then for _, claim in pairs( filteredClaims ) do if not boundaries or not propertyIds or #propertyIds == 0 then table.insert( results, claim.mainsnak ); else local startBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P580' ); local endBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P582' ); if ( (startBoundaries == nil or ( startBoundaries[2] <= boundaries[1])) and (endBoundaries == nil or ( endBoundaries[1] >= boundaries[2]))) then table.insert( results, claim.mainsnak ); end end end end if #results > 0 then break; end end end return results; end --[[ TODO ]] function p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ) -- only support exact date so far, but need improvment local left = nil; local right = nil; if ( statement.qualifiers and statement.qualifiers[qualifierId] ) then for _, qualifier in pairs( statement.qualifiers[qualifierId] ) do local boundaries = context.parseTimeBoundariesFromSnak( qualifier ); if ( not boundaries ) then return nil; end left = min( left, boundaries[1] ); right = max( right, boundaries[2] ); end end if ( not left or not right ) then return nil; end return { left, right }; end --[[ TODO ]] function p.getTimeBoundariesFromQualifiers( frame, context, statement, qualifierIds ) if not qualifierIds then qualifierIds = { 'P582', 'P580', 'P585' }; end for _, qualifierId in ipairs( qualifierIds ) do local result = p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ); if result then return result; end end return nil; end --[[ Функция для получения метки элемента в заданный момент времени. Принимает: контекст, элемент, временные границы Возвращает: текстовую метку элемента, язык метки ]] function getLabelWithLang( context, options, entity, boundaries, propertyIds ) if not entity then return nil; end local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); -- name from label local label = nil; if ( options.text and options.text ~= '' ) then label = options.text; else label, langCode = entity:getLabelWithLang(); if not langCode then return nil; end if not propertyIds then propertyIds = { 'P1813[language:' .. langCode .. ']', 'P1448[language:' .. langCode .. ']', 'P1705[language:' .. langCode .. ']' }; end -- name from properties local results = getPropertyInBoundaries( context, entity, boundaries, propertyIds ); for _, result in pairs( results ) do if result.datavalue and result.datavalue.value then if result.datavalue.type == 'monolingualtext' and result.datavalue.value.text then label = result.datavalue.value.text; lang = result.datavalue.value.language; break; elseif result.datavalue.type == 'string' then label = result.datavalue.value; break; end end end end return label, langCode; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] local function formatProperty( options ) -- Получение сущности по идентификатору local entity = getEntityFromId( options.entityId ) if not entity then return -- throwError( 'entity-not-found' ) end -- проверка на присутсвие у сущности заявлений (claim) -- подробнее о заявлениях см. d:Викиданные:Глоссарий if (entity.claims == nil) then return '' --TODO error? end -- improve options options.frame = g_frame; options.entity = entity; options.extends = function( self, newOptions ) return copyTo( newOptions, copyTo( self, {} ) ) end if ( options.i18n ) then options.i18n = copyTo( options.i18n, copyTo( i18n, {} ) ); else options.i18n = i18n; end -- create context local context = { entity = options.entity, formatSnak = formatSnak, formatPropertyDefault = formatPropertyDefault, formatStatementDefault = formatStatementDefault } context.cloneOptions = function( options ) local entity = options.entity; options.entity = nil; newOptions = mw.clone( options ); options.entity = entity; newOptions.entity = entity; return newOptions; end; context.formatProperty = function( options ) local func = getUserFunction( options, 'property', context.formatPropertyDefault ); return func( context, options ) end; context.formatStatement = function( options, statement ) return formatStatement( context, options, statement ) end; context.formatSnak = function( options, snak, circumstances ) return formatSnak( context, options, snak, circumstances ) end; context.formatRefs = function( options, statement ) return formatRefs( context, options, statement ) end; context.parseTimeFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then return tonumber(os.time( splitISO8601( tostring( snak.datavalue.value.time ) ) ) ) * 1000; end return nil; end context.parseTimeBoundariesFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time and snak.datavalue.value.precision ) then return parseTimeBoundaries( snak.datavalue.value.time, snak.datavalue.value.precision ); end return nil; end context.getSourcingCircumstances = function( statement ) return getSourcingCircumstances( statement ) end; context.selectClaims = function( options, propertyId ) return selectClaims( context, options, propertyId ) end; return context.formatProperty( options ); end function formatPropertyDefault( context, options ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; local claims; if options.property then -- TODO: Почему тут может не быть property? claims = context.selectClaims( options, options.property ); end if claims == nil then return '' --TODO error? end -- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных -- заявлений в таблице local formattedClaims = {} for i, claim in ipairs(claims) do local formattedStatement = context.formatStatement( options, claim ) -- здесь может вернуться либо оформленный текст заявления -- либо строка ошибки nil похоже никогда не возвращается if (formattedStatement) then formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( options.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>' table.insert( formattedClaims, formattedStatement ) end end -- создание текстовой строки со списком оформленых заявлений из таблицы local out = mw.text.listToText( formattedClaims, options.separator, options.conjunction ) if out ~= '' then if options.before then out = options.before .. out end if options.after then out = out .. options.after end end return out end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение и таблицу параметров Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatement( context, options, statement ) if ( not statement ) then error( 'statement is not specified or nil' ); end if not statement.type or statement.type ~= 'statement' then throwError( 'unknown-claim-type' ) end local functionToCall = getUserFunction( options, 'claim', context.formatStatementDefault ); return functionToCall( context, options, statement ); end function getSourcingCircumstances( statement ) if (not statement) then error('statement is not specified') end; local circumstances = {}; if ( statement.qualifiers and statement.qualifiers.P1480 ) then for i, qualifier in pairs( statement.qualifiers.P1480 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'wikibase-entityid' and qualifier.datavalue.value and qualifier.datavalue.value['entity-type'] == 'item' ) then local circumstance = qualifier.datavalue.value.id; if ( 'Q5727902' == circumstance ) then circumstances.circa = true; end if ( 'Q18122778' == circumstance ) then circumstances.presumably = true; end end end end return circumstances; end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение, таблицу параметров, объект-функцию оформления внутренних структур утверждения (snak) и объект-функцию оформления ссылки на источники (reference) Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatementDefault( context, options, statement ) if (not context) then error('context is not specified') end; if (not options) then error('options is not specified') end; if (not statement) then error('statement is not specified') end; local circumstances = context.getSourcingCircumstances( statement ); options.qualifiers = statement.qualifiers; if ( options.references ) then return context.formatSnak( options, statement.mainsnak, circumstances ) .. context.formatRefs( options, statement ); else return context.formatSnak( options, statement.mainsnak, circumstances ); end end --[[ Функция для оформления части утверждения (snak) Подробнее о snak см. d:Викиданные:Глоссарий Принимает: таблицу snak объекта (main snak или же snak от квалификатора) и таблицу опций Возвращает: строку оформленного викитекста ]] function formatSnak( context, options, snak, circumstances ) circumstances = circumstances or {}; local hash = ''; local mainSnakClass = ''; if ( snak.hash ) then hash = ' data-wikidata-hash="' .. snak.hash .. '"'; else mainSnakClass = ' wikidata-main-snak'; end local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>' local after = '</span>' if snak.snaktype == 'somevalue' then if ( options['somevalue'] and options['somevalue'] ~= '' ) then return before .. options['somevalue'] .. after; end return before .. options.i18n['somevalue'] .. after; elseif snak.snaktype == 'novalue' then if ( options['novalue'] and options['novalue'] ~= '' ) then return before .. options['novalue'] .. after; end return before .. options.i18n['novalue'] .. after; elseif snak.snaktype == 'value' then if ( circumstances.presumably ) then before = before .. options.i18n.presumably; end if ( circumstances.circa ) then before = before .. options.i18n.circa; end return before .. formatDatavalue( context, options, snak.datavalue, snak.datatype ) .. after; else throwError( 'unknown-snak-type' ); end end --[[ Функция для оформления объектов-значений с географическими координатами Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatGlobeCoordinate( value, options ) -- проверка на требование в параметрах вызова на возврат сырого значения if options['subvalue'] == 'latitude' then -- широты return value['latitude'] elseif options['subvalue'] == 'longitude' then -- долготы return value['longitude'] elseif options['nocoord'] and options['nocoord'] ~= '' then -- если передан параметр nocoord, то не выводить координаты -- обычно это делается при использовании нескольких карточек на странице return '' else -- в противном случае формируются параметры для вызова шаблона {{coord}} -- нужно дописать в документации шаблона, что он отсюда вызывается, и что -- любое изменние его парамеров должно быть согласовано с кодом тут local eps = 0.0000001 -- < 1/360000 local globe = options.globe or '' -- TODO local lat = {} lat['abs'] = math.abs(value['latitude']) lat['ns'] = value['latitude'] >= 0 and 'N' or 'S' lat['d'] = math.floor(lat['abs'] + eps) lat['m'] = math.floor((lat['abs'] - lat['d']) * 60 + eps) lat['s'] = math.max(0, ((lat['abs'] - lat['d']) * 60 - lat['m']) * 60 + eps) local lon = {} lon['abs'] = math.abs(value['longitude']) lon['ew'] = value['longitude'] >= 0 and 'E' or 'W' lon['d'] = math.floor(lon['abs'] + eps) lon['m'] = math.floor((lon['abs'] - lon['d']) * 60 + eps) lon['s'] = math.max(0, ((lon['abs'] - lon['d']) * 60 - lon['m']) * 60 + eps) -- TODO: round seconds with precision local coord = '{{coord' if (value['precision'] == nil) or (value['precision'] < 1/60) then -- по умолчанию с точностью до секунды coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['s'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['s'] .. '|' .. lon['ew'] elseif value['precision'] < 1 then coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['ew'] else coord = coord .. '|' .. lat['d'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['ew'] end coord = coord .. '|globe:' .. globe if options['type'] and options['type'] ~= '' then coord = coord .. '|type=' .. options.type end if options['display'] and options['display'] ~= '' then coord = coord .. '|display=' .. options.display else coord = coord .. '|display=title' end coord = coord .. '}}' return g_frame:preprocess(coord) end end --[[ Функция для оформления объектов-значений с файлами с Викисклада Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatCommonsMedia( value, options ) local image = value local caption = '' if options['caption'] and options['caption'] ~= '' then caption = options['caption'] elseif options['description'] and options['description'] ~= '' then caption = options['description'] end if caption ~= '' then caption = '<span data-wikidata-qualifier-id="P2096" style="display:block">' .. caption .. '</span>' end if not string.find( value, '[%[%]%{%}]' ) then image = '[[File:' .. value if options['border'] and options['border'] ~= '' then image = image .. '|border' end local size = options['size'] if size and size ~= '' then if not string.match( size, 'px$' ) and not string.match( size, 'пкс$' ) -- TODO: использовать перевод для языка вики then size = size .. 'px' end else size = fileDefaultSize; end image = image .. '|' .. size if options['alt'] and options['alt'] ~= '' then image = image .. '|' .. options['alt'] end image = image .. ']]' if caption ~= '' then image = image .. '<br>' .. caption end else image = image .. caption end return image end --[[ Функция для оформления внешних идентификаторов Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatExternalId( value, options ) local formatter = options.formatter if not formatter or formatter == '' then local wbStatus, entity = pcall( mw.wikibase.getEntity, options.property:upper() ) if wbStatus == true and entity then local statements = entity:getBestStatements( 'P1630' ) for _, statement in pairs( statements ) do if statement.mainsnak.snaktype == 'value' then formatter = statement.mainsnak.datavalue.value break end end end end if formatter and formatter ~= '' then local link = mw.ustring.gsub( formatter, '$1', value ) local title = options.title if not title or title == '' then title = '$1' end title = mw.ustring.gsub( title, '$1', value ) return '[' .. link .. ' ' .. title .. ']' end return value end --[[ Функция для оформления числовых значений Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatQuantity( value, options ) -- диапазон значений local amount = string.gsub( value['amount'], '^%+', '' ); local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); -- Пороги для переключения в экспоненциальный формат (настраивается) local EXP_THRESHOLD_HIGH = options.expThreshold or 1e9 -- по умолчанию 10⁹ local EXP_THRESHOLD_LOW = options.expThresholdLow or 1e-4 -- Форматирование числа: десятичное или экспоненциальное local function formatNum( number, exponent ) -- округление до 13 знаков после запятой local mult = 10^13 number = math.floor( number * mult + 0.5 ) / mult if exponent then -- Экспоненциальный формат: мантисса с нужной точностью -- Сохраняем 4-5 знаков после запятой для читаемости local mantissa = lang:formatNum( math.floor( number * 100000 + 0.5 ) / 100000 ) return mantissa .. '×10' .. mw.ustring.char( 0x207B, 0x2070 ) .. exponent -- ¹⁰⁻ⁿ else return lang:formatNum( number ) end end -- Вспомогательная функция: вычисление порядка числа (логарифм по основанию 10) local function getOrder( num ) if num == 0 then return 0 end return math.floor( math.log10( math.abs( num ) ) ) end -- Вспомогательная функция: форматирование экспоненты как верхнего индекса local function formatExponent( exp ) local superscripts = { ['0']='⁰', ['1']='¹', ['2']='²', ['3']='³', ['4']='⁴', ['5']='⁵', ['6']='⁶', ['7']='⁷', ['8']='⁸', ['9']='⁹', ['-']='⁻' } local sign = exp < 0 and '⁻' or '' local absExp = math.abs( exp ) local result = sign for digit in tostring( absExp ):gmatch( '.' ) do result = result .. (superscripts[digit] or digit) end return '×10' .. result end local amountNum = tonumber( amount ) local out -- Определяем, нужна ли экспоненциальная запись local useExponential = false local mainOrder = getOrder( amountNum ) if math.abs( amountNum ) >= EXP_THRESHOLD_HIGH or ( math.abs( amountNum ) > 0 and math.abs( amountNum ) < EXP_THRESHOLD_LOW ) then useExponential = true end -- Если есть погрешность — согласуем формат if value.upperBound then local diff = tonumber( value.upperBound ) - tonumber( amount ) if diff > 0 then if useExponential then -- Находим общий порядок для выравнивания local diffOrder = getOrder( diff ) -- Используем порядок основного числа как базовый local baseOrder = mainOrder -- Форматируем оба числа с одинаковым множителем local amountScaled = amountNum / (10^baseOrder) local diffScaled = diff / (10^baseOrder) -- Округляем погрешность до 1-2 значащих цифр для читаемости local diffRounded = tonumber( string.format( '%.2e', diffScaled ) ) -- Форматируем мантиссы local amountMantissa = lang:formatNum( math.floor( amountScaled * 100000 + 0.5 ) / 100000 ) local diffMantissa = lang:formatNum( math.floor( diffRounded * 10000 + 0.5 ) / 10000 ) out = amountMantissa .. '±' .. diffMantissa .. formatExponent( baseOrder ) else out = formatNum( amountNum ) .. '±' .. formatNum( diff ) end else out = formatNum( amountNum ) end else if useExponential then out = formatNum( amountNum, mainOrder ) else out = formatNum( amountNum ) end end -- Добавление единицы измерения if options.unit and options.unit ~= '' then if options.unit ~= '-' then out = out .. ' ' .. options.unit end elseif value.unit and string.match( value.unit, 'http://www.wikidata.org/entity/' ) then local unitEntityId = string.gsub( value.unit, 'http://www.wikidata.org/entity/', '' ); local wbStatus, unitEntity = pcall( mw.wikibase.getEntity, unitEntityId ); if wbStatus == true and unitEntity then local writingSystemElementId = 'Q8209'; local langElementId = 'Q7737'; local label = getLabelWithLang( context, options, unitEntity, nil, { 'P5061[language:' .. langCode .. ']', 'P5061[language:mul]', 'P558[P282:' .. writingSystemElementId .. ', P407:' .. langElementId .. ']', 'P558[!P282][!P407]' } ); out = out .. ' ' .. label; end end return out; end --[[ Get property datatype by ID. @param string Property ID, e.g. 'P123'. @return string Property datatype, e.g. 'commonsMedia', 'time' or 'url'. ]] local function getPropertyDatatype( propertyId ) if not propertyId or not string.match( propertyId, '^P%d+$' ) then return nil; end local wbStatus, propertyEntity = pcall( mw.wikibase.getEntity, propertyId ); if wbStatus ~= true or not propertyEntity then return nil; end return propertyEntity.datatype; end local function getDefaultValueFunction( datavalue, datatype ) -- вызов обработчиков по умолчанию для известных типов значений if datavalue.type == 'wikibase-entityid' then -- Entity ID return function( context, options, value ) return formatEntityId( context, options, getEntityIdFromValue( value ) ) end; elseif datavalue.type == 'string' then -- String if datatype and datatype == 'commonsMedia' then -- Media return function( context, options, value ) if ( not options.caption or options.caption == '' ) and ( not options.description or options.description == '' ) and options.qualifiers and options.qualifiers.P2096 then for i, qualifier in pairs( options.qualifiers.P2096 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'monolingualtext' and qualifier.datavalue.value and qualifier.datavalue.value.language == contentLanguageCode ) then options.caption = qualifier.datavalue.value.text options.description = qualifier.datavalue.value.text break end end end return formatCommonsMedia( value, options ) end; elseif datatype and datatype == 'external-id' then -- External ID return function( context, options, value ) return formatExternalId( value, options ) end elseif datatype and datatype == 'url' then -- URL return function( context, options, value ) local moduleUrl = require( 'Module:URL' ) if not options.length or options.length == '' then options.length = 25 end return moduleUrl.formatUrlSingle( context, options, value ); end end return function( context, options, value ) return value end; elseif datavalue.type == 'monolingualtext' then -- моноязычный текст (строка с указанием языка) return function( context, options, value ) if ( options.monolingualLangTemplate == 'lang' ) then return options.frame:expandTemplate{ title = 'lang-' .. value.language, args = { value.text } }; elseif ( options.monolingualLangTemplate == 'ref' ) then return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>' .. options.frame:expandTemplate{ title = 'ref-' .. value.language }; else return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>'; end end; elseif datavalue.type == 'globecoordinate' then -- географические координаты return function( context, options, value ) return formatGlobeCoordinate( value, options ) end; elseif datavalue.type == 'quantity' then return function( context, options, value ) return formatQuantity( value, options ) end; elseif datavalue.type == 'time' then return function( context, options, value ) local moduleDate = require( 'Module:Wikidata/date' ) return moduleDate.formatDate( context, options, value ); end; else -- во всех стальных случаях возвращаем ошибку throwError( 'unknown-datavalue-type' ) end end --[[ Функция для оформления значений (value) Подробнее о значениях см. d:Wikidata:Glossary/ru Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatDatavalue( context, options, datavalue, datatype ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not datavalue ) then error( 'datavalue not specified' ); end; if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end; -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове context.formatValueDefault = getDefaultValueFunction( datavalue, datatype ); local functionToCall = getUserFunction( options, 'value', context.formatValueDefault ); return functionToCall( context, options, datavalue.value ); end --[[ Функция для оформления идентификатора сущности Принимает: строку индентификатора (типа Q42) и таблицу параметров, Возвращает: строку оформленного текста ]] function formatEntityId( context, options, entityId ) -- получение локализованного названия local wbStatus, entity = pcall( mw.wikibase.getEntity, entityId ) if wbStatus ~= true then return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="color:#b32424; border-bottom: 1px dotted #b32424; cursor: help; white-space: nowrap" title="Ошибка получения элемента из Викиданных.">×</span>' .. categoryLinksToEntitiesWithWikibaseError; end local boundaries = nil if options.qualifiers then boundaries = p.getTimeBoundariesFromQualifiers( frame, context, { qualifiers = options.qualifiers } ) end local label, labelLanguageCode = getLabelWithLang( context, options, entity, boundaries ) -- определение соответствующей показываемому элементу категории local category = '' if ( options.category ) then local claims = WDS.filter( entity.claims, options.category ); if ( claims ) then for _, claim in pairs( claims ) do if ( claim.mainsnak and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.type == 'wikibase-entityid' ) then local catEntityId = claim.mainsnak.datavalue.value.id; local wbStatus, catEntity = pcall( mw.wikibase.getEntity, catEntityId ); if ( wbStatus == true and catEntity and catEntity:getSitelink() ) then category = '[[' .. catEntity:getSitelink() .. ']]'; end end end end end -- получение ссылки по идентификатору local link = mw.wikibase.sitelink( entityId ) if link then -- ссылка на категорию, а не добавление страницы в неё if mw.ustring.match( link, '^' .. mw.site.namespaces[ 14 ].name .. ':' ) then link = ':' .. link end if label then if ( contentLanguageCode ~= labelLanguageCode ) then return '[[' .. link .. '|' .. label .. ']]' .. categoryLinksToEntitiesWithMissingLocalLanguageLabel .. category; else return '[[' .. link .. '|' .. label .. ']]' .. category; end else return '[[' .. link .. ']]' .. category; end end if label then -- красная ссылка -- TODO: разобраться, почему не всегда есть options.frame local title = mw.title.new( label ); if title and not title.exists and options.frame then return '[[' .. label .. ']]<sup>[[:d:' .. entityId .. '|[d]]]</sup>' .. category; end -- TODO: перенести до проверки на существование статьи local sup = ''; if ( not options.format or options.format ~= 'text' ) and entityId ~= 'Q6581072' and entityId ~= 'Q6581097' -- TODO: переписать на format=text then sup = '<sup class="plainlinks noprint">[//www.wikidata.org/wiki/' .. entityId .. '?uselang=' .. contentLanguageCode .. ' [d&#x5d;]</sup>' end -- одноимённая статья уже существует - выводится текст и ссылка на ВД return '<span class="iw" data-title="' .. label .. '">' .. label .. sup .. '</span>' .. category end -- сообщение об отсутвии локализованного названия -- not good, but better than nothing return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="В Викиданных нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>' .. categoryLinksToEntitiesWithMissingLabel .. category; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] -- устаревшее имя, не использовать function p.formatStatements( frame ) return p.formatProperty( frame ); end --[[ Получение параметров, которые обычно используются для вывода свойства. ]] function getPropertyParams( propertyId, datatype, params ) local config = require( 'Module:Wikidata/config' ); if not config then return {}; end -- Различные уровни настройки параметров, по убыванию приоритета local propertyParams = {}; -- 1. Параметры, указанные явно при вызове if params then local tplParams = mw.clone( params ); for key, value in pairs( tplParams ) do if value ~= '' then propertyParams[key] = value; end end end -- 2. Настройки конкретного параметра if config['properties'] and config['properties'][propertyId] then local selfParams = mw.clone( config['properties'][propertyId] ); for key, value in pairs( selfParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 3. Указанный пресет настроек if propertyParams['preset'] and config['presets'] and config['presets'][propertyParams['preset']] then local presetParams = mw.clone( config['presets'][propertyParams['preset']] ); for key, value in pairs( presetParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 4. Настройки для типа данных if datatype and config['datatypes'] and config['datatypes'][datatype] then local datatypeParams = mw.clone( config['datatypes'][datatype] ); for key, value in pairs( datatypeParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 5. Общие настройки для всех свойств if config['global'] then local globalParams = mw.clone( config['global'] ); for key, value in pairs( globalParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end return propertyParams; end function p.formatProperty( frame ) local args = frame.args -- проверка на отсутствие обязательного параметра property if not args.property then throwError( 'property-param-not-provided' ) end local propertyId = mw.language.getContentLanguage():ucfirst( string.gsub( args.property, '%[.*$', '' ) ) local datatype = getPropertyDatatype( propertyId ); args = getPropertyParams( propertyId, datatype, args ); -- проброс всех параметров из шаблона {wikidata} local p_frame = frame:getParent(); if p_frame and p_frame:getTitle() == mw.site.namespaces[10].name .. ':Wikidata' then copyTo( p_frame.args, args ); end args.plain = toBoolean( args.plain, false ); args.nocat = toBoolean( args.nocat, false ); args.references = toBoolean( args.references, true ); -- если значение передано в параметрах вызова то выводим только его if args.value and args.value ~= '' then -- специальное значение для скрытия Викиданных if args.value == '-' then return '' end local value = args.value -- опция, запрещающая оформление значения, поэтому никак не трогаем if args.plain then return value end -- обработчики по типу значения local wrapperExtraArgs = '' if args['value-module'] and args['value-function'] and not string.find( value, '[%[%]%{%}]' ) then local func = getUserFunction( args, 'value' ); value = func( {}, args, value ); elseif datatype == 'commonsMedia' then value = formatCommonsMedia( value, args ); elseif datatype == 'external-id' and not string.find( value, '[%[%]%{%}]' ) then wrapperExtraArgs = wrapperExtraArgs .. ' data-wikidata-external-id="' .. mw.text.encode( value ).. '"'; value = formatExternalId( value, args ); elseif datatype == 'url' then local moduleUrl = require( 'Module:URL' ); value = moduleUrl.formatUrlSingle( nil, args, value ); end -- оборачиваем в тег для JS-функций if string.match( propertyId, '^P%d+$' ) then value = mw.text.trim( value ) -- временная штрафная категория для исправления табличных вставок if ( propertyId ~= 'P166' and string.match( value, '<t[dr][ >]' ) and not string.match( value, '<table >]' ) and not string.match( value, '^%{%|' ) ) then value = value .. '[[Category:Википедия:Статьи с табличной вставкой в карточке]]' else -- значений с блочными тегами остаются блоком, текст встраиваем в строку if ( string.match( value, '\n' ) or string.match( value, '<t[dhr][ >]' ) or string.match( value, '<div[ >]' ) ) then value = '<div class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">\n' .. value .. '</div>' else value = '<span class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">' .. value .. '</span>' end end end -- добавляем категорию-маркер if not args.nocat then local pageTitle = mw.title.getCurrentTitle(); if pageTitle.namespace == 0 then value = value .. categoryLocalValuePresent; end end return value end if ( args.plain ) then -- вызова стандартного обработчика без оформления, если передана опция plain return frame:callParserFunction( '#property', propertyId ); end g_frame = frame -- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений) return formatProperty( args ) end --[[ Функция оформления ссылок на источники (reference) Подробнее о ссылках на источники см. d:Wikidata:Glossary/ru Экспортируется в качестве зарезервированной точки для вызова из функций-расширения вида claim-module/claim-function через context Вызов из других модулей напрямую осуществляться не должен (используйте frame:expandTemplate вместе с одним из специлизированных шаблонов вывода значения свойства). Принимает: объект-таблицу утверждение Возвращает: строку оформленных ссылок для отображения в статье ]] function formatRefs( context, options, statement ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; if ( not statement ) then error( 'statement not specified' ); end; if ( not outputReferences ) then return ''; end local result = ''; if ( statement.references ) then local allReferences = statement.references; -- local hasPreferred = false; -- for _, reference in pairs( statement.references ) do -- if ( reference.snaks -- and reference.snaks.P248 -- and reference.snaks.P248[1] -- and reference.snaks.P248[1].datavalue -- and reference.snaks.P248[1].datavalue.value.id ) then -- local entityId = reference.snaks.P248[1].datavalue.value.id; -- if ( preferredSources[entityId] ) then -- hasPreferred = true; -- end -- end -- end for _, reference in pairs( statement.references ) do local display = false; -- if ( hasPreferred ) then if ( reference.snaks and reference.snaks.P248 and reference.snaks.P248[1] and reference.snaks.P248[1].datavalue and reference.snaks.P248[1].datavalue.value.id ) then local entityId = reference.snaks.P248[1].datavalue.value.id; if (not deprecatedSources[entityId] ) then display = true; end end -- end if ( display ) then result = result .. moduleSources.renderReference( g_frame, options.entity, reference ); end end end return result end return p lcbabvxyzmqefntmj3iwlho0ekj3nnq 49895 49884 2026-05-10T10:46:07Z Olksolo 356 deprecatedSources +1 49895 Scribunto text/plain local i18n = { ["errors"] = { ["property-param-not-provided"] = "Не дан параметр свойства", ["entity-not-found"] = "Сущность не найдена.", ["unknown-claim-type"] = "Неизвестный тип заявления.", ["unknown-snak-type"] = "Неизвестный тип снэка.", ["unknown-datavalue-type"] = "Неизвестный тип значения данных.", ["unknown-entity-type"] = "Неизвестный тип сущности.", ["unknown-property-module"] = "Вы должны установить и property-module, и property-function.", ["unknown-claim-module"] = "Вы должны установить и claim-module, и claim-function.", ["unknown-value-module"] = "Вы должны установить и value-module, и value-function.", ["property-module-not-found"] = "Модуль для отображения свойства не найден", ["property-function-not-found"] = "Функция для отображения свойства не найдена", ["claim-module-not-found"] = "Модуль для отображения утверждения не найден.", ["claim-function-not-found"] = "Функция для отображения утверждения не найдена.", ["value-module-not-found"] = "Модуль для отображения значения не найден.", ["value-function-not-found"] = "Функция для отображения значения не найдена." }, ["somevalue"] = "''tiedämätöi''", ["novalue"] = "", ["circa"] = 'läs&nbsp;', ["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="huaveillen">h.&nbsp;</span>', } -- settings, may differ from project to project local categoryLinksToEntitiesWithMissingLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без подписи]]'; local categoryLinksToEntitiesWithWikibaseError = '[[Category:Википедия:Страницы с ошибками скриптов, использующих Викиданные]]'; local categoryLinksToEntitiesWithMissingLocalLanguageLabel = '[[Category:Википедия:Статьи со ссылками на элементы Викиданных без русской подписи]]'; local categoryLocalValuePresent = '[[Category:Википедия:Статьи с переопределением значения из Викиданных]]'; local fileDefaultSize = '267x400px'; local outputReferences = true; -- sources that shall be omitted if any preffered sources exists local deprecatedSources = { Q36578 = true, -- Gemeinsame Normdatei Q63056 = true, -- Find a Grave Q15222191 = true, -- BNF Q230051 = true, -- Alexa Internet }; local preferredSources = { Q5375741 = true, -- Encyclopædia Britannica Online Q17378135 = true, -- Great Soviet Encyclopedia (1969—1978) }; -- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании) local moduleSources = require( 'Module:Sources' ) local WDS = require( 'Module:WikidataSelectors' ); -- Константы local contentLanguageCode = mw.getContentLanguage():getCode(); local p = {} local formatDatavalue, formatEntityId, formatRefs, formatSnak, formatStatement, formatStatementDefault, formatProperty, getSourcingCircumstances, getPropertyDatatype, getPropertyParams, throwError, toBoolean; local function copyTo( obj, target ) for k, v in pairs( obj ) do target[k] = v end return target; end local function min( prev, next ) if ( prev == nil ) then return next; elseif ( prev > next ) then return next; else return prev; end end local function max( prev, next ) if ( prev == nil ) then return next; elseif ( prev < next ) then return next; else return prev; end end local function splitISO8601(str) if 'table' == type(str) then if str.args and str.args[1] then str = '' .. str.args[1] else return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str ) end end local Y, M, D = (function(str) local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" local Y, M, D = mw.ustring.match( str, pattern ) return tonumber(Y), tonumber(M), tonumber(D) end) (str); local h, m, s = (function(str) local pattern = "T(%d+):(%d+):(%d+)%Z"; local H, M, S = mw.ustring.match( str, pattern); return tonumber(H), tonumber(M), tonumber(S); end) (str); local oh,om = ( function(str) if str:sub(-1)=="Z" then return 0,0 end; -- ends with Z, Zulu time -- matches ±hh:mm, ±hhmm or ±hh; else returns nils local pattern = "([-+])(%d%d):?(%d?%d?)$"; local sign, oh, om = mw.ustring.match( str, pattern); sign, oh, om = sign or "+", oh or "00", om or "00"; return tonumber(sign .. oh), tonumber(sign .. om); end )(str) return {year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}; end local function parseTimeBoundaries( time, precision ) local s = splitISO8601( time ); if (not s) then return nil; end if ( precision >= 0 and precision <= 8 ) then local powers = { 1000000000 , 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 } local power = powers[ precision + 1 ]; local left = s.year - ( s.year % power ); return { tonumber(os.time( {year=left, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=left + power - 1, month=12, day=31, hour=29, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 9 ) then return { tonumber(os.time( {year=s.year, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=12, day=31, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 10 ) then local lastDays = {31, 28.25, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local lastDay = lastDays[s.month]; return { tonumber(os.time( {year=s.year, month=s.month, day=1, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=lastDay, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 11 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=0, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=23, min=59, sec=58} )) * 1000 + 1999 }; end if ( precision == 12 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=0, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=59, sec=58} )) * 1000 + 19991999 }; end if ( precision == 13 ) then return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} )) * 1000, tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=58} )) * 1000 + 1999 }; end if ( precision == 14 ) then local t = tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} ) ); return { t * 1000, t * 1000 + 999 }; end error('Unsupported precision: ' .. precision ); end --[[ Преобразует строку в булевое значение Принимает: строковое значение (может отсутствовать) Возвращает: булевое значение true или false, если получается распознать значение, или defaultValue во всех остальных случаях ]] local function toBoolean( valueToParse, defaultValue ) if ( valueToParse ~= nil ) then if valueToParse == false or valueToParse == '' or valueToParse == 'false' or valueToParse == '0' then return false end return true end return defaultValue; end --[[ Функция для получения сущности (еntity) для текущей страницы Подробнее о сущностях см. d:Wikidata:Glossary/ru Принимает: строковый индентификатор (типа P18, Q42) Возвращает: объект таблицу, элементы которой индексируются с нуля ]] local function getEntityFromId( id ) local entity; local wbStatus; if id then wbStatus, entity = pcall( mw.wikibase.getEntityObject, id ) end wbStatus, entity = pcall( mw.wikibase.getEntityObject ); return entity; end --[[ Внутрення функция для формирования сообщения об ошибке Принимает: ключ элемента в таблице i18n (например entity-not-found) Возвращает: строку сообщения ]] local function throwError( key ) error( i18n.errors[key] ); end --[[ Функция для получения идентификатора сущностей Принимает: объект таблицу сущности Возвращает: строковый индентификатор (типа P18, Q42) ]] local function getEntityIdFromValue( value ) local prefix = '' if value['entity-type'] == 'item' then prefix = 'Q' elseif value['entity-type'] == 'property' then prefix = 'P' else throwError( 'unknown-entity-type' ) end return prefix .. value['numeric-id'] end -- проверка на наличие специилизированной функции в опциях local function getUserFunction( options, prefix, defaultFunction ) -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове if options[ prefix .. '-module' ] or options[ prefix .. '-function' ] then -- проверка на пустые строки в параметрах или их отсутствие if not options[ prefix .. '-module' ] or not options[ prefix .. '-function' ] then throwError( 'unknown-' .. prefix .. '-module' ); end -- динамическая загруза модуля с обработчиком указанным в параметре local formatter = require ('Module:' .. options[ prefix .. '-module' ]); if formatter == nil then throwError( prefix .. '-module-not-found' ) end local fun = formatter[ options[ prefix .. '-function' ] ] if fun == nil then throwError( prefix .. '-function-not-found' ) end return fun; end return defaultFunction; end -- Выбирает свойства по property id, дополнительно фильтруя их по рангу local function selectClaims( context, options, propertySelector ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity is missing' ); end; if ( not propertySelector ) then error( 'propertySelector not specified' ); end; result = WDS.filter( options.entity.claims, propertySelector ); if ( not result or #result == 0 ) then return nil; end if options.limit and options.limit ~= '' and options.limit ~= '-' then local limit = tonumber( options.limit, 10 ); while #result > limit do table.remove( result ); end end return result; end --[[ Функция для получения значения свойства элемента в заданный момент времени. Принимает: контекст, элемент, временные границы, таблица ID свойства Возвращает: таблицу соответствующих значений свойства ]] local function getPropertyInBoundaries( context, entity, boundaries, propertyIds ) local results = {}; if not propertyIds or #propertyIds == 0 then return results; end if entity.claims then for _, propertyId in ipairs( propertyIds ) do local filteredClaims = WDS.filter( entity.claims, propertyId .. '[rank:preferred, rank:normal]' ); if filteredClaims then for _, claim in pairs( filteredClaims ) do if not boundaries or not propertyIds or #propertyIds == 0 then table.insert( results, claim.mainsnak ); else local startBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P580' ); local endBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P582' ); if ( (startBoundaries == nil or ( startBoundaries[2] <= boundaries[1])) and (endBoundaries == nil or ( endBoundaries[1] >= boundaries[2]))) then table.insert( results, claim.mainsnak ); end end end end if #results > 0 then break; end end end return results; end --[[ TODO ]] function p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ) -- only support exact date so far, but need improvment local left = nil; local right = nil; if ( statement.qualifiers and statement.qualifiers[qualifierId] ) then for _, qualifier in pairs( statement.qualifiers[qualifierId] ) do local boundaries = context.parseTimeBoundariesFromSnak( qualifier ); if ( not boundaries ) then return nil; end left = min( left, boundaries[1] ); right = max( right, boundaries[2] ); end end if ( not left or not right ) then return nil; end return { left, right }; end --[[ TODO ]] function p.getTimeBoundariesFromQualifiers( frame, context, statement, qualifierIds ) if not qualifierIds then qualifierIds = { 'P582', 'P580', 'P585' }; end for _, qualifierId in ipairs( qualifierIds ) do local result = p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ); if result then return result; end end return nil; end --[[ Функция для получения метки элемента в заданный момент времени. Принимает: контекст, элемент, временные границы Возвращает: текстовую метку элемента, язык метки ]] function getLabelWithLang( context, options, entity, boundaries, propertyIds ) if not entity then return nil; end local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); -- name from label local label = nil; if ( options.text and options.text ~= '' ) then label = options.text; else label, langCode = entity:getLabelWithLang(); if not langCode then return nil; end if not propertyIds then propertyIds = { 'P1813[language:' .. langCode .. ']', 'P1448[language:' .. langCode .. ']', 'P1705[language:' .. langCode .. ']' }; end -- name from properties local results = getPropertyInBoundaries( context, entity, boundaries, propertyIds ); for _, result in pairs( results ) do if result.datavalue and result.datavalue.value then if result.datavalue.type == 'monolingualtext' and result.datavalue.value.text then label = result.datavalue.value.text; lang = result.datavalue.value.language; break; elseif result.datavalue.type == 'string' then label = result.datavalue.value; break; end end end end return label, langCode; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] local function formatProperty( options ) -- Получение сущности по идентификатору local entity = getEntityFromId( options.entityId ) if not entity then return -- throwError( 'entity-not-found' ) end -- проверка на присутсвие у сущности заявлений (claim) -- подробнее о заявлениях см. d:Викиданные:Глоссарий if (entity.claims == nil) then return '' --TODO error? end -- improve options options.frame = g_frame; options.entity = entity; options.extends = function( self, newOptions ) return copyTo( newOptions, copyTo( self, {} ) ) end if ( options.i18n ) then options.i18n = copyTo( options.i18n, copyTo( i18n, {} ) ); else options.i18n = i18n; end -- create context local context = { entity = options.entity, formatSnak = formatSnak, formatPropertyDefault = formatPropertyDefault, formatStatementDefault = formatStatementDefault } context.cloneOptions = function( options ) local entity = options.entity; options.entity = nil; newOptions = mw.clone( options ); options.entity = entity; newOptions.entity = entity; return newOptions; end; context.formatProperty = function( options ) local func = getUserFunction( options, 'property', context.formatPropertyDefault ); return func( context, options ) end; context.formatStatement = function( options, statement ) return formatStatement( context, options, statement ) end; context.formatSnak = function( options, snak, circumstances ) return formatSnak( context, options, snak, circumstances ) end; context.formatRefs = function( options, statement ) return formatRefs( context, options, statement ) end; context.parseTimeFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then return tonumber(os.time( splitISO8601( tostring( snak.datavalue.value.time ) ) ) ) * 1000; end return nil; end context.parseTimeBoundariesFromSnak = function( snak ) if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time and snak.datavalue.value.precision ) then return parseTimeBoundaries( snak.datavalue.value.time, snak.datavalue.value.precision ); end return nil; end context.getSourcingCircumstances = function( statement ) return getSourcingCircumstances( statement ) end; context.selectClaims = function( options, propertyId ) return selectClaims( context, options, propertyId ) end; return context.formatProperty( options ); end function formatPropertyDefault( context, options ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; local claims; if options.property then -- TODO: Почему тут может не быть property? claims = context.selectClaims( options, options.property ); end if claims == nil then return '' --TODO error? end -- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных -- заявлений в таблице local formattedClaims = {} for i, claim in ipairs(claims) do local formattedStatement = context.formatStatement( options, claim ) -- здесь может вернуться либо оформленный текст заявления -- либо строка ошибки nil похоже никогда не возвращается if (formattedStatement) then formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( options.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>' table.insert( formattedClaims, formattedStatement ) end end -- создание текстовой строки со списком оформленых заявлений из таблицы local out = mw.text.listToText( formattedClaims, options.separator, options.conjunction ) if out ~= '' then if options.before then out = options.before .. out end if options.after then out = out .. options.after end end return out end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение и таблицу параметров Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatement( context, options, statement ) if ( not statement ) then error( 'statement is not specified or nil' ); end if not statement.type or statement.type ~= 'statement' then throwError( 'unknown-claim-type' ) end local functionToCall = getUserFunction( options, 'claim', context.formatStatementDefault ); return functionToCall( context, options, statement ); end function getSourcingCircumstances( statement ) if (not statement) then error('statement is not specified') end; local circumstances = {}; if ( statement.qualifiers and statement.qualifiers.P1480 ) then for i, qualifier in pairs( statement.qualifiers.P1480 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'wikibase-entityid' and qualifier.datavalue.value and qualifier.datavalue.value['entity-type'] == 'item' ) then local circumstance = qualifier.datavalue.value.id; if ( 'Q5727902' == circumstance ) then circumstances.circa = true; end if ( 'Q18122778' == circumstance ) then circumstances.presumably = true; end end end end return circumstances; end --[[ Функция для оформления одного утверждения (statement) Принимает: объект-таблицу утверждение, таблицу параметров, объект-функцию оформления внутренних структур утверждения (snak) и объект-функцию оформления ссылки на источники (reference) Возвращает: строку оформленного текста с заявлением (claim) ]] function formatStatementDefault( context, options, statement ) if (not context) then error('context is not specified') end; if (not options) then error('options is not specified') end; if (not statement) then error('statement is not specified') end; local circumstances = context.getSourcingCircumstances( statement ); options.qualifiers = statement.qualifiers; if ( options.references ) then return context.formatSnak( options, statement.mainsnak, circumstances ) .. context.formatRefs( options, statement ); else return context.formatSnak( options, statement.mainsnak, circumstances ); end end --[[ Функция для оформления части утверждения (snak) Подробнее о snak см. d:Викиданные:Глоссарий Принимает: таблицу snak объекта (main snak или же snak от квалификатора) и таблицу опций Возвращает: строку оформленного викитекста ]] function formatSnak( context, options, snak, circumstances ) circumstances = circumstances or {}; local hash = ''; local mainSnakClass = ''; if ( snak.hash ) then hash = ' data-wikidata-hash="' .. snak.hash .. '"'; else mainSnakClass = ' wikidata-main-snak'; end local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>' local after = '</span>' if snak.snaktype == 'somevalue' then if ( options['somevalue'] and options['somevalue'] ~= '' ) then return before .. options['somevalue'] .. after; end return before .. options.i18n['somevalue'] .. after; elseif snak.snaktype == 'novalue' then if ( options['novalue'] and options['novalue'] ~= '' ) then return before .. options['novalue'] .. after; end return before .. options.i18n['novalue'] .. after; elseif snak.snaktype == 'value' then if ( circumstances.presumably ) then before = before .. options.i18n.presumably; end if ( circumstances.circa ) then before = before .. options.i18n.circa; end return before .. formatDatavalue( context, options, snak.datavalue, snak.datatype ) .. after; else throwError( 'unknown-snak-type' ); end end --[[ Функция для оформления объектов-значений с географическими координатами Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatGlobeCoordinate( value, options ) -- проверка на требование в параметрах вызова на возврат сырого значения if options['subvalue'] == 'latitude' then -- широты return value['latitude'] elseif options['subvalue'] == 'longitude' then -- долготы return value['longitude'] elseif options['nocoord'] and options['nocoord'] ~= '' then -- если передан параметр nocoord, то не выводить координаты -- обычно это делается при использовании нескольких карточек на странице return '' else -- в противном случае формируются параметры для вызова шаблона {{coord}} -- нужно дописать в документации шаблона, что он отсюда вызывается, и что -- любое изменние его парамеров должно быть согласовано с кодом тут local eps = 0.0000001 -- < 1/360000 local globe = options.globe or '' -- TODO local lat = {} lat['abs'] = math.abs(value['latitude']) lat['ns'] = value['latitude'] >= 0 and 'N' or 'S' lat['d'] = math.floor(lat['abs'] + eps) lat['m'] = math.floor((lat['abs'] - lat['d']) * 60 + eps) lat['s'] = math.max(0, ((lat['abs'] - lat['d']) * 60 - lat['m']) * 60 + eps) local lon = {} lon['abs'] = math.abs(value['longitude']) lon['ew'] = value['longitude'] >= 0 and 'E' or 'W' lon['d'] = math.floor(lon['abs'] + eps) lon['m'] = math.floor((lon['abs'] - lon['d']) * 60 + eps) lon['s'] = math.max(0, ((lon['abs'] - lon['d']) * 60 - lon['m']) * 60 + eps) -- TODO: round seconds with precision local coord = '{{coord' if (value['precision'] == nil) or (value['precision'] < 1/60) then -- по умолчанию с точностью до секунды coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['s'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['s'] .. '|' .. lon['ew'] elseif value['precision'] < 1 then coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['ew'] else coord = coord .. '|' .. lat['d'] .. '|' .. lat['ns'] coord = coord .. '|' .. lon['d'] .. '|' .. lon['ew'] end coord = coord .. '|globe:' .. globe if options['type'] and options['type'] ~= '' then coord = coord .. '|type=' .. options.type end if options['display'] and options['display'] ~= '' then coord = coord .. '|display=' .. options.display else coord = coord .. '|display=title' end coord = coord .. '}}' return g_frame:preprocess(coord) end end --[[ Функция для оформления объектов-значений с файлами с Викисклада Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatCommonsMedia( value, options ) local image = value local caption = '' if options['caption'] and options['caption'] ~= '' then caption = options['caption'] elseif options['description'] and options['description'] ~= '' then caption = options['description'] end if caption ~= '' then caption = '<span data-wikidata-qualifier-id="P2096" style="display:block">' .. caption .. '</span>' end if not string.find( value, '[%[%]%{%}]' ) then image = '[[File:' .. value if options['border'] and options['border'] ~= '' then image = image .. '|border' end local size = options['size'] if size and size ~= '' then if not string.match( size, 'px$' ) and not string.match( size, 'пкс$' ) -- TODO: использовать перевод для языка вики then size = size .. 'px' end else size = fileDefaultSize; end image = image .. '|' .. size if options['alt'] and options['alt'] ~= '' then image = image .. '|' .. options['alt'] end image = image .. ']]' if caption ~= '' then image = image .. '<br>' .. caption end else image = image .. caption end return image end --[[ Функция для оформления внешних идентификаторов Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatExternalId( value, options ) local formatter = options.formatter if not formatter or formatter == '' then local wbStatus, entity = pcall( mw.wikibase.getEntity, options.property:upper() ) if wbStatus == true and entity then local statements = entity:getBestStatements( 'P1630' ) for _, statement in pairs( statements ) do if statement.mainsnak.snaktype == 'value' then formatter = statement.mainsnak.datavalue.value break end end end end if formatter and formatter ~= '' then local link = mw.ustring.gsub( formatter, '$1', value ) local title = options.title if not title or title == '' then title = '$1' end title = mw.ustring.gsub( title, '$1', value ) return '[' .. link .. ' ' .. title .. ']' end return value end --[[ Функция для оформления числовых значений Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] local function formatQuantity( value, options ) -- диапазон значений local amount = string.gsub( value['amount'], '^%+', '' ); local lang = mw.language.getContentLanguage(); local langCode = lang:getCode(); -- Пороги для переключения в экспоненциальный формат (настраивается) local EXP_THRESHOLD_HIGH = options.expThreshold or 1e9 -- по умолчанию 10⁹ local EXP_THRESHOLD_LOW = options.expThresholdLow or 1e-4 -- Форматирование числа: десятичное или экспоненциальное local function formatNum( number, exponent ) -- округление до 13 знаков после запятой local mult = 10^13 number = math.floor( number * mult + 0.5 ) / mult if exponent then -- Экспоненциальный формат: мантисса с нужной точностью -- Сохраняем 4-5 знаков после запятой для читаемости local mantissa = lang:formatNum( math.floor( number * 100000 + 0.5 ) / 100000 ) return mantissa .. '×10' .. mw.ustring.char( 0x207B, 0x2070 ) .. exponent -- ¹⁰⁻ⁿ else return lang:formatNum( number ) end end -- Вспомогательная функция: вычисление порядка числа (логарифм по основанию 10) local function getOrder( num ) if num == 0 then return 0 end return math.floor( math.log10( math.abs( num ) ) ) end -- Вспомогательная функция: форматирование экспоненты как верхнего индекса local function formatExponent( exp ) local superscripts = { ['0']='⁰', ['1']='¹', ['2']='²', ['3']='³', ['4']='⁴', ['5']='⁵', ['6']='⁶', ['7']='⁷', ['8']='⁸', ['9']='⁹', ['-']='⁻' } local sign = exp < 0 and '⁻' or '' local absExp = math.abs( exp ) local result = sign for digit in tostring( absExp ):gmatch( '.' ) do result = result .. (superscripts[digit] or digit) end return '×10' .. result end local amountNum = tonumber( amount ) local out -- Определяем, нужна ли экспоненциальная запись local useExponential = false local mainOrder = getOrder( amountNum ) if math.abs( amountNum ) >= EXP_THRESHOLD_HIGH or ( math.abs( amountNum ) > 0 and math.abs( amountNum ) < EXP_THRESHOLD_LOW ) then useExponential = true end -- Если есть погрешность — согласуем формат if value.upperBound then local diff = tonumber( value.upperBound ) - tonumber( amount ) if diff > 0 then if useExponential then -- Находим общий порядок для выравнивания local diffOrder = getOrder( diff ) -- Используем порядок основного числа как базовый local baseOrder = mainOrder -- Форматируем оба числа с одинаковым множителем local amountScaled = amountNum / (10^baseOrder) local diffScaled = diff / (10^baseOrder) -- Округляем погрешность до 1-2 значащих цифр для читаемости local diffRounded = tonumber( string.format( '%.2e', diffScaled ) ) -- Форматируем мантиссы local amountMantissa = lang:formatNum( math.floor( amountScaled * 100000 + 0.5 ) / 100000 ) local diffMantissa = lang:formatNum( math.floor( diffRounded * 10000 + 0.5 ) / 10000 ) out = amountMantissa .. '±' .. diffMantissa .. formatExponent( baseOrder ) else out = formatNum( amountNum ) .. '±' .. formatNum( diff ) end else out = formatNum( amountNum ) end else if useExponential then out = formatNum( amountNum, mainOrder ) else out = formatNum( amountNum ) end end -- Добавление единицы измерения if options.unit and options.unit ~= '' then if options.unit ~= '-' then out = out .. ' ' .. options.unit end elseif value.unit and string.match( value.unit, 'http://www.wikidata.org/entity/' ) then local unitEntityId = string.gsub( value.unit, 'http://www.wikidata.org/entity/', '' ); local wbStatus, unitEntity = pcall( mw.wikibase.getEntity, unitEntityId ); if wbStatus == true and unitEntity then local writingSystemElementId = 'Q8209'; local langElementId = 'Q7737'; local label = getLabelWithLang( context, options, unitEntity, nil, { 'P5061[language:' .. langCode .. ']', 'P5061[language:mul]', 'P558[P282:' .. writingSystemElementId .. ', P407:' .. langElementId .. ']', 'P558[!P282][!P407]' } ); out = out .. ' ' .. label; end end return out; end --[[ Get property datatype by ID. @param string Property ID, e.g. 'P123'. @return string Property datatype, e.g. 'commonsMedia', 'time' or 'url'. ]] local function getPropertyDatatype( propertyId ) if not propertyId or not string.match( propertyId, '^P%d+$' ) then return nil; end local wbStatus, propertyEntity = pcall( mw.wikibase.getEntity, propertyId ); if wbStatus ~= true or not propertyEntity then return nil; end return propertyEntity.datatype; end local function getDefaultValueFunction( datavalue, datatype ) -- вызов обработчиков по умолчанию для известных типов значений if datavalue.type == 'wikibase-entityid' then -- Entity ID return function( context, options, value ) return formatEntityId( context, options, getEntityIdFromValue( value ) ) end; elseif datavalue.type == 'string' then -- String if datatype and datatype == 'commonsMedia' then -- Media return function( context, options, value ) if ( not options.caption or options.caption == '' ) and ( not options.description or options.description == '' ) and options.qualifiers and options.qualifiers.P2096 then for i, qualifier in pairs( options.qualifiers.P2096 ) do if ( qualifier and qualifier.datavalue and qualifier.datavalue.type == 'monolingualtext' and qualifier.datavalue.value and qualifier.datavalue.value.language == contentLanguageCode ) then options.caption = qualifier.datavalue.value.text options.description = qualifier.datavalue.value.text break end end end return formatCommonsMedia( value, options ) end; elseif datatype and datatype == 'external-id' then -- External ID return function( context, options, value ) return formatExternalId( value, options ) end elseif datatype and datatype == 'url' then -- URL return function( context, options, value ) local moduleUrl = require( 'Module:URL' ) if not options.length or options.length == '' then options.length = 25 end return moduleUrl.formatUrlSingle( context, options, value ); end end return function( context, options, value ) return value end; elseif datavalue.type == 'monolingualtext' then -- моноязычный текст (строка с указанием языка) return function( context, options, value ) if ( options.monolingualLangTemplate == 'lang' ) then return options.frame:expandTemplate{ title = 'lang-' .. value.language, args = { value.text } }; elseif ( options.monolingualLangTemplate == 'ref' ) then return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>' .. options.frame:expandTemplate{ title = 'ref-' .. value.language }; else return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>'; end end; elseif datavalue.type == 'globecoordinate' then -- географические координаты return function( context, options, value ) return formatGlobeCoordinate( value, options ) end; elseif datavalue.type == 'quantity' then return function( context, options, value ) return formatQuantity( value, options ) end; elseif datavalue.type == 'time' then return function( context, options, value ) local moduleDate = require( 'Module:Wikidata/date' ) return moduleDate.formatDate( context, options, value ); end; else -- во всех стальных случаях возвращаем ошибку throwError( 'unknown-datavalue-type' ) end end --[[ Функция для оформления значений (value) Подробнее о значениях см. d:Wikidata:Glossary/ru Принимает: объект-значение и таблицу параметров, Возвращает: строку оформленного текста ]] function formatDatavalue( context, options, datavalue, datatype ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not datavalue ) then error( 'datavalue not specified' ); end; if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end; -- проверка на указание специализированных обработчиков в параметрах, -- переданных при вызове context.formatValueDefault = getDefaultValueFunction( datavalue, datatype ); local functionToCall = getUserFunction( options, 'value', context.formatValueDefault ); return functionToCall( context, options, datavalue.value ); end --[[ Функция для оформления идентификатора сущности Принимает: строку индентификатора (типа Q42) и таблицу параметров, Возвращает: строку оформленного текста ]] function formatEntityId( context, options, entityId ) -- получение локализованного названия local wbStatus, entity = pcall( mw.wikibase.getEntity, entityId ) if wbStatus ~= true then return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="color:#b32424; border-bottom: 1px dotted #b32424; cursor: help; white-space: nowrap" title="Ошибка получения элемента из Викиданных.">×</span>' .. categoryLinksToEntitiesWithWikibaseError; end local boundaries = nil if options.qualifiers then boundaries = p.getTimeBoundariesFromQualifiers( frame, context, { qualifiers = options.qualifiers } ) end local label, labelLanguageCode = getLabelWithLang( context, options, entity, boundaries ) -- определение соответствующей показываемому элементу категории local category = '' if ( options.category ) then local claims = WDS.filter( entity.claims, options.category ); if ( claims ) then for _, claim in pairs( claims ) do if ( claim.mainsnak and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.type == 'wikibase-entityid' ) then local catEntityId = claim.mainsnak.datavalue.value.id; local wbStatus, catEntity = pcall( mw.wikibase.getEntity, catEntityId ); if ( wbStatus == true and catEntity and catEntity:getSitelink() ) then category = '[[' .. catEntity:getSitelink() .. ']]'; end end end end end -- получение ссылки по идентификатору local link = mw.wikibase.sitelink( entityId ) if link then -- ссылка на категорию, а не добавление страницы в неё if mw.ustring.match( link, '^' .. mw.site.namespaces[ 14 ].name .. ':' ) then link = ':' .. link end if label then if ( contentLanguageCode ~= labelLanguageCode ) then return '[[' .. link .. '|' .. label .. ']]' .. categoryLinksToEntitiesWithMissingLocalLanguageLabel .. category; else return '[[' .. link .. '|' .. label .. ']]' .. category; end else return '[[' .. link .. ']]' .. category; end end if label then -- красная ссылка -- TODO: разобраться, почему не всегда есть options.frame local title = mw.title.new( label ); if title and not title.exists and options.frame then return '[[' .. label .. ']]<sup>[[:d:' .. entityId .. '|[d]]]</sup>' .. category; end -- TODO: перенести до проверки на существование статьи local sup = ''; if ( not options.format or options.format ~= 'text' ) and entityId ~= 'Q6581072' and entityId ~= 'Q6581097' -- TODO: переписать на format=text then sup = '<sup class="plainlinks noprint">[//www.wikidata.org/wiki/' .. entityId .. '?uselang=' .. contentLanguageCode .. ' [d&#x5d;]</sup>' end -- одноимённая статья уже существует - выводится текст и ссылка на ВД return '<span class="iw" data-title="' .. label .. '">' .. label .. sup .. '</span>' .. category end -- сообщение об отсутвии локализованного названия -- not good, but better than nothing return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="В Викиданных нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>' .. categoryLinksToEntitiesWithMissingLabel .. category; end --[[ Функция для оформления утверждений (statement) Подробнее о утверждениях см. d:Wikidata:Glossary/ru Принимает: таблицу параметров Возвращает: строку оформленного текста, предназначенного для отображения в статье ]] -- устаревшее имя, не использовать function p.formatStatements( frame ) return p.formatProperty( frame ); end --[[ Получение параметров, которые обычно используются для вывода свойства. ]] function getPropertyParams( propertyId, datatype, params ) local config = require( 'Module:Wikidata/config' ); if not config then return {}; end -- Различные уровни настройки параметров, по убыванию приоритета local propertyParams = {}; -- 1. Параметры, указанные явно при вызове if params then local tplParams = mw.clone( params ); for key, value in pairs( tplParams ) do if value ~= '' then propertyParams[key] = value; end end end -- 2. Настройки конкретного параметра if config['properties'] and config['properties'][propertyId] then local selfParams = mw.clone( config['properties'][propertyId] ); for key, value in pairs( selfParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 3. Указанный пресет настроек if propertyParams['preset'] and config['presets'] and config['presets'][propertyParams['preset']] then local presetParams = mw.clone( config['presets'][propertyParams['preset']] ); for key, value in pairs( presetParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 4. Настройки для типа данных if datatype and config['datatypes'] and config['datatypes'][datatype] then local datatypeParams = mw.clone( config['datatypes'][datatype] ); for key, value in pairs( datatypeParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end -- 5. Общие настройки для всех свойств if config['global'] then local globalParams = mw.clone( config['global'] ); for key, value in pairs( globalParams ) do if propertyParams[key] == nil then propertyParams[key] = value; end end end return propertyParams; end function p.formatProperty( frame ) local args = frame.args -- проверка на отсутствие обязательного параметра property if not args.property then throwError( 'property-param-not-provided' ) end local propertyId = mw.language.getContentLanguage():ucfirst( string.gsub( args.property, '%[.*$', '' ) ) local datatype = getPropertyDatatype( propertyId ); args = getPropertyParams( propertyId, datatype, args ); -- проброс всех параметров из шаблона {wikidata} local p_frame = frame:getParent(); if p_frame and p_frame:getTitle() == mw.site.namespaces[10].name .. ':Wikidata' then copyTo( p_frame.args, args ); end args.plain = toBoolean( args.plain, false ); args.nocat = toBoolean( args.nocat, false ); args.references = toBoolean( args.references, true ); -- если значение передано в параметрах вызова то выводим только его if args.value and args.value ~= '' then -- специальное значение для скрытия Викиданных if args.value == '-' then return '' end local value = args.value -- опция, запрещающая оформление значения, поэтому никак не трогаем if args.plain then return value end -- обработчики по типу значения local wrapperExtraArgs = '' if args['value-module'] and args['value-function'] and not string.find( value, '[%[%]%{%}]' ) then local func = getUserFunction( args, 'value' ); value = func( {}, args, value ); elseif datatype == 'commonsMedia' then value = formatCommonsMedia( value, args ); elseif datatype == 'external-id' and not string.find( value, '[%[%]%{%}]' ) then wrapperExtraArgs = wrapperExtraArgs .. ' data-wikidata-external-id="' .. mw.text.encode( value ).. '"'; value = formatExternalId( value, args ); elseif datatype == 'url' then local moduleUrl = require( 'Module:URL' ); value = moduleUrl.formatUrlSingle( nil, args, value ); end -- оборачиваем в тег для JS-функций if string.match( propertyId, '^P%d+$' ) then value = mw.text.trim( value ) -- временная штрафная категория для исправления табличных вставок if ( propertyId ~= 'P166' and string.match( value, '<t[dr][ >]' ) and not string.match( value, '<table >]' ) and not string.match( value, '^%{%|' ) ) then value = value .. '[[Category:Википедия:Статьи с табличной вставкой в карточке]]' else -- значений с блочными тегами остаются блоком, текст встраиваем в строку if ( string.match( value, '\n' ) or string.match( value, '<t[dhr][ >]' ) or string.match( value, '<div[ >]' ) ) then value = '<div class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">\n' .. value .. '</div>' else value = '<span class="no-wikidata"' .. wrapperExtraArgs .. ' data-wikidata-property-id="' .. propertyId .. '">' .. value .. '</span>' end end end -- добавляем категорию-маркер if not args.nocat then local pageTitle = mw.title.getCurrentTitle(); if pageTitle.namespace == 0 then value = value .. categoryLocalValuePresent; end end return value end if ( args.plain ) then -- вызова стандартного обработчика без оформления, если передана опция plain return frame:callParserFunction( '#property', propertyId ); end g_frame = frame -- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений) return formatProperty( args ) end --[[ Функция оформления ссылок на источники (reference) Подробнее о ссылках на источники см. d:Wikidata:Glossary/ru Экспортируется в качестве зарезервированной точки для вызова из функций-расширения вида claim-module/claim-function через context Вызов из других модулей напрямую осуществляться не должен (используйте frame:expandTemplate вместе с одним из специлизированных шаблонов вывода значения свойства). Принимает: объект-таблицу утверждение Возвращает: строку оформленных ссылок для отображения в статье ]] function formatRefs( context, options, statement ) if ( not context ) then error( 'context not specified' ); end; if ( not options ) then error( 'options not specified' ); end; if ( not options.entity ) then error( 'options.entity missing' ); end; if ( not statement ) then error( 'statement not specified' ); end; if ( not outputReferences ) then return ''; end local result = ''; if ( statement.references ) then local allReferences = statement.references; -- local hasPreferred = false; -- for _, reference in pairs( statement.references ) do -- if ( reference.snaks -- and reference.snaks.P248 -- and reference.snaks.P248[1] -- and reference.snaks.P248[1].datavalue -- and reference.snaks.P248[1].datavalue.value.id ) then -- local entityId = reference.snaks.P248[1].datavalue.value.id; -- if ( preferredSources[entityId] ) then -- hasPreferred = true; -- end -- end -- end for _, reference in pairs( statement.references ) do local display = false; -- if ( hasPreferred ) then if ( reference.snaks and reference.snaks.P248 and reference.snaks.P248[1] and reference.snaks.P248[1].datavalue and reference.snaks.P248[1].datavalue.value.id ) then local entityId = reference.snaks.P248[1].datavalue.value.id; if (not deprecatedSources[entityId] ) then display = true; end end -- end if ( display ) then result = result .. moduleSources.renderReference( g_frame, options.entity, reference ); end end end return result end return p qbrajwbm1hosb7st9gawkgfgvtm65ue Džibuti 0 2794 49885 36679 2026-05-09T20:55:59Z Onegaborg 144 49885 wikitext text/x-wiki {{Mua | Nimi=Džibutin Tazavaldu | Alguperäine nimi={{lang-ar|جمهورية جيبوتي}} ''Džumhuri'at Džibuti''<br>{{lang-fr|République de Djibouti}} | Flagu=Flag_of_Djibouti.svg | Gerbu=Emblem of Djibouti.svg | Audio = The Djiboutian anthem.ogg | Iččenäžyön päivymiärät=27. kezäkuudu 1977 | Genetiivu=Džibutin | Piälinnu=[[Džibuti (linnu)|Džibuti]] | Suurin linnu=[[Džibuti (linnu)|Džibuti]] | Rahvahan lugumäry=921 804 | Hinnoituksen vuozi=2020 | Pinduala=23&nbsp;200 }} '''Džibuti (Džibutin Tazavaldu)''' on valdivo Päivännouzupuolizes Afriekas, se on Afriekan niemimuan lähäl. Valdivon päivännouzupuoles on Adenan lahti, pohjazes – [[Eritrei]], päivänlaskupuoles sego suves – [[Efiopii]], liidehes - iččenäine [[Somali]]mua, kuduan alovehtu pietäh Somalin ozannu. Virrallizet kielet ollah fransien da aruabien kielet. Muan piälinnu on [[Džibuti (linnu)|Džibuti]]. Pinduala on 23 200 km². Džibutin Tazavallas eläy (vuvven [[2009]] tiedoloin mugah) 818 169 hengie. [[Kategourii:Afriekan muat|D]] [[Kategourii:Muantiedo|D]] 4mbxgm9qkpzd9x8q2bgiaj6r14bbf7z 49886 49885 2026-05-09T20:59:39Z Onegaborg 144 49886 wikitext text/x-wiki {{Mua | Nimi=Džibutin Tazavaldu | Alguperäine nimi={{lang-ar|جمهورية جيبوتي}} ''Džumhuri'at Džibuti''<br>{{lang-fr|République de Djibouti}} | Flagu=Flag_of_Djibouti.svg | Gerbu=Emblem of Djibouti.svg | Audio = The Djiboutian anthem.ogg | Iččenäžyön päivymiärät=27. kezäkuudu 1977 | Genetiivu=Džibutin | Piälinnu=[[Džibuti (linnu)|Džibuti]] | Suurin linnu=[[Džibuti (linnu)|Džibuti]] | Rahvahan lugumäry=921 804 | Hinnoituksen vuozi=2020 | Pinduala=23&nbsp;200 }} '''Džibuti (Džibutin Tazavaldu)''' on valdivo Päivännouzupuolizes Afriekas, se on Afriekan niemimuan lähäl. Valdivon päivännouzupuoles on Adenan lahti, pohjazes – [[Eritrei]], päivänlaskupuoles sego suves – [[Efiopii]], liidehes - iččenäine [[Somalimua]], kuduan alovehtu pietäh [[Somali]]n ozannu. Virrallizet kielet ollah fransien da aruabien kielet. Muan piälinnu on [[Džibuti (linnu)|Džibuti]]. Pinduala on 23 200 km². Džibutin Tazavallas eläy (vuvven [[2009]] tiedoloin mugah) 818 169 hengie. [[Kategourii:Afriekan muat|D]] [[Kategourii:Muantiedo|D]] ba3m89j2nbo5taancbr0jneo10o9eze Efiopii 0 2800 49882 44882 2026-05-09T20:49:29Z Onegaborg 144 49882 wikitext text/x-wiki {{Mua | Nimi=Efiopien Demokrattine liittotazavaldu | Alguperäine nimi=<b>amh.</b>የኢትዮጵያ ፌዴራላዊ ዲሞክራሲያዊ ሪፐብሊክ<br><small>(''ye’Ītiyoṗṗya Fēdēralawī Dēmokirasīyawī Rīpebilīk'')</small><br><b>oro.</b>Federaalawaa Dimokraatawaa Repabliikii Itoophiyaa<br><b>tigr./b> ናይኢትዮጵያ ፌዴራላዊ ዴሞክራሲያዊ ሪፐብሊክ<br><small>(''nayi’ītiyop’iya fēdēralawī dēmokirasīyawī rīpebilīki'')</small> | Flagu=Flag_of_Ethiopia.svg | Gerbu=Coat_of_arms_of_Ethiopia.svg | Audio = Wedefit_Gesgeshi_Widd_Innat_Ittyoppya.ogg | Iččenäžyön päivymiärät= | Genetiivu=Efiopien | Piälinnu=[[Addis-Abeba]] | Suurimat linnat=[[Addis-Abeba]], [[Dire-Daua]], [[Bahr-Dar]], [[Gonder]], [[Auasa]] | Rahvahan lugumäry=116 462 712 | Hinnoituksen vuozi=2023 | Pinduala=1 104 300 }} '''Efiopii''' (virralline nimi on ''Efiopien Demokrattine liittotazavaldu'', endine ''Abissinii'') on valdivo Päivännouzupuolizes [[Afriekku|Afriekas]], kuduan rajal ei ole merdy (sen jälles, konzu [[Eritrei]] eroi Efiopiespäi vuvven [[1993]] oraskuun 24. päivänny). Rahvahan lugumiäry on 90 miljonua hengie, muan pinduala on 1 104 300 km². Efiopii on toine Afriekan mua ([[Nigerii|Nigerien]] jälles), kus eläy suurin rahvahan lugumiäry. Rahvahan lugumiärän mugah Efiopii on n'elländeltostu sijal muailmas, pindualan suuruon mugah - kahtendelkymmenendel seiččemendel sijal. Piälinnu on [[Addis-Abeba]]. Valdivolline kieli on amharan kieli. Efiopien rajasusiedumualoinnu ollah: [[Eritrei]] – pohjazes, [[Džibuti]] – koillizes, [[Somali]] da iččenäine Somalimua – päivännouzupuoles, [[Kenii]] – suves, [[Sudan]] – luodehes da [[Suvi Sudan|Suvi-Sudan]] – lounazes. Efiopies on etnokul'tuuroin eriluadužus. Läs 60% muan eläjis uskou [[Hristianskoi usko|Hristossah]]. [[Kategourii:Afriekan muat|E]] [[Kategourii:Muantiedo|E]] thocx3stuonvibqjxgttxx5s9r7h8w6 49883 49882 2026-05-09T20:49:51Z Onegaborg 144 49883 wikitext text/x-wiki {{Mua | Nimi=Efiopien Demokrattine liittotazavaldu | Alguperäine nimi=<b>amh.</b>የኢትዮጵያ ፌዴራላዊ ዲሞክራሲያዊ ሪፐብሊክ<br><small>(''ye’Ītiyoṗṗya Fēdēralawī Dēmokirasīyawī Rīpebilīk'')</small><br><b>oro.</b>Federaalawaa Dimokraatawaa Repabliikii Itoophiyaa<br><b>tigr.</b> ናይኢትዮጵያ ፌዴራላዊ ዴሞክራሲያዊ ሪፐብሊክ<br><small>(''nayi’ītiyop’iya fēdēralawī dēmokirasīyawī rīpebilīki'')</small> | Flagu=Flag_of_Ethiopia.svg | Gerbu=Coat_of_arms_of_Ethiopia.svg | Audio = Wedefit_Gesgeshi_Widd_Innat_Ittyoppya.ogg | Iččenäžyön päivymiärät= | Genetiivu=Efiopien | Piälinnu=[[Addis-Abeba]] | Suurimat linnat=[[Addis-Abeba]], [[Dire-Daua]], [[Bahr-Dar]], [[Gonder]], [[Auasa]] | Rahvahan lugumäry=116 462 712 | Hinnoituksen vuozi=2023 | Pinduala=1 104 300 }} '''Efiopii''' (virralline nimi on ''Efiopien Demokrattine liittotazavaldu'', endine ''Abissinii'') on valdivo Päivännouzupuolizes [[Afriekku|Afriekas]], kuduan rajal ei ole merdy (sen jälles, konzu [[Eritrei]] eroi Efiopiespäi vuvven [[1993]] oraskuun 24. päivänny). Rahvahan lugumiäry on 90 miljonua hengie, muan pinduala on 1 104 300 km². Efiopii on toine Afriekan mua ([[Nigerii|Nigerien]] jälles), kus eläy suurin rahvahan lugumiäry. Rahvahan lugumiärän mugah Efiopii on n'elländeltostu sijal muailmas, pindualan suuruon mugah - kahtendelkymmenendel seiččemendel sijal. Piälinnu on [[Addis-Abeba]]. Valdivolline kieli on amharan kieli. Efiopien rajasusiedumualoinnu ollah: [[Eritrei]] – pohjazes, [[Džibuti]] – koillizes, [[Somali]] da iččenäine Somalimua – päivännouzupuoles, [[Kenii]] – suves, [[Sudan]] – luodehes da [[Suvi Sudan|Suvi-Sudan]] – lounazes. Efiopies on etnokul'tuuroin eriluadužus. Läs 60% muan eläjis uskou [[Hristianskoi usko|Hristossah]]. [[Kategourii:Afriekan muat|E]] [[Kategourii:Muantiedo|E]] awd1h373wlauez0z14f6f1ls29tyshr Eritrei 0 2817 49881 36795 2026-05-09T20:40:56Z Onegaborg 144 49881 wikitext text/x-wiki {{Mua | Nimi=Eritrein valdukundu | Alguperäine nimi=<b>tigr.</b> ሃገረ ኤርትራ<br>{{lang-ar|دولة إرتريا}}<br>{{lang-en|State of Eritrea}} | Flagu=Flag_of_Eritrea.svg | Gerbu=Emblem_of_Eritrea_(or_argent_azur).svg | Audio = National Anthem of Eritrea by US Navy Band.ogg | Iččenäžyön päivymiärät=24. oraskuudu 1993 | Genetiivu=Eritrein | Piälinnu=[[Asmera]] | Suurin linnu=[[Asmera]] | Rahvahan lugumäry=6 081 196 | Hinnoituksen vuozi=2022 | Pinduala=117 600 }} '''Eritrei''' on valdivo Päivännouzupuolizes Afriekas, Ruskien meren rannikol. Sen rajal ollah: [[Sudan]] – päivänlaskupuoles, [[Efiopii]] – suves, [[Džibuti]] – päivännouzupuoles. Eritrei eroi Efiopiespäi vuonnu 1993. Muan piälinnu on [[Asmera]]-linnu. Pinduala - 121 100 km². Eritreis eläy (vuvven 2012 tiedoloin mugah) 6 086 495 hengie. [[Kategourii:Afriekan muat|E]] [[Kategourii:Muantiedo|E]] tox342rj3xjd190odcgjp5wxcd7a0c5 Kenii 0 3092 49888 37087 2026-05-09T21:12:09Z Onegaborg 144 49888 wikitext text/x-wiki {{Mua | Nimi=Kenien tazavaldu | Alguperäine nimi=<b>suah.</b> Jamhuri ya Kenya<br>{{lang-en|Republic of Kenya}} | Flagu=Flag_of_Kenya.svg | Gerbu=Alternate Coat of arms of Kenya.svg | Audio = National anthem of Kenya, performed by the United States Navy Band.wav | Iččenäžyön päivymiärät=12. talvikuudu 1963 | Genetiivu=Kenien | Piälinnu=[[Nairobi]] | Suurimat linnat=[[Nairobi]], [Mombasa]] | Rahvahan lugumäry=52 428 290 | Hinnoituksen vuozi=2024 | Pinduala=580 367 }} '''Kenii''', virrallizesti '''Kenien tazavaldu''' on valdivo Päivännouzu-Afriekas. Kenii on endine Britanien kolounii, se sai iččenäžyön 11.talvikuudu v.1963. Pohjazes se rajoittuu [[Efiopii|Efiopienke]], päivännouzus [[Somali]]enke, lounuas [[Tanzanii|Tanzanienke]], päivänlaskus [[Ugandu|Ugandanke]], luodehes [[Suvi Sudan|Suvi-Sudananke]]. Virrallizet kielet ollah anglien kieli da suahili. Piälinnu on [[Nairobi]]. Suurimat linnat ollah Nairobi da Mombasa. Muan pinduala on 582 650 km². Rahvahan lugumiäry on 44 037 656 hengie. [[Kategourii:Afriekan muat|K]] [[Kategourii:Muantiedo|K]] a31y38445a03c4r0hdvy3q4psmhmtjx Somali 0 3699 49887 42796 2026-05-09T21:06:23Z Onegaborg 144 49887 wikitext text/x-wiki {{Mua | Nimi=Somalin federatiivine tazavaldu | Alguperäine nimi=<b>som.</b> Jamhuuriyadda Federaalka Soomaaliya<br>{{lang-ar|جمهورية الصومال الفيدرالية}} | Flagu=Flag_of_Somalia.svg | Gerbu=Coat_of_arms_of_Somalia.svg | Audio = Somali national anthem, performed by the United States Navy Band.oga | Iččenäžyön päivymiärät=1960 | Genetiivu=Somalin | Piälinnu=[[Mogadišo]] | Suurimat linnat=[[Mogadišo]], [Hargeisa]] | Rahvahan lugumäry=19 280 850 | Hinnoituksen vuozi=2025 | Pinduala=637 657 }} '''Somali''', virrallizesti Somalin federatiivine tazavaldu on valdivo Päivännouzu-Afriekas. Somali sijaiččou Afriekan päivännouzurannal Afriekan sarves Somalin niemimual. Lounuas se rajoittuu [[Kenii|Kenienke]], päivännouzus [[Efiopii|Efiopienke]], luodehes [[Džibuti]]nke. Päivännouzus Somali rajoittuu [[Indien valdumeri|Indien valdumerenke]], pohjazes Adenan lahtenke. Virrallizet kielet ollah somali da arabien kieli. Piälinnu on [[Mogadišo]]. Muan pinduala on 637 657&nbsp;km². Rahvahan lugumiäry on 10 251 568 hengie. [[Kategourii:Afriekan muat|S]] [[Kategourii:Muantiedo|S]] qazvddy70z1cwa47zpg4okogcjqxa8l Sudan 0 3716 49880 38264 2026-05-09T20:31:25Z Onegaborg 144 49880 wikitext text/x-wiki {{Mua | Nimi=Sudanan tazavaldu | Alguperäine nimi={{lang-ar|جمهورية السودان}}<br>{{lang-en|Republic of Sudan}} | Flagu=Flag_of_Sudan.svg | Gerbu=Emblem_of_Sudan.svg | Audio = Sudan.ogg | Iččenäžyön päivymiärät=1. pakkaskuudu 1956 | Genetiivu=Sudanan | Piälinnu=[[Hartum]] | Suurimat linnat=[[Hartum]], [[Omdurman]], [[Pohjas-Hartum]], [[Niala]], [[Port-Sudan]] | Rahvahan lugumäry=50 015 092 | Hinnoituksen vuozi=2024 | Pinduala=1 861 484 }} '''Sudan''' libo Sudanan tazavaldu on valdivo Päivännouzu-Afriekas. Pohjazes se rajoittuu [[Jegiptu|Jegiptanke]], luodehes [[Livii|Livienke]], päivänlaskus [[Čad]]anke, lounuas [[Keski Afriekan Tazavaldu|Keski Afriekan Tazavaldanke]], suves [[Eritrei]]nke, liidehes [[Efiopii|Efiopienke]]. Sudan rajoittuu Ruskien merenke. Piälinnu on [[Hartum]]. Muan pinduala on 1 886 068 km². Rahvahan lugumiäry on 40 234 882 hengie. Virrallizet kielet ollah arabien da anglien kielet. [[Kategourii:Afriekan muat|S]] [[Kategourii:Muantiedo|S]] lyt9xxjhv0lt4hpkfzvz5un7f1ri1hu The Rollling Stones 0 10851 49889 44940 2026-05-09T22:06:55Z Niegodzisie 3607 /* Stuudiial'bomat */ 49889 wikitext text/x-wiki [[Failu:The Rolling Stones Summerfest in Milwaukee - 2015.jpg|thumb|500.px|alt=The Rollling Stones|The Rollling Stones (2015)]] [[Failu:Rolling_Stones_1965.jpg|thumb|The Rolling Stones 1965: Brian Jones, Charlie Watts, Mick Jagger, Keith Richards, Bill Wyman]] '''The Rollling Stones''' on vuvvennu 1962 perustettu [[Yhtys Kuningaskundu|britannielaine]] rock-joukkoveh. == Jäzenet == * Mick Jagger * Keith Richards * Charlie Watts * Ron Wood == Stuudiial'bomat == * The Rolling Stones (1964) * 12 X 5 (1964) * The Rolling Stones No. 2 (1965) * The Rolling Stones, Now! (1965) * Out of Our Heads (1965) * December’s Children (And Everybody’s) (1965) * Aftermath (1966) * Between the Buttons (1967) * Their Satanic Majesties Request (1967) * Beggars Banquet (1968) * Let It Bleed (1969) * Sticky Fingers (1971) * Exile on Main St. (1972) * Goats Head Soup (1973) * It’s Only Rock ’n’ Roll (1974) * Black and Blue (1976) * Some Girls (1978) * Emotional Rescue (1980) * Tattoo You (1981) * Undercover (1983) * Dirty Work (1986) * Steel Wheels (1989) * Voodoo Lounge (1994) * Bridges to Babylon (1997) * A Bigger Bang (2005) * Blue & Lonesome (2016) * Hackney Diamonds (2023) * Foreign Tongues (2026) [[Kategourii:Muuzikkujoukot]] mfzsiaujcn9s0lznsva7o99a2wsddm4 Kategourii:Sivut, joissa on viittausvirheitä 14 15571 49894 47867 2026-05-10T10:39:28Z Olksolo 356 hidden cat 49894 wikitext text/x-wiki __HIDDENCAT__ [[Kategourii:Wikipedii]] 01blgpupnuyqf4xdxnujuuoirrkk4dj Käyttäi pagin:Olksolo 3 15884 49871 48487 2026-05-09T13:01:43Z Fembriha 8650 /* Шаблон, касающийся космических тел */ uuzi oza 49871 wikitext text/x-wiki == Шаблон, касающийся космических тел == Добрый день. Большое спасибо за внедрение шаблона в статьи, посвящённые космическим телам. Они на финском и могу заняться переводом шаблона на ливвиковский. Как это сделать? [[Käyttäi:Fembriha|Fembriha]] ([[Käyttäi pagin:Fembriha|pagin]]) 9. Oraskuuta 2026 kello 16.01 (MSK) tqwv3t9tblmh7b4e8ca864cjq2x6aot 49872 49871 2026-05-09T13:31:40Z Olksolo 356 /* Шаблон, касающийся космических тел */ vastaus ([[mw:c:Special:MyLanguage/User:JWBTH/CD|CD]]) 49872 wikitext text/x-wiki == Шаблон, касающийся космических тел == Добрый день. Большое спасибо за внедрение шаблона в статьи, посвящённые космическим телам. Они на финском и могу заняться переводом шаблона на ливвиковский. Как это сделать? [[Käyttäi:Fembriha|Fembriha]] ([[Käyttäi pagin:Fembriha|pagin]]) 9. Oraskuuta 2026 kello 16.01 (MSK) : Я воткнул "универсальную карточку" (UIC), но можно сделать шаблон специфической карточки для определённого типа статей (как например есть {{tl|Ristikanzu}} или {{tl|Mua}}). В универсальной карточке названия свойств подставляются прямо из Викиданных. Насколько я понимаю, где-то сконфигурировано, что если ливвиковского названия нет, то нам возвращается финское.<br> Вот, например, у свойства P155 не было ливвиковского названия. Я добавил: [[d:Special:Diff/2489218342]]. В результате у астероида [[50000 Quaoar]] в карточке поменялось "Edeltäjä" на "Ielembäine". Я не уверен, что перевод подходящий, если знаете лучше — меняйте.<br> Следует учесть, что изменения в Викиданных долго проходят через систему кэширования и единомоментно могут быть не видны. В таком случае надо попробовать открыть на редактирование страничку, использующую викиданные и, ничего не меняя, сохранить её. [[Käyttäi:Olksolo|Olksolo]] ([[Käyttäi pagin:Olksolo|pagin]]) 9. Oraskuuta 2026 kello 16.31 (MSK) afqnaza1p0d6k5bs0m7n3e263ruoyal 49877 49872 2026-05-09T19:25:41Z Fembriha 8650 /* Шаблон, касающийся космических тел */ Vastavus 49877 wikitext text/x-wiki == Шаблон, касающийся космических тел == Добрый день. Большое спасибо за внедрение шаблона в статьи, посвящённые космическим телам. Они на финском и могу заняться переводом шаблона на ливвиковский. Как это сделать? [[Käyttäi:Fembriha|Fembriha]] ([[Käyttäi pagin:Fembriha|pagin]]) 9. Oraskuuta 2026 kello 16.01 (MSK) : Я воткнул "универсальную карточку" (UIC), но можно сделать шаблон специфической карточки для определённого типа статей (как например есть {{tl|Ristikanzu}} или {{tl|Mua}}). В универсальной карточке названия свойств подставляются прямо из Викиданных. Насколько я понимаю, где-то сконфигурировано, что если ливвиковского названия нет, то нам возвращается финское.<br> Вот, например, у свойства P155 не было ливвиковского названия. Я добавил: [[d:Special:Diff/2489218342]]. В результате у астероида [[50000 Quaoar]] в карточке поменялось "Edeltäjä" на "Ielembäine". Я не уверен, что перевод подходящий, если знаете лучше — меняйте.<br> Следует учесть, что изменения в Викиданных долго проходят через систему кэширования и единомоментно могут быть не видны. В таком случае надо попробовать открыть на редактирование страничку, использующую викиданные и, ничего не меняя, сохранить её. [[Käyttäi:Olksolo|Olksolo]] ([[Käyttäi pagin:Olksolo|pagin]]) 9. Oraskuuta 2026 kello 16.31 (MSK) ::Мне не даёт там редачить, но аккаунт автоподтверждён. Не знаю почему так. Есть мысли? [[Käyttäi:Fembriha|Fembriha]] ([[Käyttäi pagin:Fembriha|pagin]]) 9. Oraskuuta 2026 kello 22.25 (MSK) 11w3w4ussb2wufi2qsr7j400quni78r 49878 49877 2026-05-09T19:34:40Z Olksolo 356 /* Шаблон, касающийся космических тел */ vastaus käyttäjän Fembriha viestiin ([[mw:c:Special:MyLanguage/User:JWBTH/CD|CD]]) 49878 wikitext text/x-wiki == Шаблон, касающийся космических тел == Добрый день. Большое спасибо за внедрение шаблона в статьи, посвящённые космическим телам. Они на финском и могу заняться переводом шаблона на ливвиковский. Как это сделать? [[Käyttäi:Fembriha|Fembriha]] ([[Käyttäi pagin:Fembriha|pagin]]) 9. Oraskuuta 2026 kello 16.01 (MSK) : Я воткнул "универсальную карточку" (UIC), но можно сделать шаблон специфической карточки для определённого типа статей (как например есть {{tl|Ristikanzu}} или {{tl|Mua}}). В универсальной карточке названия свойств подставляются прямо из Викиданных. Насколько я понимаю, где-то сконфигурировано, что если ливвиковского названия нет, то нам возвращается финское.<br> Вот, например, у свойства P155 не было ливвиковского названия. Я добавил: [[d:Special:Diff/2489218342]]. В результате у астероида [[50000 Quaoar]] в карточке поменялось "Edeltäjä" на "Ielembäine". Я не уверен, что перевод подходящий, если знаете лучше — меняйте.<br> Следует учесть, что изменения в Викиданных долго проходят через систему кэширования и единомоментно могут быть не видны. В таком случае надо попробовать открыть на редактирование страничку, использующую викиданные и, ничего не меняя, сохранить её. [[Käyttäi:Olksolo|Olksolo]] ([[Käyttäi pagin:Olksolo|pagin]]) 9. Oraskuuta 2026 kello 16.31 (MSK) ::Мне не даёт там редачить, но аккаунт автоподтверждён. Не знаю почему так. Есть мысли? [[Käyttäi:Fembriha|Fembriha]] ([[Käyttäi pagin:Fembriha|pagin]]) 9. Oraskuuta 2026 kello 22.25 (MSK) ::: На Викиданных у вас нет статуса "автоподтверждённого". Для этого необходимо 50 правок.<br> В принципе, набрать 50 правок в Викиданных несложно — cейчас, например, идёт марафон «[[d:Wikidata:Events/Coordinate Me 2026/ru|Окоординать!]]». [[Käyttäi:Olksolo|Olksolo]] ([[Käyttäi pagin:Olksolo|pagin]]) 9. Oraskuuta 2026 kello 22.34 (MSK) oljo3rqqvs3u148lkfh8x7cpvw3gzph 49879 49878 2026-05-09T19:38:59Z Fembriha 8650 /* Шаблон, касающийся космических тел */ Vastavus 49879 wikitext text/x-wiki == Шаблон, касающийся космических тел == Добрый день. Большое спасибо за внедрение шаблона в статьи, посвящённые космическим телам. Они на финском и могу заняться переводом шаблона на ливвиковский. Как это сделать? [[Käyttäi:Fembriha|Fembriha]] ([[Käyttäi pagin:Fembriha|pagin]]) 9. Oraskuuta 2026 kello 16.01 (MSK) : Я воткнул "универсальную карточку" (UIC), но можно сделать шаблон специфической карточки для определённого типа статей (как например есть {{tl|Ristikanzu}} или {{tl|Mua}}). В универсальной карточке названия свойств подставляются прямо из Викиданных. Насколько я понимаю, где-то сконфигурировано, что если ливвиковского названия нет, то нам возвращается финское.<br> Вот, например, у свойства P155 не было ливвиковского названия. Я добавил: [[d:Special:Diff/2489218342]]. В результате у астероида [[50000 Quaoar]] в карточке поменялось "Edeltäjä" на "Ielembäine". Я не уверен, что перевод подходящий, если знаете лучше — меняйте.<br> Следует учесть, что изменения в Викиданных долго проходят через систему кэширования и единомоментно могут быть не видны. В таком случае надо попробовать открыть на редактирование страничку, использующую викиданные и, ничего не меняя, сохранить её. [[Käyttäi:Olksolo|Olksolo]] ([[Käyttäi pagin:Olksolo|pagin]]) 9. Oraskuuta 2026 kello 16.31 (MSK) ::Мне не даёт там редачить, но аккаунт автоподтверждён. Не знаю почему так. Есть мысли? [[Käyttäi:Fembriha|Fembriha]] ([[Käyttäi pagin:Fembriha|pagin]]) 9. Oraskuuta 2026 kello 22.25 (MSK) ::: На Викиданных у вас нет статуса "автоподтверждённого". Для этого необходимо 50 правок.<br> В принципе, набрать 50 правок в Викиданных несложно — cейчас, например, идёт марафон «[[d:Wikidata:Events/Coordinate Me 2026/ru|Окоординать!]]». [[Käyttäi:Olksolo|Olksolo]] ([[Käyttäi pagin:Olksolo|pagin]]) 9. Oraskuuta 2026 kello 22.34 (MSK) ::::50, а не 15? Спасибо, а то я уже сколько мозг ломаю) [[Käyttäi:Fembriha|Fembriha]] ([[Käyttäi pagin:Fembriha|pagin]]) 9. Oraskuuta 2026 kello 22.38 (MSK) ee94fiwemb5923mknjtqzoypbocys8q Parfenon 0 16513 49892 49862 2026-05-10T09:54:37Z Onegaborg 144 49892 wikitext text/x-wiki [[Failu:Parthenon,_Athens,_20240531_1315_9664.jpg|thumb|Parfenon]] '''Parfenon''' - (muinasgriekan kielel Παρθενών Parthenōn [par.tʰe.nɔ̌ːn], {{K-gr|Παρθενώνας}} Parthenónas [parθeˈnonas], sanasanal – «Neijiten huonehet») on muinasarhitektuuran mustomerki, muinasgriekalaine pyhäkkö, kudai on sijoitannuhes afinalaizele Akropolile. Se on piäpyhäkkö muinazis [[Afiinat|Afinois]], pyhitetty tämän linnan da kogon Attikan suojelijale, jumalattarele Afina-Palladale (Ἀθηνᾶ Παρθένος). Rakendettu vuvvil 447–438 enne meijän aigua arhitektoran Kallikratan Iktinan projiektan mugah da uvistettu vuvvil 438–431 enne meijän aigua Fidian opastukses Periklesan halličuksen aigua. Se on yksi kogo muailman merkillizimbis rakenduksis, voibi olla, ku jopa kaikkein tunnetuin – Afinoin merki «meijän niškoi tänäpäi, samal lail kui joga aijan griekkoin niškoi». Nygöi se on puolirokottu tilas, da nostandu-ruavot ollah käynnys. [[Kategourii:Arheolougii|P]] 82til8s70hw811rullij66tifzul0hi Byzantien valdukundu 0 16514 49890 2026-05-10T01:18:11Z Fembriha 8650 Uuzi sivu: {| class="infobox" style="width: 23em; font-size: 90%;" |- ! colspan="2" style="font-size: 125%; background: #f2f2f2;" | Byzantien valdukundu <br>[[Griekan kieli|grek.]] Βασιλεία τῶν Ῥωμαίων <br>[[Latinan kieli|lat.]] Imperium Romanum <br> <br> 17. pakkaskuudu 395 — <br> — 29. oraskuudu 1453 |- | colspan="2" style="text-align: center;" | [[File:Byzantine Empire animated.gif|350x350px]] |- ! style="text-align: left;" | Piälinnu | [[Konstantinopoli]] |-... 49890 wikitext text/x-wiki {| class="infobox" style="width: 23em; font-size: 90%;" |- ! colspan="2" style="font-size: 125%; background: #f2f2f2;" | Byzantien valdukundu <br>[[Griekan kieli|grek.]] Βασιλεία τῶν Ῥωμαίων <br>[[Latinan kieli|lat.]] Imperium Romanum <br> <br> 17. pakkaskuudu 395 — <br> — 29. oraskuudu 1453 |- | colspan="2" style="text-align: center;" | [[File:Byzantine Empire animated.gif|350x350px]] |- ! style="text-align: left;" | Piälinnu | [[Konstantinopoli]] |- ! style="text-align: left;" | Virallizet kielet | [[Latinan kieli|latina]] ([[620]] vuodessah) <br>[[Griekan kieli|griekka]] ([[620]] vuvves) |- ! style="text-align: left;" | Kielet | [[Byzantien griekan lieli|Byzantien griekka]] |- ! style="text-align: left;" | Halličuksen muodo | Keisarikundu |- ! style="text-align: left;" | Keisarit | [[Constantinus I]] (306–337) <br>[[Theodosius I]] (379–395) <br>[[Arcadius]] (395–408) <br>[[Theodosius II]] (408–450) <br>[[Justinianus I]] (527–565) <br>[[Herakleios]] (610–641) <br>[[Leo III]] (717–741) <br>[[Nikeforos II]] (963–969) <br>[[Iivan I]] (969–976) <br>[[Basilejos II]] (976–1025) <br>[[Aleksios I]] (1081–1118) <br>[[Iivan II]] (1118–1143) <br>[[Manuel I]] (1143–1180) <br>[[Mihkali VIII]] (1261–1282) <br>[[Konstantinos XI]] (1449–1453) |- ! style="text-align: left;" | Pinduala | 2 350 000 km² (457) <br>3 400 000 km² (565) <br>880 000 km² (775) <br>1 675 000 km² (1025) <br>440 000 km² (1281) <br>17 668 km² (1368) |- ! style="text-align: left;" | Rahvahalisto | 16 000 000 heng. (457) <br>20 000 000 km² (565) <br>7 000 000 km² (775) <br>12 000 000 km² (1025) <br>2 000 000 km² (1320) |- ! style="text-align: left;" | Val'uuttu | [[Solidus|solidus]], [[Hyperpyron|hyperpyron]] |} '''Byzantien valdukundu''' libo '''Päivännouzu-Roman valdukundu''' oli Roman valdukunnan jatkeh sen päivännouzupuolizis provinsois [[Myöhäsantiekku|myöhäsantiekal]] da [[Keskiaigu|keskiaijal]], konzu [[Konstantinopoli]] oli Päivännouzu-Roman valdukunnan piälinnannu. Päivännouzu-Roman valdukundu kesti Päivänlasku-Roman valdukunnan murenemine da kuadumine 5. vuozisual m. a. da jatkoi sen olemasoluo vie tuhat vuottu, kuni Konstantinopoli andavui [[Osmanoin valdukundu|Ottomanoin valdukunnale]] vuonnu 1453. Suuriman ozan olemasolos valdukundu pyzyi [[Jevrouppu|Euroupan]] suurimannu talovehellizennu, sodilahallizennu da kul'tuuruvoimannu. Terminät "Byzantien valdukundu" da "Päivännouzu-Roman valdukundu" luajittih valdukunnan häviemizen jälgeh; kanzalazet kučuttih heijän valdukundua ielleh Roman valdukunnakse da iččie romalazikse — nimel, kudamua ristikanzat kučuttih iččie ielleh Ottomanoin valdukunnan aigois. Hos Roman valdivo jatkoi sen olemasoluo da sen perindöt säilyttih, nygyaijan histourientutkijat eroitetah Byzantii sen aijembas inkarnatsiespäi sendäh, ku sen keskus oli Konstantinopolis, se oli suundavunnuh griekkalazeh eiga latinalazeh kul'tuurah da sille oli ominastu päivännouzuortodoksine [[Hristianskoi usko|kristilližys]]. [[Kategourii:Histouriellizet valdivot]] hyiih9juar3zwh6fhqni323cvcgu1be 49891 49890 2026-05-10T01:27:12Z Fembriha 8650 49891 wikitext text/x-wiki '''Byzantien valdukundu''' libo '''Päivännouzu-Roman valdukundu''' oli Roman valdukunnan jatkeh sen päivännouzupuolizis provinsois [[Myöhäsantiekku|myöhäsantiekal]] da [[Keskiaigu|keskiaijal]], konzu [[Konstantinopoli]] oli Päivännouzu-Roman valdukunnan piälinnannu. {| class="infobox" style="width: 23em; font-size: 90%;" |- ! colspan="2" style="font-size: 125%; background: #f2f2f2;" | Byzantien valdukundu <br>[[Griekan kieli|grek.]] Βασιλεία τῶν Ῥωμαίων <br>[[Latinan kieli|lat.]] Imperium Romanum <br> <br> 17. pakkaskuudu 395 — <br> — 29. oraskuudu 1453 |- | colspan="2" style="text-align: center;" | [[File:Byzantine Empire animated.gif|350x350px]] |- ! style="text-align: left;" | Piälinnu | [[Konstantinopoli]] |- ! style="text-align: left;" | Virallizet kielet | [[Latinan kieli|latina]] ([[620]] vuodessah) <br>[[Griekan kieli|griekka]] ([[620]] vuvves) |- ! style="text-align: left;" | Kielet | [[Byzantien griekan lieli|Byzantien griekka]] |- ! style="text-align: left;" | Halličuksen muodo | Keisarikundu |- ! style="text-align: left;" | Keisarit | [[Constantinus I]] (306–337) <br>[[Theodosius I]] (379–395) <br>[[Arcadius]] (395–408) <br>[[Theodosius II]] (408–450) <br>[[Justinianus I]] (527–565) <br>[[Herakleios]] (610–641) <br>[[Leo III]] (717–741) <br>[[Nikeforos II]] (963–969) <br>[[Iivan I]] (969–976) <br>[[Basilejos II]] (976–1025) <br>[[Aleksios I]] (1081–1118) <br>[[Iivan II]] (1118–1143) <br>[[Manuel I]] (1143–1180) <br>[[Mihkali VIII]] (1261–1282) <br>[[Konstantinos XI]] (1449–1453) |- ! style="text-align: left;" | Pinduala | 2 350 000 km² (457) <br>3 400 000 km² (565) <br>880 000 km² (775) <br>1 675 000 km² (1025) <br>440 000 km² (1281) <br>17 668 km² (1368) |- ! style="text-align: left;" | Rahvahalisto | 16 000 000 heng. (457) <br>20 000 000 km² (565) <br>7 000 000 km² (775) <br>12 000 000 km² (1025) <br>2 000 000 km² (1320) |- ! style="text-align: left;" | Val'uuttu | [[Solidus|solidus]], [[Hyperpyron|hyperpyron]] |} Päivännouzu-Roman valdukundu kesti Päivänlasku-Roman valdukunnan murenemine da kuadumine 5. vuozisual m. a. da jatkoi sen olemasoluo vie tuhat vuottu, kuni Konstantinopoli andavui [[Osmanoin valdukundu|Ottomanoin valdukunnale]] vuonnu 1453. Suuriman ozan olemasolos valdukundu pyzyi [[Jevrouppu|Euroupan]] suurimannu talovehellizennu, sodilahallizennu da kul'tuuruvoimannu. Terminät "Byzantien valdukundu" da "Päivännouzu-Roman valdukundu" luajittih valdukunnan häviemizen jälgeh; kanzalazet kučuttih heijän valdukundua ielleh Roman valdukunnakse da iččie romalazikse — nimel, kudamua ristikanzat kučuttih iččie ielleh Ottomanoin valdukunnan aigois. Hos Roman valdivo jatkoi sen olemasoluo da sen perindöt säilyttih, nygyaijan histourientutkijat eroitetah Byzantii sen aijembas inkarnatsiespäi sendäh, ku sen keskus oli Konstantinopolis, se oli suundavunnuh griekkalazeh eiga latinalazeh kul'tuurah da sille oli ominastu päivännouzuortodoksine [[Hristianskoi usko|kristilližys]]. [[Kategourii:Histouriellizet valdivot]] 6fqa1bnd3u54an5hqs145ig33rrdarp