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 .. ' '
.. mw.ustring.sub( i, 1, 1 ) .. '. '
.. 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 .. ' ' .. i .. '. ' .. 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 .. ' ' .. mw.ustring.sub( i, 1, 1 ) .. '. ' .. 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 .. ' ' .. 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 .. ' ' .. toString( context, src.volume, options_commas ) .. '.';
end
if ( src.issue ) then
result = result .. ' — № ' .. toString( context, src.issue, options_commas ) .. '.';
end
if ( src.page ) then
local letter = i18nPage[ context.lang ] or i18nPage[ i18nDefaultLanguage ];
result = result .. ' — ' .. letter .. ' ' .. 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 ' .. 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 .. ' ' .. toString( context, src.volume, options_commas ) .. '.';
end
if ( src.issue ) then
result = result .. ' — № ' .. toString( context, src.issue, options_commas ) .. '.';
end
if ( src.page ) then
local letter = i18nPage[ context.lang ] or i18nPage[ i18nDefaultLanguage ];
result = result .. ' — ' .. letter .. ' ' .. 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 .. " от " .. tostring(d) .. " " .. monthg[m] .. " " .. tostring(y) .. " г. № " .. 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 .. ' '
.. mw.ustring.sub( i, 1, 1 ) .. '. '
.. 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 .. ' ' .. i .. '. ' .. 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 .. ' ' .. mw.ustring.sub( i, 1, 1 ) .. '. ' .. 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 .. ' ' .. 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 .. ' ' .. toString( context, src.volume, options_commas ) .. '.';
end
if ( src.issue ) then
result = result .. ' — № ' .. toString( context, src.issue, options_commas ) .. '.';
end
if ( src.page ) then
local letter = i18nPage[ context.lang ] or i18nPage[ i18nDefaultLanguage ];
result = result .. ' — ' .. letter .. ' ' .. 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 ' .. 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 .. ' ' .. toString( context, src.volume, options_commas ) .. '.';
end
if ( src.issue ) then
result = result .. ' — № ' .. toString( context, src.issue, options_commas ) .. '.';
end
if ( src.page ) then
local letter = i18nPage[ context.lang ] or i18nPage[ i18nDefaultLanguage ];
result = result .. ' — ' .. letter .. ' ' .. 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 .. " от " .. tostring(d) .. " " .. monthg[m] .. " " .. tostring(y) .. " г. № " .. 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]]</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]]</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 ',
["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="huaveillen">h. </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]]</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 ',
["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="huaveillen">h. </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]]</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 ',
["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="huaveillen">h. </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]]</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 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 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 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