Модуль:Sources: различия между версиями
Перейти к навигации
Перейти к поиску
Karaby (обсуждение | вклад) (Новая страница: «local p = {}; local i18nDefaultLanguage = 'Q7737'; local i18nEditors = { Q150 = '', -- French Q1321 = '', -- Spanish Q1860 = '', -- English Q7737 = 'п…») |
bukvica>Wikisaurus (грузить Модуль:Languages только тогда, когда нужно (TODO: не выдавать ошибку, если нету - для малых разделов)) |
||
Строка 1: | Строка 1: | ||
local p = {} | local p = {} | ||
local utils = require('Module:Sources/utils') | |||
local i18nDefaultLanguage = 'ru' | |||
local i18nEtAlDefault = ' et al.' | |||
local i18nEtAl = { | |||
ru = ' и др.', | |||
} | |||
local i18nEditors = { | local i18nEditors = { | ||
fr = '', | |||
de = 'Hrsg.: ', | |||
es = '', | |||
en = '', | |||
it = '', | |||
ru = 'под ред. ', | |||
} | } | ||
local i18nTranslators = { | |||
fr = '', | |||
de = '', | |||
es = '', | |||
en = '', | |||
it = '', | |||
ru = 'пер. ', | |||
} | |||
local i18nVolume = { | local i18nVolume = { | ||
de = 'Vol.', | |||
fr = 'Vol.', | |||
es = 'Vol.', | |||
en = 'Vol.', | |||
it = 'Vol.', | |||
ru = 'Т.', | |||
} | } | ||
local | |||
local i18nIssue = { | |||
en = 'Iss.', | |||
ru = 'вып.', | |||
} | } | ||
local | local i18nPages = { | ||
fr = 'P.', | |||
de = 'S.', | |||
es = 'P.', | |||
en = 'P.', | |||
it = 'P.', | |||
ru = 'С.', | |||
} | |||
local | local i18nNumberOfPages = { | ||
en = 'p.', | |||
ru = 'с.', | |||
} | |||
local i18nTirage = { | |||
en = 'ed. size: %d', | |||
ru = '%d экз.', | |||
} | |||
local monthg = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', "сентября", "октября", "ноября", "декабря"} | |||
local PREFIX_CITEREF = "CITEREF_"; | |||
-- Returns formatted pair {Family name(s), First name(s)} | |||
local function tokenizeName( fullName ) | |||
local start = '^%s*' -- matches beginning of the string + arbitrary number of spaces | |||
local finish = '%s*$' -- matches end of the string + arbitrary number of spaces | |||
local comma = '\,%s+' -- matches comma + single or more spacing character | |||
local space = '%s+' -- matches single or more spacing character | |||
local name = '(%a[%a\-\']*)\.?' -- matches single name, have to start with letter, can contain apostrophe and hyphen, may end with dot | |||
local surname = '(%a[%a\-\']*)' -- same as name, but can't end with dot | |||
local f, i = mw.ustring.match(fullName, start .. surname .. comma .. name .. finish) | |||
if f then | |||
mw.log('tokenizeName: «' .. fullName .. '»: have «Fa, Im» match') | |||
return {f, mw.ustring.sub( i, 1, 1 ) .. '.'} | |||
end | end | ||
local | local f, i, o = mw.ustring.match(fullName, start .. surname .. comma .. name .. space .. name .. finish) | ||
if ( | if f then | ||
mw.log( 'tokenizeName: «' .. fullName .. '»: have «Fa, Im Ot» match') | |||
return {f, mw.ustring.sub( i, 1, 1 ) .. '. ' | |||
.. mw.ustring.sub( o, 1, 1 ) .. '.'} | |||
end | end | ||
if ( | local f1, f2, i = mw.ustring.match(fullName, start .. surname .. space .. surname .. comma .. name .. finish) | ||
if f1 then | |||
mw.log('tokenizeName: «' .. fullName .. '»: have «Fa Fa, Im» match') | |||
return {f1 .. ' ' .. f2, mw.ustring.sub( i, 1, 1 ) .. '.'} | |||
end | end | ||
local i, o, f = mw.ustring.match(fullName, start .. name .. space .. name .. space .. 'оглы' .. space .. surname .. finish) | |||
if f then | |||
mw.log('tokenizeName: «' .. fullName .. '»: have «Im Ot оглы Fa» match') | |||
return {f, mw.ustring.sub(i, 1, 1) .. '. ' .. mw.ustring.sub(o, 1, 1) .. '.'} | |||
end | end | ||
local i1, i2, f = mw.ustring.match(fullName, start .. name .. space .. name .. space .. 'de' .. space .. surname .. finish) | |||
if f then | |||
mw.log('tokenizeName: «' .. fullName .. '»: have «Im Im de Fa» match') | |||
return {f, mw.ustring.sub( i1, 1, 1 ) .. '. ' .. mw.ustring.sub( i2, 1, 1 ) .. '.'} | |||
end | end | ||
-- Try matching k names + surname | |||
for k = 1, 4 do | |||
pattern = start .. string.rep(name .. space, k) .. surname .. finish | |||
matched = {mw.ustring.match(fullName, pattern)} | |||
if #matched ~= 0 then | |||
mw.log('tokenizeName: «' .. fullName .. '»: have «Im (x' .. k .. ') Fa» match') | |||
for i = 1, k do | |||
matched[i] = mw.ustring.sub(matched[i], 1, 1) | |||
end | end | ||
return {matched[k + 1], table.concat(matched, '. ', 1, k) .. '.'} | |||
end | end | ||
end | end | ||
mw.log('Unmatched any pattern: «' .. fullName .. '»') | |||
return {fullName} | |||
end | |||
end | |||
local function personNameToAuthorName( fullName ) | |||
if not fullName then return fullName end | |||
local tokenized = tokenizeName(fullName) | |||
end | if #tokenized == 1 then | ||
return tokenized[1] | |||
else | |||
return tokenized[1] .. ' ' .. tokenized[2] | |||
if | |||
end | end | ||
end | |||
if | local function personNameToResponsibleName( fullName ) | ||
if not fullName then return fullName end | |||
local tokenized = tokenizeName(fullName) | |||
if #tokenized == 1 then | |||
return tokenized[1] | |||
else | |||
return tokenized[2] .. ' ' .. tokenized[1] | |||
end | end | ||
end | |||
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_short = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false, short = true }; | |||
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 }; | |||
local options_commas_authors = { separator = ', ', conjunction = ', ', format = personNameToAuthorName, nolinks = false, preferids = false }; | |||
local options_commas_responsible = { separator = ', ', conjunction = ', ', format = personNameToResponsibleName, nolinks = false, preferids = false }; | |||
function | local options_arxiv = { separator = '; ', conjunction = '; ', format = function( id ) return '[http://arxiv.org/abs/' .. id .. ' arXiv:' .. id .. ']' end, nolinks = true, preferids = false }; | ||
local options_doi = { separator = '; ', conjunction = '; ', format = function( doi ) return '[http://dx.doi.org/' .. doi .. ' doi:' .. doi .. ']' end, nolinks = true, preferids = false }; | |||
local options_issn = { separator = '; ', conjunction = '; ', format = function( issn ) return '[https://www.worldcat.org/issn/' .. issn .. ' ' .. issn .. ']' end, nolinks = true, preferids = false }; | |||
local options_pmid = { separator = '; ', conjunction = '; ', format = function( pmid ) return '[https://www.ncbi.nlm.nih.gov/pubmed/?term=' .. pmid .. ' PMID:' .. pmid .. ']' end, nolinks = true, preferids = false }; | |||
local function getPersonNameAsLabel( context, entityId, providedLabel, options ) | |||
if ( | -- would custom label provided we don't need to check entity at all | ||
if ( not utils.isEmpty( providedLabel ) ) then | |||
mw.log( 'Custom label provided for ' .. entityId ); | |||
return options.format( providedLabel ); | |||
end | end | ||
if ( | local entity = utils.getEntity( context, entityId ); | ||
if ( not entity ) then return '\'\'(entity ' .. entityId .. ' is missing)\'\'' end; | |||
local personName = nil; | |||
-- support only labels so far | |||
if ( entity.labels[ context.lang ] ) then | |||
personName = entity.labels[ context.lang ].value; | |||
mw.log('Got person name of ' .. entityId .. ' from label: «' .. personName .. '»' ) | |||
end | end | ||
if ( | if ( not utils.isInstanceOf( entity, 'Q5' ) ) then | ||
mw.log( 'Entity ' .. entityId .. ' is not a person' ); | |||
return personName; | |||
end | end | ||
if ( | if ( utils.isEmpty( personName ) ) then | ||
return '\'\'(not translated to ' .. context.lang .. ')\'\''; | |||
else | |||
return options.format( personName ); | |||
end | end | ||
end | end | ||
function | local function getPersonNameAsWikitext( context, entityId, customLabel, options ) | ||
if ( | local personName = getPersonNameAsLabel( context, entityId, customLabel, options); | ||
return; | if ( personName == nil ) then | ||
return nil; | |||
end | end | ||
local link = utils.getElementLink( context, entityId, nil ); | |||
return utils.wrapInUrl( link, personName ); | |||
return | |||
end | end | ||
function | local function getPeopleAsWikitext( context, value, options ) | ||
if | if type( value ) == 'string' then | ||
return options.format( value ) | return options.format( value ) | ||
elseif | elseif type( value ) == 'table' then | ||
if | if value.id then | ||
-- this is link | -- this is link | ||
if | if options.preferids then | ||
return | return value.id | ||
else | else | ||
if | if options.nolinks then | ||
return | return getPersonNameAsLabel( context, value.id, value.label, options ) | ||
else | else | ||
return | return getPersonNameAsWikitext( context, value.id, value.label, options ) | ||
end | |||
end | |||
end | |||
local maxAuthors = 10 -- need some restrictions, as some publications have enormous amount of authors (e.g. 115 authors of Q68951544) | |||
local resultList = {} | |||
for i, tableValue in pairs( value ) do | |||
local nextWikitext = getPeopleAsWikitext( context, tableValue, options ) | |||
if not utils.isEmpty( nextWikitext ) then | |||
table.insert( resultList, nextWikitext ) | |||
if #resultList == maxAuthors + 1 then | |||
-- keep one more to indicate that there are too many | |||
break | |||
end | end | ||
end | end | ||
end | end | ||
local | local resultWikitext = '' | ||
for i, | for i, wikitext in pairs( resultList ) do | ||
if i == maxAuthors + 1 then | |||
resultWikitext = resultWikitext .. ( i18nEtAl[ context.lang ] or i18nEtAlDefault ) | |||
break; | |||
end | |||
if i ~= 1 then | |||
resultWikitext = resultWikitext .. ', ' | |||
end | |||
resultWikitext = resultWikitext .. wikitext | |||
end | end | ||
return | return resultWikitext | ||
end | end | ||
return '' | return options.format( '(unknown type)' ) | ||
end | end | ||
function | local function generateAuthorLinks(context, src) | ||
if ( | local result = '' | ||
if src.author then | |||
result = getPeopleAsWikitext( context, src.author, options_commas_authors ) | |||
result = '<i class="wef_low_priority_links">' .. result .. '</i> ' | |||
end | end | ||
return result | |||
return | |||
end | end | ||
local function appendProperty(result, context, src, conjunctor, property, url) | |||
function | if src[property] then | ||
if | if url and src[url] then | ||
result = result .. conjunctor .. utils.wrapInUrl( src[url], utils.toString( context, src[property], options_commas_nolinks ) ) | |||
else | |||
result = result .. conjunctor .. utils.toString( context, src[property], options_commas ) | |||
end | end | ||
end | |||
return result | |||
end | |||
local function appendTitle(result, context, src) | |||
conjunctor = '' | |||
if src.part then | |||
result = appendProperty(result, context, src, '', 'part', 'parturl') | |||
conjunctor = ' // ' | |||
end | |||
result = appendProperty(result, context, src, conjunctor, 'title', 'url') | |||
return result | |||
end | |||
local function appendLanguage(result, context, src) | |||
if context.lang ~= i18nDefaultLanguage then | |||
local langs = require('Module:Languages') | |||
result = result .. langs.list_ref(p.currentFrame:newChild{ args = {context.lang} }) | |||
end | |||
return result | |||
end | |||
local function appendSubtitle(result, context, src) | |||
return appendProperty(result, context, src, ': ', 'subtitle') | |||
end | |||
local function appendOriginalTitle(result, context, src) | |||
return appendProperty(result, context, src, ' = ', 'originaltitle') | |||
end | |||
local function appendPublication(result, context, src) | |||
if ( | if src.publication then | ||
if type( src.publication.title or '') ~= 'string' then | |||
error('type of src.publication.title is not string but ' .. type( src.publication.title ) ) | |||
end | end | ||
result = result .. ' // ' .. utils.toString( context, src.publication, options_commas_it_short ) | |||
if src.publication.subtitle then | |||
if | result = result .. ': ' .. utils.toString( context, src.publication.subtitle, options_commas_it_short ) | ||
end | end | ||
end | end | ||
return result | |||
end | end | ||
function | local function appendEditor(result, context, src) | ||
if | if src.editor or src.translator then | ||
result = result .. ' / ' | |||
if src.editor then | |||
local prefix = i18nEditors[ context.lang ] or i18nEditors[ i18nDefaultLanguage ] | |||
result = result .. prefix .. getPeopleAsWikitext( context, src.editor, options_commas_responsible ) | |||
if src.translator then | |||
result = result .. ', ' | |||
end | end | ||
end | |||
if src.translator then | |||
local prefix = i18nTranslators[ context.lang ] or i18nTranslators[ i18nDefaultLanguage ] | |||
result = result .. prefix .. getPeopleAsWikitext( context, src.translator, options_commas_responsible ) | |||
end | end | ||
end | end | ||
return result | return result | ||
end | end | ||
function | local function appendEdition(result, context, src) | ||
return appendProperty(result, context, src, ' — ', 'edition') | |||
end | end | ||
function | local function appendPublicationData(result, context, src) | ||
if | if src.place or src.publisher or src.year then | ||
result = result .. ' — ' | |||
if | if src.place then | ||
result = result .. utils.toString( context, src.place, options_commas_short ) | |||
if src.publisher or src.year then | |||
result = result .. ': ' | |||
end | end | ||
end | end | ||
if src.publisher then | |||
result = result .. utils.toString( context, src.publisher, options_commas_short ) | |||
if src.year then | |||
result = result .. ', ' | |||
if | |||
end | end | ||
end | end | ||
if src.year then | |||
result = result .. utils.toString( context, src.year, options_commas ) | |||
end | |||
result = result .. '.'; | |||
end | end | ||
return result | |||
end | end | ||
function | local function appendVolumeAndIssue(result, context, src) | ||
if src.volume or src.issue then | |||
local | result = result .. ' — ' | ||
if ( | local letter_vol = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ] | ||
local letter_iss = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ] | |||
if src.volume then | |||
result = appendProperty(result, context, src, letter_vol .. ' ', 'volume') | |||
result = appendProperty(result, context, src, ', ' .. letter_iss .. ' ', 'issue') | |||
else | |||
result = appendProperty(result, context, src, letter_iss .. ' ', 'issue') | |||
end | end | ||
result = result .. '.' | |||
end | |||
return result | |||
end | end | ||
function | local function appendPages(result, context, src) | ||
local | if src.pages then | ||
local letter = i18nPages[ context.lang ] or i18nPages[ i18nDefaultLanguage ] | |||
local strPages = utils.toString( context, src.pages, options_commas ) | |||
strPages = mw.ustring.gsub( strPages, '[-—]', '—' ); | |||
result = result .. ' — ' .. letter .. ' ' .. strPages .. '.' | |||
end | |||
return result | |||
end | |||
local function appendNumberOfPages(result, context, src) | |||
if | if src.numberOfPages then | ||
local letter = i18nNumberOfPages[ context.lang ] or i18nNumberOfPages[ i18nDefaultLanguage ] | |||
result = appendProperty(result, context, src, ' — ', 'numberOfPages') .. ' ' .. letter | |||
end | end | ||
return result | |||
end | |||
if ( | local function appendBookSeries(result, context, src) | ||
if src.bookSeries then | |||
result = appendProperty(result, context, src, ' — (', 'bookSeries') | |||
if src.bookSeriesVolume or src.bookSeriesIssue then | |||
result = result .. '; ' | |||
local letter_vol = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ] | |||
local letter_iss = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ] | |||
if ( src.bookSeriesVolume ) then | |||
result = appendProperty(result, context, src, letter_vol .. ' ', 'bookSeriesVolume') | |||
result = appendProperty(result, context, src, ', ' .. letter_iss .. ' ', 'bookSeriesIssue') | |||
else | |||
result = appendProperty(result, context, src, letter_iss .. ' ', 'bookSeriesIssue') | |||
end | |||
end | |||
result = result .. ')' | |||
end | end | ||
return result | |||
end | end | ||
function | local function appendTirage(result, context, src) | ||
if | if src.tirage then | ||
local tirageTemplate = i18nTirage[ context.lang ] or i18nTirage[ i18nDefaultLanguage ] | |||
result = result .. ' — ' .. utils.toString( context, src.tirage, { separator = '; ', conjunction = '; ', format = function( data ) return mw.ustring.format(tirageTemplate, data) end } ) | |||
end | end | ||
local | return result | ||
if ( | end | ||
end | local function appendIdentifiers(result, context, src) | ||
return | if src.isbn then result = result .. ' — ISBN ' .. utils.toString( context, src.isbn, options_commas ) end | ||
if src.issn then result = result .. ' — ISSN ' .. utils.toString( context, src.issn, options_issn ) end | |||
if src.doi then result = result .. ' — ' .. utils.toString( context, src.doi, options_doi ) end | |||
if src.pmid then result = result .. ' — ' .. utils.toString( context, src.pmid, options_pmid ) end | |||
if src.arxiv then result = result .. ' — ' .. utils.toString( context, src.arxiv, options_arxiv ) end | |||
return result | |||
end | end | ||
function | local function appendSourceId(result, context, src) | ||
local | if src.sourceId then | ||
local citetype = src.type and utils.toString(context, src.type, options_citetypes) or 'citetype_unknown' | |||
result = '<span class="wikidata_cite ' .. citetype .. '" data-entity-id="' .. utils.getSingle(src.sourceId) .. '">' .. result .. '</span>' | |||
end | end | ||
return result | |||
end | end | ||
function | local function appendAccessDate(result, context, src) | ||
if src.accessdate then | |||
local date = utils.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) | |||
local date_str = (d > 0 and ' ' .. tostring(d) or '') | |||
.. (m > 0 and ' ' .. monthg[m] or '') | |||
.. (y > 0 and ' ' .. tostring(y) or '') | |||
result = result .. " <small>Проверено" .. date_str .. ".</small>" | |||
end | end | ||
return result | |||
end | |||
local function populateUrl(context, src) | |||
if ( | if src.sourceId and not src.url then | ||
if | local entity = utils.getEntity(context, src.sourceId) | ||
if entity.sitelinks and entity.sitelinks[context.lang .. 'wikisource'] then | |||
src.url = ':' .. context.lang .. ':s:' .. entity.sitelinks[context.lang .. 'wikisource'].title | |||
end | end | ||
end | end | ||
end | end | ||
function | local function populateYear(src) | ||
if not src.year and src.dateOfPublication then | |||
if | local date = utils.getSingle(src.dateOfPublication) | ||
local | src.year = mw.ustring.sub(date, 2, 5) | ||
src. | |||
end | end | ||
if not src.year and src.dateOfCreation then | |||
local date = utils.getSingle(src.dateOfCreation) | |||
src.year = mw.ustring.sub(date, 2, 5) | |||
end | |||
end | |||
local function populateTitle(src) | |||
src.title = src.title or utils.getSingle(src.url) or '\'\'(unspecified title)\'\'' | |||
end | end | ||
function | local function renderSource(context, src) | ||
local | options_commas_authors.format = personNameToAuthorName | ||
options_commas_responsible.format = personNameToResponsibleName | |||
context.lang = utils.getLangCode(utils.getSingle(src.lang)) or i18nDefaultLanguage | |||
utils.preprocessPlaces(src, context.lang) | |||
populateUrl(context, src) | |||
populateTitle(src) | |||
populateYear(src) | |||
local result = generateAuthorLinks(context, src) | |||
result = appendTitle(result, context, src) | |||
result = appendLanguage(result, context, src) | |||
result = appendSubtitle(result, context, src) | |||
result = appendOriginalTitle(result, context, src) | |||
result = appendPublication(result, context, src) | |||
result = result .. '<span class="wef_low_priority_links">' | |||
result = appendEditor(result, context, src) -- Might take current editor instead of actual. Use with caution | |||
result = appendEdition(result, context, src) | |||
result = appendPublicationData(result, context, src) | |||
result = appendVolumeAndIssue(result, context, src) | |||
result = appendPages(result, context, src) | |||
result = appendNumberOfPages(result, context, src) | |||
result = appendBookSeries(result, context, src) | |||
result = appendTirage(result, context, src) | |||
result = appendIdentifiers(result, context, src) | |||
result = appendSourceId(result, context, src) | |||
result = appendAccessDate(result, context, src) | |||
result = result .. '</span>' | |||
return result | |||
return | |||
end | end | ||
function | local function renderReferenceImpl(currentEntity, reference, refAnchor, refAnchorYear) | ||
if not reference.snaks then | |||
return nil | |||
if | |||
end | end | ||
local | -- контекст, содержит также кеш элементов | ||
local context = { | |||
cache = {} | |||
} | |||
if ( | -- данные в простом формате, согласованном с модулями формирования библиографического описания | ||
local data = {} | |||
-- забрать данные из reference | |||
utils.populateDataFromClaims(context, nil, reference.snaks, data) | |||
utils.expandSpecials(context, currentEntity, reference, data) | |||
local sourceEntity = nil | |||
if data.sourceId then | |||
sourceEntity = utils.getEntity(context, data.sourceId) | |||
if sourceEntity then | |||
utils.populateSourceDataImpl(context, sourceEntity, data) | |||
end | |||
end | end | ||
if data.publication then | |||
utils.expandPublication(context, sourceEntity, data) | |||
end | |||
if ( | utils.expandBookSeries(context, data) | ||
if next(data) == nil then | |||
return nil | |||
end | end | ||
return | local rendered = renderSource(context, data) | ||
end | if mw.ustring.len(rendered) == 0 then | ||
return nil | |||
end | |||
if refAnchor then | |||
if ( | local anchorValue = 'CITEREF' .. refAnchor .. (utils.coalesce(refAnchorYear, data.year) or '') | ||
rendered = '<span class="citation" id="' .. mw.uri.anchorEncode(anchorValue) .. '">' .. rendered .. '</span>' | |||
end | end | ||
local | return rendered | ||
end | |||
if | |||
entityId = | local function artificialSnaks(args) | ||
local snaks = {} | |||
if args[1] then | |||
entityId = mw.text.trim(args[1]) | |||
snaks.P248 = {utils.toWikibaseEntityIdSnak("P248", entityId)} | |||
snaks.P805 = {utils.toWikibaseEntityIdSnak("P805", entityId)} | |||
end | end | ||
utils.copyArgsToSnaks(args, snaks) | |||
return mw.wikibase.getEntity(), {snaks = snaks} | |||
end | |||
function p.renderReference(frame, currentEntity, reference) | |||
p.currentFrame = frame | |||
-- template call | |||
if | if frame and not currentEntity and not reference then | ||
currentEntity, reference = artificialSnaks(frame.args) | |||
end | end | ||
local rendered = renderReferenceImpl(currentEntity, reference) | |||
return | if not rendered then | ||
return '' | |||
end | end | ||
-- Про выбор алгоритма хеширования см. [[Модуль:Hash]]. Знак подчёркивания в начале позволяет | |||
-- исключить ошибку, когда имя сноски — чисто числовое значение, каковыми иногда бывают хеши. | |||
return frame:extensionTag('ref', rendered, {name = '_' .. mw.hash.hashValue('fnv164', rendered)}) .. '[[Category:Википедия:Статьи с источниками из Викиданных]]' | |||
end | |||
function p.renderSource(frame) | |||
return | p.currentFrame = frame | ||
currentEntity, reference = artificialSnaks(frame.args) | |||
return renderReferenceImpl(currentEntity, reference, frame.args['ref'], frame.args['ref-year']) | |||
end | end | ||
return p; | return p; |
Версия от 23:55, 8 ноября 2020
Генерирует сноски и ссылки на источники.
local p = {}
local utils = require('Module:Sources/utils')
local i18nDefaultLanguage = 'ru'
local i18nEtAlDefault = ' et al.'
local i18nEtAl = {
ru = ' и др.',
}
local i18nEditors = {
fr = '',
de = 'Hrsg.: ',
es = '',
en = '',
it = '',
ru = 'под ред. ',
}
local i18nTranslators = {
fr = '',
de = '',
es = '',
en = '',
it = '',
ru = 'пер. ',
}
local i18nVolume = {
de = 'Vol.',
fr = 'Vol.',
es = 'Vol.',
en = 'Vol.',
it = 'Vol.',
ru = 'Т.',
}
local i18nIssue = {
en = 'Iss.',
ru = 'вып.',
}
local i18nPages = {
fr = 'P.',
de = 'S.',
es = 'P.',
en = 'P.',
it = 'P.',
ru = 'С.',
}
local i18nNumberOfPages = {
en = 'p.',
ru = 'с.',
}
local i18nTirage = {
en = 'ed. size: %d',
ru = '%d экз.',
}
local monthg = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', "сентября", "октября", "ноября", "декабря"}
local PREFIX_CITEREF = "CITEREF_";
-- Returns formatted pair {Family name(s), First name(s)}
local function tokenizeName( fullName )
local start = '^%s*' -- matches beginning of the string + arbitrary number of spaces
local finish = '%s*$' -- matches end of the string + arbitrary number of spaces
local comma = '\,%s+' -- matches comma + single or more spacing character
local space = '%s+' -- matches single or more spacing character
local name = '(%a[%a\-\']*)\.?' -- matches single name, have to start with letter, can contain apostrophe and hyphen, may end with dot
local surname = '(%a[%a\-\']*)' -- same as name, but can't end with dot
local f, i = mw.ustring.match(fullName, start .. surname .. comma .. name .. finish)
if f then
mw.log('tokenizeName: «' .. fullName .. '»: have «Fa, Im» match')
return {f, mw.ustring.sub( i, 1, 1 ) .. '.'}
end
local f, i, o = mw.ustring.match(fullName, start .. surname .. comma .. name .. space .. name .. finish)
if f then
mw.log( 'tokenizeName: «' .. fullName .. '»: have «Fa, Im Ot» match')
return {f, mw.ustring.sub( i, 1, 1 ) .. '. '
.. mw.ustring.sub( o, 1, 1 ) .. '.'}
end
local f1, f2, i = mw.ustring.match(fullName, start .. surname .. space .. surname .. comma .. name .. finish)
if f1 then
mw.log('tokenizeName: «' .. fullName .. '»: have «Fa Fa, Im» match')
return {f1 .. ' ' .. f2, mw.ustring.sub( i, 1, 1 ) .. '.'}
end
local i, o, f = mw.ustring.match(fullName, start .. name .. space .. name .. space .. 'оглы' .. space .. surname .. finish)
if f then
mw.log('tokenizeName: «' .. fullName .. '»: have «Im Ot оглы Fa» match')
return {f, mw.ustring.sub(i, 1, 1) .. '. ' .. mw.ustring.sub(o, 1, 1) .. '.'}
end
local i1, i2, f = mw.ustring.match(fullName, start .. name .. space .. name .. space .. 'de' .. space .. surname .. finish)
if f then
mw.log('tokenizeName: «' .. fullName .. '»: have «Im Im de Fa» match')
return {f, mw.ustring.sub( i1, 1, 1 ) .. '. ' .. mw.ustring.sub( i2, 1, 1 ) .. '.'}
end
-- Try matching k names + surname
for k = 1, 4 do
pattern = start .. string.rep(name .. space, k) .. surname .. finish
matched = {mw.ustring.match(fullName, pattern)}
if #matched ~= 0 then
mw.log('tokenizeName: «' .. fullName .. '»: have «Im (x' .. k .. ') Fa» match')
for i = 1, k do
matched[i] = mw.ustring.sub(matched[i], 1, 1)
end
return {matched[k + 1], table.concat(matched, '. ', 1, k) .. '.'}
end
end
mw.log('Unmatched any pattern: «' .. fullName .. '»')
return {fullName}
end
local function personNameToAuthorName( fullName )
if not fullName then return fullName end
local tokenized = tokenizeName(fullName)
if #tokenized == 1 then
return tokenized[1]
else
return tokenized[1] .. ' ' .. tokenized[2]
end
end
local function personNameToResponsibleName( fullName )
if not fullName then return fullName end
local tokenized = tokenizeName(fullName)
if #tokenized == 1 then
return tokenized[1]
else
return tokenized[2] .. ' ' .. tokenized[1]
end
end
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_short = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false, short = true };
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 };
local options_commas_authors = { separator = ', ', conjunction = ', ', format = personNameToAuthorName, nolinks = false, preferids = false };
local options_commas_responsible = { separator = ', ', conjunction = ', ', format = personNameToResponsibleName, nolinks = false, preferids = false };
local options_arxiv = { separator = '; ', conjunction = '; ', format = function( id ) return '[http://arxiv.org/abs/' .. id .. ' arXiv:' .. id .. ']' end, nolinks = true, preferids = false };
local options_doi = { separator = '; ', conjunction = '; ', format = function( doi ) return '[http://dx.doi.org/' .. doi .. ' doi:' .. doi .. ']' end, nolinks = true, preferids = false };
local options_issn = { separator = '; ', conjunction = '; ', format = function( issn ) return '[https://www.worldcat.org/issn/' .. issn .. ' ' .. issn .. ']' end, nolinks = true, preferids = false };
local options_pmid = { separator = '; ', conjunction = '; ', format = function( pmid ) return '[https://www.ncbi.nlm.nih.gov/pubmed/?term=' .. pmid .. ' PMID:' .. pmid .. ']' end, nolinks = true, preferids = false };
local function getPersonNameAsLabel( context, entityId, providedLabel, options )
-- would custom label provided we don't need to check entity at all
if ( not utils.isEmpty( providedLabel ) ) then
mw.log( 'Custom label provided for ' .. entityId );
return options.format( providedLabel );
end
local entity = utils.getEntity( context, entityId );
if ( not entity ) then return '\'\'(entity ' .. entityId .. ' is missing)\'\'' end;
local personName = nil;
-- support only labels so far
if ( entity.labels[ context.lang ] ) then
personName = entity.labels[ context.lang ].value;
mw.log('Got person name of ' .. entityId .. ' from label: «' .. personName .. '»' )
end
if ( not utils.isInstanceOf( entity, 'Q5' ) ) then
mw.log( 'Entity ' .. entityId .. ' is not a person' );
return personName;
end
if ( utils.isEmpty( personName ) ) then
return '\'\'(not translated to ' .. context.lang .. ')\'\'';
else
return options.format( personName );
end
end
local function getPersonNameAsWikitext( context, entityId, customLabel, options )
local personName = getPersonNameAsLabel( context, entityId, customLabel, options);
if ( personName == nil ) then
return nil;
end
local link = utils.getElementLink( context, entityId, nil );
return utils.wrapInUrl( link, personName );
end
local function getPeopleAsWikitext( 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 value.id
else
if options.nolinks then
return getPersonNameAsLabel( context, value.id, value.label, options )
else
return getPersonNameAsWikitext( context, value.id, value.label, options )
end
end
end
local maxAuthors = 10 -- need some restrictions, as some publications have enormous amount of authors (e.g. 115 authors of Q68951544)
local resultList = {}
for i, tableValue in pairs( value ) do
local nextWikitext = getPeopleAsWikitext( context, tableValue, options )
if not utils.isEmpty( nextWikitext ) then
table.insert( resultList, nextWikitext )
if #resultList == maxAuthors + 1 then
-- keep one more to indicate that there are too many
break
end
end
end
local resultWikitext = ''
for i, wikitext in pairs( resultList ) do
if i == maxAuthors + 1 then
resultWikitext = resultWikitext .. ( i18nEtAl[ context.lang ] or i18nEtAlDefault )
break;
end
if i ~= 1 then
resultWikitext = resultWikitext .. ', '
end
resultWikitext = resultWikitext .. wikitext
end
return resultWikitext
end
return options.format( '(unknown type)' )
end
local function generateAuthorLinks(context, src)
local result = ''
if src.author then
result = getPeopleAsWikitext( context, src.author, options_commas_authors )
result = '<i class="wef_low_priority_links">' .. result .. '</i> '
end
return result
end
local function appendProperty(result, context, src, conjunctor, property, url)
if src[property] then
if url and src[url] then
result = result .. conjunctor .. utils.wrapInUrl( src[url], utils.toString( context, src[property], options_commas_nolinks ) )
else
result = result .. conjunctor .. utils.toString( context, src[property], options_commas )
end
end
return result
end
local function appendTitle(result, context, src)
conjunctor = ''
if src.part then
result = appendProperty(result, context, src, '', 'part', 'parturl')
conjunctor = ' // '
end
result = appendProperty(result, context, src, conjunctor, 'title', 'url')
return result
end
local function appendLanguage(result, context, src)
if context.lang ~= i18nDefaultLanguage then
local langs = require('Module:Languages')
result = result .. langs.list_ref(p.currentFrame:newChild{ args = {context.lang} })
end
return result
end
local function appendSubtitle(result, context, src)
return appendProperty(result, context, src, ': ', 'subtitle')
end
local function appendOriginalTitle(result, context, src)
return appendProperty(result, context, src, ' = ', 'originaltitle')
end
local function appendPublication(result, context, src)
if src.publication then
if type( src.publication.title or '') ~= 'string' then
error('type of src.publication.title is not string but ' .. type( src.publication.title ) )
end
result = result .. ' // ' .. utils.toString( context, src.publication, options_commas_it_short )
if src.publication.subtitle then
result = result .. ': ' .. utils.toString( context, src.publication.subtitle, options_commas_it_short )
end
end
return result
end
local function appendEditor(result, context, src)
if src.editor or src.translator then
result = result .. ' / '
if src.editor then
local prefix = i18nEditors[ context.lang ] or i18nEditors[ i18nDefaultLanguage ]
result = result .. prefix .. getPeopleAsWikitext( context, src.editor, options_commas_responsible )
if src.translator then
result = result .. ', '
end
end
if src.translator then
local prefix = i18nTranslators[ context.lang ] or i18nTranslators[ i18nDefaultLanguage ]
result = result .. prefix .. getPeopleAsWikitext( context, src.translator, options_commas_responsible )
end
end
return result
end
local function appendEdition(result, context, src)
return appendProperty(result, context, src, ' — ', 'edition')
end
local function appendPublicationData(result, context, src)
if src.place or src.publisher or src.year then
result = result .. ' — '
if src.place then
result = result .. utils.toString( context, src.place, options_commas_short )
if src.publisher or src.year then
result = result .. ': '
end
end
if src.publisher then
result = result .. utils.toString( context, src.publisher, options_commas_short )
if src.year then
result = result .. ', '
end
end
if src.year then
result = result .. utils.toString( context, src.year, options_commas )
end
result = result .. '.';
end
return result
end
local function appendVolumeAndIssue(result, context, src)
if src.volume or src.issue then
result = result .. ' — '
local letter_vol = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]
local letter_iss = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ]
if src.volume then
result = appendProperty(result, context, src, letter_vol .. ' ', 'volume')
result = appendProperty(result, context, src, ', ' .. letter_iss .. ' ', 'issue')
else
result = appendProperty(result, context, src, letter_iss .. ' ', 'issue')
end
result = result .. '.'
end
return result
end
local function appendPages(result, context, src)
if src.pages then
local letter = i18nPages[ context.lang ] or i18nPages[ i18nDefaultLanguage ]
local strPages = utils.toString( context, src.pages, options_commas )
strPages = mw.ustring.gsub( strPages, '[-—]', '—' );
result = result .. ' — ' .. letter .. ' ' .. strPages .. '.'
end
return result
end
local function appendNumberOfPages(result, context, src)
if src.numberOfPages then
local letter = i18nNumberOfPages[ context.lang ] or i18nNumberOfPages[ i18nDefaultLanguage ]
result = appendProperty(result, context, src, ' — ', 'numberOfPages') .. ' ' .. letter
end
return result
end
local function appendBookSeries(result, context, src)
if src.bookSeries then
result = appendProperty(result, context, src, ' — (', 'bookSeries')
if src.bookSeriesVolume or src.bookSeriesIssue then
result = result .. '; '
local letter_vol = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]
local letter_iss = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ]
if ( src.bookSeriesVolume ) then
result = appendProperty(result, context, src, letter_vol .. ' ', 'bookSeriesVolume')
result = appendProperty(result, context, src, ', ' .. letter_iss .. ' ', 'bookSeriesIssue')
else
result = appendProperty(result, context, src, letter_iss .. ' ', 'bookSeriesIssue')
end
end
result = result .. ')'
end
return result
end
local function appendTirage(result, context, src)
if src.tirage then
local tirageTemplate = i18nTirage[ context.lang ] or i18nTirage[ i18nDefaultLanguage ]
result = result .. ' — ' .. utils.toString( context, src.tirage, { separator = '; ', conjunction = '; ', format = function( data ) return mw.ustring.format(tirageTemplate, data) end } )
end
return result
end
local function appendIdentifiers(result, context, src)
if src.isbn then result = result .. ' — ISBN ' .. utils.toString( context, src.isbn, options_commas ) end
if src.issn then result = result .. ' — ISSN ' .. utils.toString( context, src.issn, options_issn ) end
if src.doi then result = result .. ' — ' .. utils.toString( context, src.doi, options_doi ) end
if src.pmid then result = result .. ' — ' .. utils.toString( context, src.pmid, options_pmid ) end
if src.arxiv then result = result .. ' — ' .. utils.toString( context, src.arxiv, options_arxiv ) end
return result
end
local function appendSourceId(result, context, src)
if src.sourceId then
local citetype = src.type and utils.toString(context, src.type, options_citetypes) or 'citetype_unknown'
result = '<span class="wikidata_cite ' .. citetype .. '" data-entity-id="' .. utils.getSingle(src.sourceId) .. '">' .. result .. '</span>'
end
return result
end
local function appendAccessDate(result, context, src)
if src.accessdate then
local date = utils.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)
local date_str = (d > 0 and ' ' .. tostring(d) or '')
.. (m > 0 and ' ' .. monthg[m] or '')
.. (y > 0 and ' ' .. tostring(y) or '')
result = result .. " <small>Проверено" .. date_str .. ".</small>"
end
return result
end
local function populateUrl(context, src)
if src.sourceId and not src.url then
local entity = utils.getEntity(context, src.sourceId)
if entity.sitelinks and entity.sitelinks[context.lang .. 'wikisource'] then
src.url = ':' .. context.lang .. ':s:' .. entity.sitelinks[context.lang .. 'wikisource'].title
end
end
end
local function populateYear(src)
if not src.year and src.dateOfPublication then
local date = utils.getSingle(src.dateOfPublication)
src.year = mw.ustring.sub(date, 2, 5)
end
if not src.year and src.dateOfCreation then
local date = utils.getSingle(src.dateOfCreation)
src.year = mw.ustring.sub(date, 2, 5)
end
end
local function populateTitle(src)
src.title = src.title or utils.getSingle(src.url) or '\'\'(unspecified title)\'\''
end
local function renderSource(context, src)
options_commas_authors.format = personNameToAuthorName
options_commas_responsible.format = personNameToResponsibleName
context.lang = utils.getLangCode(utils.getSingle(src.lang)) or i18nDefaultLanguage
utils.preprocessPlaces(src, context.lang)
populateUrl(context, src)
populateTitle(src)
populateYear(src)
local result = generateAuthorLinks(context, src)
result = appendTitle(result, context, src)
result = appendLanguage(result, context, src)
result = appendSubtitle(result, context, src)
result = appendOriginalTitle(result, context, src)
result = appendPublication(result, context, src)
result = result .. '<span class="wef_low_priority_links">'
result = appendEditor(result, context, src) -- Might take current editor instead of actual. Use with caution
result = appendEdition(result, context, src)
result = appendPublicationData(result, context, src)
result = appendVolumeAndIssue(result, context, src)
result = appendPages(result, context, src)
result = appendNumberOfPages(result, context, src)
result = appendBookSeries(result, context, src)
result = appendTirage(result, context, src)
result = appendIdentifiers(result, context, src)
result = appendSourceId(result, context, src)
result = appendAccessDate(result, context, src)
result = result .. '</span>'
return result
end
local function renderReferenceImpl(currentEntity, reference, refAnchor, refAnchorYear)
if not reference.snaks then
return nil
end
-- контекст, содержит также кеш элементов
local context = {
cache = {}
}
-- данные в простом формате, согласованном с модулями формирования библиографического описания
local data = {}
-- забрать данные из reference
utils.populateDataFromClaims(context, nil, reference.snaks, data)
utils.expandSpecials(context, currentEntity, reference, data)
local sourceEntity = nil
if data.sourceId then
sourceEntity = utils.getEntity(context, data.sourceId)
if sourceEntity then
utils.populateSourceDataImpl(context, sourceEntity, data)
end
end
if data.publication then
utils.expandPublication(context, sourceEntity, data)
end
utils.expandBookSeries(context, data)
if next(data) == nil then
return nil
end
local rendered = renderSource(context, data)
if mw.ustring.len(rendered) == 0 then
return nil
end
if refAnchor then
local anchorValue = 'CITEREF' .. refAnchor .. (utils.coalesce(refAnchorYear, data.year) or '')
rendered = '<span class="citation" id="' .. mw.uri.anchorEncode(anchorValue) .. '">' .. rendered .. '</span>'
end
return rendered
end
local function artificialSnaks(args)
local snaks = {}
if args[1] then
entityId = mw.text.trim(args[1])
snaks.P248 = {utils.toWikibaseEntityIdSnak("P248", entityId)}
snaks.P805 = {utils.toWikibaseEntityIdSnak("P805", entityId)}
end
utils.copyArgsToSnaks(args, snaks)
return mw.wikibase.getEntity(), {snaks = snaks}
end
function p.renderReference(frame, currentEntity, reference)
p.currentFrame = frame
-- template call
if frame and not currentEntity and not reference then
currentEntity, reference = artificialSnaks(frame.args)
end
local rendered = renderReferenceImpl(currentEntity, reference)
if not rendered then
return ''
end
-- Про выбор алгоритма хеширования см. [[Модуль:Hash]]. Знак подчёркивания в начале позволяет
-- исключить ошибку, когда имя сноски — чисто числовое значение, каковыми иногда бывают хеши.
return frame:extensionTag('ref', rendered, {name = '_' .. mw.hash.hashValue('fnv164', rendered)}) .. '[[Category:Википедия:Статьи с источниками из Викиданных]]'
end
function p.renderSource(frame)
p.currentFrame = frame
currentEntity, reference = artificialSnaks(frame.args)
return renderReferenceImpl(currentEntity, reference, frame.args['ref'], frame.args['ref-year'])
end
return p;