"Module:Wikidata" के अवतरणों में अंतर

मुक्त ज्ञानकोश विकिपीडिया से
नेविगेशन पर जाएँ खोज पर जाएँ
https://hiwiki.iiit.ac.in/index.php?title=>Uzume
(missed one)
https://hiwiki.iiit.ac.in/index.php/>Pols12
(fixes getEquivalentWPArticle (workaround for T14974 (output can’t start with a colon); fixes targetLang param forwarding))
पंक्ति १: पंक्ति १:
-- vim: set noexpandtab ft=lua ts=4 sw=4:
--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua
require('Module:No globals')


local p = {}
local p = {}
local debug = false


local linguistic = require('Module:Linguistic')
--local formatDate = require('Module:Complex date') only loaded when needed to save memory in large pages like Wikidata:List of properties/all
local fb = require('Module:Fallback')
local i18nmessages = mw.loadData('Module:i18n/wikidata')


------------------------------------------------------------------------------
-- Wiki-specific parameters
-- module local variables and functions
local defaultlang = mw.getCurrentFrame():preprocess("{{int:lang}}")
local defaultlink = 'wikidata'


local wiki =
local function i18n(str)
{
local message = i18nmessages[str]
langcode = mw.language.getContentLanguage().code
if type(message) == 'string' then
}
return message
end
return fb._langSwitch(message, defaultlang) .. ''
end


-- internationalisation
local function formatError( key, text )
local i18n =
return error(i18n(key) .. (text or ''))
{
end
["errors"] =
{
["property-not-found"] = "Property not found.",
["entity-not-found"] = "Wikidata entity not found.",
["unknown-claim-type"] = "Unknown claim type.",
["unknown-entity-type"] = "Unknown entity type.",
["qualifier-not-found"] = "Qualifier not found.",
["site-not-found"] = "Wikimedia project not found.",
["unknown-datetime-format"] = "Unknown datetime format.",
["local-article-not-found"] = "Article is not yet available in this wiki."
},
["datetime"] =
{
-- $1 is a placeholder for the actual number
[0] = "$1 billion years", -- precision: billion years
[1] = "$100 million years", -- precision: hundred million years
[2] = "$10 million years", -- precision: ten million years
[3] = "$1 million years", -- precision: million years
[4] = "$100,000 years", -- precision: hundred thousand years
[5] = "$10,000 years", -- precision: ten thousand years
[6] = "$1 millennium", -- precision: millennium
[7] = "$1 century", -- precision: century
[8] = "$1s", -- precision: decade
-- the following use the format of #time parser function
[9]  = "Y", -- precision: year,
[10] = "F Y", -- precision: month
[11] = "F j, Y", -- precision: day
[12] = "F j, Y ga", -- precision: hour
[13] = "F j, Y g:ia", -- precision: minute
[14] = "F j, Y g:i:sa", -- precision: second
["beforenow"] = "$1 BCE", -- how to format negative numbers for precisions 0 to 5
["afternow"] = "$1 CE", -- how to format positive numbers for precisions 0 to 5
["bc"] = '$1 "BCE"', -- how print negative years
["ad"] = "$1", -- how print positive years
-- the following are for function getDateValue() and getQualifierDateValue()
["default-format"] = "dmy", -- default value of the #3 (getDateValue) or
-- #4 (getQualifierDateValue) argument
["default-addon"] = "BC", -- default value of the #4 (getDateValue) or
-- #5 (getQualifierDateValue) argument
["prefix-addon"] = false, -- set to true for languages put "BC" in front of the
-- datetime string; or the addon will be suffixed
["addon-sep"] = " ", -- separator between datetime string and addon (or inverse)
["format"] = -- options of the 3rd argument
{
["mdy"] = "F j, Y",
["my"] = "F Y",
["y"] = "Y",
["dmy"] = "j F Y",
["ymd"] = "Y-m-d",
["ym"] = "Y-m"
}
},
["monolingualtext"] = '<span lang="%language">%text</span>',
["warnDump"] = "[[Category:Called function 'Dump' from module Wikidata]]",
["ordinal"] =
{
[1] = "st",
[2] = "nd",
[3] = "rd",
["default"] = "th"
}
}


if wiki.langcode ~= "en" then
local function addTrackingCat(prop, cat)
--require("Module:i18n").loadI18n("Module:Wikidata/i18n", i18n)
if not prop and not cat then
-- got idea from [[:w:Module:Wd]]
return error("no property provided")
local module_title; if ... == nil then
end
module_title = mw.getCurrentFrame():getTitle()
if not cat then
else
cat = i18nmessages.trackingcat .. '/' .. string.upper(prop)
module_title = ...
end
end
require('Module:i18n').loadI18n(module_title..'/i18n', i18n)
return '[[Category:' .. cat .. ']]'
end
end


-- this function needs to be internationalised along with the above:
local function removeBlanks(args)
-- takes cardinal numer as a numeric and returns the ordinal as a string
for i, j in pairs(args) do -- does not work ??
-- we need three exceptions in English for 1st, 2nd, 3rd, 21st, .. 31st, etc.
if (j == '') or (j == '-') then args[i] = nil end
local function makeOrdinal (cardinal)
local ordsuffix = i18n.ordinal.default
if cardinal % 10 == 1 then
ordsuffix = i18n.ordinal[1]
elseif cardinal % 10 == 2 then
ordsuffix = i18n.ordinal[2]
elseif cardinal % 10 == 3 then
ordsuffix = i18n.ordinal[3]
end
end
-- In English, 1, 21, 31, etc. use 'st', but 11, 111, etc. use 'th'
return args
-- similarly for 12 and 13, etc.
end
if (cardinal % 100 == 11) or (cardinal % 100 == 12) or (cardinal % 100 == 13) then
 
ordsuffix = i18n.ordinal.default
local function formatTheUnknown() -- voir si on peut accorder/adapter l'usage de "inconnu"
end
return i18n('somevalue')
return tostring(cardinal) .. ordsuffix
end
 
local function isSpecial(snak)
return snak.snaktype ~= 'value'
end
end


local function printError(code)
local function sameValue(snak, target)
return '<span class="error">' .. (i18n.errors[code] or code) .. '</span>'
return not isSpecial(snak) and p.getRawvalue(snak) == target
end
end
local function parseDateFormat(f, timestamp, addon, prefix_addon, addon_sep)  
 
local year_suffix
local function showLang(statement, str) -- TODO (not yet in proper format)
local tstr = ""
--adds a lang indication at the start of the string, based on data in statement
local lang_obj = mw.language.new(wiki.langcode)
local mainsnak = statement.mainsnak
local f_parts = mw.text.split(f, 'Y', true)
if isSpecial(mainsnak) then
for idx, f_part in pairs(f_parts) do
return str
year_suffix = ''
end
if string.match(f_part, "x[mijkot]$") then
 
-- for non-Gregorian year
local langlist = {}
f_part = f_part .. 'Y'
if mainsnak.datavalue.type == 'monolingualtext' then
elseif idx < #f_parts then
langlist = {mainsnak.datavalue.value.language}
-- supress leading zeros in year
elseif statement.qualifiers and statement.qualifiers.P407 then
year_suffix = lang_obj:formatDate('Y', timestamp)
local convertlangcode = mw.loadData('Module:Dictionary/lang codes')
year_suffix = string.gsub(year_suffix, '^0+', '', 1)
for i, j in pairs( statement.qualifiers.P407 ) do
if not isSpecial(j) then
local val = convertlangcode[j.datavalue.value['numeric-id']]
table.insert(langlist, val)
end
end
end
tstr = tstr .. lang_obj:formatDate(f_part, timestamp) .. year_suffix
end
end
if addon ~= "" and prefix_addon then
if #langlist == 0 then
return addon .. addon_sep .. tstr
return str
elseif addon ~= "" then
return tstr .. addon_sep .. addon
else
else
return tstr
return '(' .. table.concat(langlist) .. ')' .. str
end
end
end
end
local function parseDateValue(timestamp, date_format, date_addon)
local prefix_addon = i18n["datetime"]["prefix-addon"]
local addon_sep = i18n["datetime"]["addon-sep"]
local addon = ""


-- check for negative date
function p.getEntity( val )
if string.sub(timestamp, 1, 1) == '-' then
if type(val) == 'table' then
timestamp = '+' .. string.sub(timestamp, 2)
return val
addon = date_addon
end
local _date_format = i18n["datetime"]["format"][date_format]
if _date_format ~= nil then
return parseDateFormat(_date_format, timestamp, addon, prefix_addon, addon_sep)
else
return printError("unknown-datetime-format")
end
end
return mw.wikibase.getEntityObject(val)
end
end


-- This local function combines the year/month/day/BC/BCE handling of parseDateValue{}
-- DATE FUNCTIONS
-- with the millennium/century/decade handling of formatDate()
local function splitTimestamp(timestamp, calendar)
local function parseDateFull(timestamp, precision, date_format, date_addon)
local pattern = "(%W)(%d+)%-(%d+)%-(%d+)"
local prefix_addon = i18n["datetime"]["prefix-addon"]
local era, year, month, day = timestamp:match(pattern)
local addon_sep = i18n["datetime"]["addon-sep"]
local addon = ""


-- check for negative date
if calendar == 'julian' then
if string.sub(timestamp, 1, 1) == '-' then
--todo  year, month, day = formatdate.gregorianToJulian( era .. year, month, day )
timestamp = '+' .. string.sub(timestamp, 2)
addon = date_addon
end
end


-- get the next four characters after the + (should be the year now in all cases)
return {day = day, month = month, year = year, era = era, timestamp = timestamp, type = 'dateobject'}
-- ok, so this is dirty, but let's get it working first
end
local intyear = tonumber(string.sub(timestamp, 2, 5))
 
if intyear == 0 and precision <= 9 then
local function rangeObject(begin, ending)
return ""
local timestamp
if begin then
timestamp = begin.timestamp
elseif ending then
timestamp = ending.timestamp
end
end
return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'}
end


-- precision is 10000 years or more
local function dateObject(orig, params) -- transforme un snak en un nouvel objet utilisable par Module:Date complexe
if precision <= 5 then
if not params then
local factor = 10 ^ ((5 - precision) + 4)
params = {}
local y2 = math.ceil(math.abs(intyear) / factor)
local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2))
if addon ~= "" then
-- negative date
relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
else
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
end
return relative
end
end


-- precision is decades (8), centuries (7) and millennia (6)
local newobj = splitTimestamp(orig.time, orig.calendar) -- initalise l'object en mettant la valeur des dates
local era, card
 
if precision == 6 then
newobj.precision = params.precision or orig.precision
card = math.floor((intyear - 1) / 1000) + 1
newobj.type = 'dateobject'
era = mw.ustring.gsub(i18n.datetime[6], "$1", makeOrdinal(card))
return newobj
end
 
local function formatDatepoint(obj, params) -- TO IMPROVE
if not obj then
return nil
end
end
if precision == 7 then
local formatDate = require('Module:Complex date')
card = math.floor((intyear - 1) / 100) + 1
local lang = params.lang or defaultlang
era = mw.ustring.gsub(i18n.datetime[7], "$1", makeOrdinal(card))
local precision = math.min(obj.precision, params.precision or 15) -- if we don't want to show the value to its full detail
if precision >= 11 then
return formatDate.complex_date{args={date1 = obj.year .. '-' .. obj.month .. '-' .. obj.day, lang= lang}}
elseif precision == 10 then
return formatDate.complex_date{args={date1 = obj.year .. '-' .. obj.month, lang= lang}}
elseif precision == 9 then
return formatDate.complex_date{args={date1 = tostring(obj.year), lang= lang}}
elseif precision == 8 then
return formatDate.complex_date{args={date1 = string.sub(tostring(obj.year), 1, 3) .. '0', lang = lang, precision = 'decade'}}
elseif precision == 7 then
return formatDate.complex_date{args={date1 = string.sub(tostring(obj.year + 100), 1, 2), lang = lang, precision = 'century'}}
end
end
if precision == 8 then
return nil
era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(intyear) / 10) * 10))
end
end
 
if era then
local function formatDaterange(obj, params) --TODO
if addon ~= "" then
local begin = formatDatepoint(obj.begin, params) or ''
era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era)
local ending = formatDatepoint(obj.ending, params) or ''
else
return begin .. '-' .. ending
era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era)
end
end
 
return era
local function objectToText(obj, params)
if obj.type == 'dateobject' then
return formatDatepoint(obj, params)
elseif obj.type == 'rangeobject' then
return formatDaterange(obj, params)
end
end
return nil
end


local _date_format = i18n["datetime"]["format"][date_format]
local function tableToText(values, params) -- takes a list of already formatted values and make them a text
if _date_format ~= nil then
if not values then
-- check for precision is year and override supplied date_format
return nil
if precision == 9 then
_date_format = i18n["datetime"][9]
end
return parseDateFormat(_date_format, timestamp, addon, prefix_addon, addon_sep)
else
return printError("unknown-datetime-format")
end
end
return linguistic.conj(values, params.lang or defaultlang, params.conjtype)--linguistic.conj( values, params.lang, params.conjtype )
end
end


-- the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field
function p.getDate(obj)
-- use these as the second parameter and this function instead of the built-in "pairs" function
--[[
-- to iterate over all qualifiers and snaks in the intended order.
returns an object containing a timestamp for easy sorting, and other data
local function orderedpairs(array, order)
possible types of object:
if not order then return pairs(array) end
dateobject
{timestamp = string, year = number, month = number, day = number, calendar = string}
rangeobject
{timestamp = string, begin = dateobject, ending = dateobject}
]]--
if not obj then
return nil
end
if type(obj) == 'string' then
obj = p.getEntity(obj)
end


-- return iterator function
-- if obj is a statement with date, get it
local i = 0
if obj.mainsnak and not isSpecial(obj.mainsnak) and obj.mainsnak.datatype == 'time' then
return function()
return dateObject(obj.mainsnak.datavalue.value)
i = i + 1
if order[i] then
return order[i], array[order[i]]
end
end
end
end


-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
-- else preload relevant data
local function normalizeDate(date)
local qualifs = obj.qualifiers -- when obj is a statement, look in qualifiers
date = mw.text.trim(date, "+")
local claims = obj.claims -- when obj is an item, look in claims
-- extract year
local yearstr = mw.ustring.match(date, "^\-?%d+")
local year = tonumber(yearstr)
-- remove leading zeros of year
return year .. mw.ustring.sub(date, #yearstr + 1), year
end


local function formatDate(date, precision, timezone)
local pointprop = {'P585', 'P571'} -- dates corresponding to a punctual fact
precision = precision or 11
local beginprop = {'P580', 'P569'} -- start date, birth date == start of a date range
local date, year = normalizeDate(date)
local endingprop = {'P582', 'P570'}
if year == 0 and precision <= 9 then return "" end


-- precision is 10000 years or more
local function getval(prop)
if precision <= 5 then
local val
local factor = 10 ^ ((5 - precision) + 4)
if claims and claims[prop] and not isSpecial(claims[prop][1].mainsnak) then
local y2 = math.ceil(math.abs(year) / factor)
val = claims[prop][1].mainsnak.datavalue.value
local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2))
elseif qualifs and qualifs[prop] and not isSpecial(qualifs[prop][1]) then
if year < 0 then
val = qualifs[prop][1].datavalue.value
relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
end
else
if val then
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
return dateObject(val)
end
end
return relative
return nil
end
end


-- precision is decades, centuries and millennia
for i, prop in pairs(pointprop) do
local era
local val = getval(prop)
if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end
if val then return val end
if precision == 7 then era = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(math.floor((math.abs(year) - 1) / 100) + 1)) end
if precision == 8 then era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(year) / 10) * 10)) end
if era then
if year < 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era)
elseif year > 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era) end
return era
end
end
 
--if no date has not been found, look for startdate or enddate
-- precision is year
local begin, ending
if precision == 9 then
for i, prop in pairs(beginprop) do
return year
begin = getval(prop)
if begin then
break
end
end
end
 
for i, prop in pairs(endingprop) do
-- precision is less than years
ending = getval(prop)
if precision > 9 then
if ending then
--[[ the following code replaces the UTC suffix with the given negated timezone to convert the global time to the given local time
break
timezone = tonumber(timezone)
if timezone and timezone ~= 0 then
timezone = -timezone
timezone = string.format("%.2d%.2d", timezone / 60, timezone % 60)
if timezone[1] ~= '-' then timezone = "+" .. timezone end
date = mw.text.trim(date, "Z") .. " " .. timezone
end
end
]]--
local formatstr = i18n.datetime[precision]
if year == 0 then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], "")
elseif year < 0 then
-- Mediawiki formatDate doesn't support negative years
date = mw.ustring.sub(date, 2)
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.bc, "$1", i18n.datetime[9]))
elseif year > 0 and i18n.datetime.ad ~= "$1" then
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.ad, "$1", i18n.datetime[9]))
end
return mw.language.new(wiki.langcode):formatDate(formatstr, date)
end
end
if begin or ending then
return rangeObject(begin, ending)
end
return nil
end
end


local function printDatavalueEntity(data, parameter)
function p.getFormattedDate(statement, params)
-- data fields: entity-type [string], numeric-id [int, Wikidata id]
local datetable = p.getDate(statement)
local id
if not datetable then
 
return nil
if data["entity-type"] == "item" then id = "Q" .. data["numeric-id"]
elseif data["entity-type"] == "property" then id = "P" .. data["numeric-id"]
else return printError("unknown-entity-type")
end
end
return objectToText(datetable, params)
end


if parameter then
local function hasTargetValue(claim, target)
if parameter == "link" then
if target == nil then
local linkTarget = mw.wikibase.getSitelink(id)
return true
local linkName = mw.wikibase.getLabel(id)
if linkTarget then
-- if there is a local Wikipedia article link to it using the label or the article title
return "[[" .. linkTarget .. "|" .. (linkName or linkTarget) .. "]]"
else
-- if there is no local Wikipedia article output the label or link to the Wikidata object to let the user input a proper label
if linkName then return linkName else return "[[:d:" .. id .. "|" .. id .. "]]" end
end
else
return data[parameter]
end
else
return mw.wikibase.getLabel(id) or id
end
end
return sameValue(claim.mainsnak, target)
end
end


local function printDatavalueTime(data, parameter)
local function hasRank(claim, target)
-- data fields: time [ISO 8601 time], timezone [int in minutes], before [int], after [int], precision [int], calendarmodel [wikidata URI]
if target == 'valid' then
--  precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
return hasRank(claim, 'preferred') or hasRank(claim, 'normal')
--  calendarmodel: e.g. http://www.wikidata.org/entity/Q1985727 for the proleptic Gregorian calendar or http://www.wikidata.org/wiki/Q11184 for the Julian calendar]
if parameter then
if parameter == "calendarmodel" then data.calendarmodel = mw.ustring.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI
elseif parameter == "time" then data.time = normalizeDate(data.time) end
return data[parameter]
else
else
return formatDate(data.time, data.precision, data.timezone)
return claim.rank == target
end
end
end
end


local function printDatavalueMonolingualText(data, parameter)
local function bestRanked(claims)
-- data fields: language [string], text [string]
if not claims then
if parameter then
return nil
return data[parameter]
end
local preferred, normal = {}, {}
for _, j in ipairs(claims) do
if j.rank == 'preferred' then
table.insert(preferred, j)
elseif j.rank == 'normal' then
table.insert(normal, j)
end
end
if #preferred > 0 then
return preferred
else
else
local result = mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext, "%%language", data["language"]), "%%text", data["text"])
return normal
return result
end
end
end
end


local function findClaims(entity, property)
local function hasQualifier(claim, qualifier, qualifiervalues)
if not property or not entity or not entity.claims then return end
if not qualifier then -- si aucun qualificatif est demandé, ça passe
return true
end


if mw.ustring.match(property, "^P%d+$") then
qualifier = string.upper(qualifier)
-- if the property is given by an id (P..) access the claim list by this id
if not claim.qualifiers or not claim.qualifiers[qualifier] then
return entity.claims[property]
return false
else
end
property = mw.wikibase.resolvePropertyId(property)
if not property then return end


return entity.claims[property]
if type(qualifiervalues) == 'string' then
qualifiervalues = mw.text.split(qualifiervalues, ',')
end
end
end


local function getSnakValue(snak, parameter)
if (not qualifiervalues) or (qualifiervalues == {}) then
if snak.snaktype == "value" then
return true -- si aucune valeur spécifique n'est exigée
-- call the respective snak parser
if snak.datavalue.type == "string" then return snak.datavalue.value
elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "monolingualtext" then return printDatavalueMonolingualText(snak.datavalue.value, parameter)
end
end
end
return mw.wikibase.renderSnak(snak)
end


local function getQualifierSnak(claim, qualifierId)
for _, j in ipairs(claim.qualifiers[qualifier]) do
-- a "snak" is Wikidata terminology for a typed key/value pair
for _, l in ipairs(qualifiervalues) do
-- a claim consists of a main snak holding the main information of this claim,
if p.sameValue(j, l) then
-- as well as a list of attribute snaks and a list of references snaks
return true
if qualifierId then
end
-- search the attribute snak with the given qualifier as key
if claim.qualifiers then
local qualifier = claim.qualifiers[qualifierId]
if qualifier then return qualifier[1] end
end
end
return nil, printError("qualifier-not-found")
else
-- otherwise return the main snak
return claim.mainsnak
end
end
end
return false
end


local function getValueOfClaim(claim, qualifierId, parameter)
local function hasSource(statement, source, sourceproperty)
local error
if not statement.references then
local snak
return false
snak, error = getQualifierSnak(claim, qualifierId)
if snak then
return getSnakValue(snak, parameter)
else
return nil, error
end
end
end
sourceproperty = string.upper(sourceproperty or 'P248')
 
local sourcevalue = string.upper(source or '')
local function getReferences(frame, claim)
for _, ref in ipairs(statement.references) do
local result = ""
for prop, content in pairs(ref.snaks) do
-- traverse through all references
if prop == sourceproperty then
for ref in pairs(claim.references or {}) do
if sourcevalue == '' then
local refparts
return true
-- traverse through all parts of the current reference
else
for snakkey, snakval in orderedpairs(claim.references[ref].snaks or {}, claim.references[ref]["snaks-order"]) do
for _, k in ipairs(content) do
if refparts then refparts = refparts .. ", " else refparts = "" end
if sameValue(k, source) then
-- output the label of the property of the reference part, e.g. "imported from" for P143
return true
refparts = refparts .. tostring(mw.wikibase.getLabel(snakkey)) .. ": "
end
-- output all values of this reference part, e.g. "German Wikipedia" and "English Wikipedia" if the referenced claim was imported from both sites
end
for snakidx = 1, #snakval do
end
if snakidx > 1 then refparts = refparts .. ", " end
refparts = refparts .. getSnakValue(snakval[snakidx])
end
end
end
end
if refparts then result = result .. frame:extensionTag("ref", refparts) end
end
end
return result
return false
end
end


local function parseInput(frame)
local function hasDate(statement)
local qid = frame.args.qid
if not statement.qualifiers then
if qid and (#qid == 0) then qid = nil end
return false
local propertyID = mw.text.trim(frame.args[1] or "")
local input_parm = mw.text.trim(frame.args[2] or "")
if input_parm ~= "FETCH_WIKIDATA" then
return false, input_parm, nil, nil
end
end
local entity = mw.wikibase.getEntity(qid)
local dateprops = {'P580', 'P585', 'P582'}
local claims
for i, prop in pairs(dateprops) do
if entity and entity.claims then
if statement.qualifiers[prop] then
claims = entity.claims[propertyID]
return true
if not claims then
return false, "", nil, nil
end
end
else
return false, "", nil, nil
end
end
return true, entity, claims, propertyID
return false
end
end
local function isType(claims, type)
 
return claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == type
local function isInLanguage(snak, lang) -- ne fonctionne que pour les monolingualtext / étendre aux autres types en utilisant les qualifiers ?
return not isSpecial(snak) and snak.datavalue.type == 'monolingualtext' and snak.datavalue.value.language == lang
end
end
local function getValue(entity, claims, propertyID, delim, labelHook)  
 
if labelHook == nil then
local function numval(claims, numval) -- retourn les numval premières valeurs de la table claims
labelHook = function (qnumber)
local numval = tonumber(numval) or 0 -- raise an error if numval is not a positive integer ?
return nil;
if #claims <= numval then
end
return claims
end
local newclaims = {}
while #newclaims < numval do
table.insert(newclaims, claims[#newclaims + 1])
end
return newclaims
end
 
local function wikipediaLink(entity, lang)
local link
 
local lg = string.gsub(lang, '-', '_')
if (lg == 'be_tarask') then lg = 'be_x_old' end
if (lg == 'nb') then lg = 'no' end
 
if type(entity) == 'table' then
link = entity:getSitelink(lg .. 'wiki')
else
link = mw.wikibase.getSitelink(entity, lg .. 'wiki')
end
if link then
return ':' .. lang .. ':' .. link
end
end
if isType(claims, "wikibase-entityid") then
return nil
local out = {}
end
for k, v in pairs(claims) do
 
local qnumber = "Q" .. v.mainsnak.datavalue.value["numeric-id"]
local function getLink(entity, typelink, lang)
local sitelink = mw.wikibase.getSitelink(qnumber)
if typelink == 'wikidata' then
local label = labelHook(qnumber) or mw.wikibase.getLabel(qnumber) or qnumber
if type(entity) == 'table' then
if sitelink then
if entity.type == 'property' then
out[#out + 1] = "[[" .. sitelink .. "|" .. label .. "]]"
return 'd:P:' .. entity.id
elseif entity.type == 'lexeme' then
return 'd:L:' .. entity.id
else
else
out[#out + 1] = "[[:d:" .. qnumber .. "|" .. label .. "]]<abbr title='" .. i18n["errors"]["local-article-not-found"] .. "'>[*]</abbr>"
return 'd:' .. entity.id
end
else
if string.sub(entity, 1, 1) == 'P' then
return 'd:P:' .. entity
elseif string.sub(entity, 1, 1) == 'L' then
return 'd:L:' .. entity
else
return 'd:' .. entity
end
end
 
elseif typelink == 'wikipedia' then
return wikipediaLink(entity, lang or defaultlang)
 
elseif typelink == 'anywikipedia' then
for _, lg in ipairs(fb.fblist(lang or defaultlang, true)) do
local link = wikipediaLink(entity, lg)
if link then
return link
end
end
end
end
return table.concat(out, delim)
else
-- just return best values
return entity:formatPropertyValues(propertyID).value
end
end
return nil
end
end


------------------------------------------------------------------------------
function p.comparedate(a, b) -- returns true if a is earlier than B or if a has a date but not b
-- module global functions
if a and b then
return a.timestamp < b.timestamp
elseif a then
return true
end
return false
end


if debug then
function p.chronosort(objs, inverted)
function p.inspectI18n(frame)
table.sort(objs, function(a, b)
local val = i18n
local timeA = p.getDate(a)
for _, key in pairs(frame.args) do
local timeB = p.getDate(b)
key = mw.text.trim(key)
if inverted then
val = val[key]
return p.comparedate(timeB, timeA)
else
return p.comparedate(timeA, timeB)
end
end
return val
end)
end
 
return objs
end
end


function p.descriptionIn(frame)
function p.sortclaims(claims, sorttype)
local langcode = frame.args[1]
if type(sorttype) == 'function' then
local id = frame.args[2]
table.sort(claims, sorttype)
-- return description of a Wikidata entity in the given language or the default language of this Wikipedia site
elseif sorttype == 'chronological' then
return mw.wikibase.getEntity(id):getDescription(langcode or wiki.langcode)
return p.chronosort(claims)
elseif sorttype == 'inverted' then
return p.chronosort(claims, true)
end
return claims
end
end


function p.labelIn(frame)
function p.getRawvalue(snak)
local langcode = frame.args[1]
return p.getDatavalue(snak, { displayformat = 'raw' })
local id = frame.args[2]
-- return label of a Wikidata entity in the given language or the default language of this Wikipedia site
return mw.wikibase.getEntity(id):getLabel(langcode or wiki.langcode)
end
end


-- This is used to get a value, or a comma separated list of them if multiple values exist
function p.showentity(entity, lang)
p.getValue = function(frame)
if not entity then
local delimdefault = ", " -- **internationalise later**
return nil
local delim = frame.args.delimiter or ""
delim = string.gsub(delim, '"', '')
if #delim == 0 then
delim = delimdefault
end
end
local go, errorOrentity, claims, propertyID = parseInput(frame)
local label, link, id = p._getLabel(entity, lang), getLink(entity, 'wikidata')
if not go then
if type(entity) == 'table' then
return errorOrentity
id = entity.id
else
id = entity
end
end
return getValue(errorOrentity, claims, propertyID, delim)
return '[[' .. link .. '|' .. label .. ']] <small>(' .. id .. ')</small>'
end
end


-- Same as above, but uses the short name property for label if available.
function p.getDatavalue(snak, params)
p.getValueShortName = function(frame)
if isSpecial(snak) then
local go, errorOrentity, claims, propertyID = parseInput(frame)
return nil
if not go then
return errorOrentity
end
end
local entity = errorOrentity
 
-- if wiki-linked value output as link if possible
if not params then
local function labelHook (qnumber)
params = {}
local label
end
local claimEntity = mw.wikibase.getEntity(qnumber)
 
if claimEntity ~= nil then
local displayformat = params.displayformat
if claimEntity.claims.P1813 then
local datatype = snak.datavalue.type
for k2, v2 in pairs(claimEntity.claims.P1813) do
local value = snak.datavalue.value
if v2.mainsnak.datavalue.value.language == "en" then
 
label = v2.mainsnak.datavalue.value.text
if datatype == 'wikibase-entityid' then
end
if type(displayformat) == 'function' then
return displayformat(snak, params)
end
local id = snak.datavalue.value.id
if displayformat == 'raw' then
return id
elseif displayformat == 'wikidatastyle' then
return p.showentity(id, params.lang)
else
return p.formatEntity(id, params)
end
 
elseif datatype == 'string' then
local showntext = params.showntext
if displayformat == 'weblink' then
if showntext then
return '[' .. value .. ' ' .. showntext .. ']'
else
return value
end
end
if snak.datatype == 'math' and displayformat ~= 'raw' then
value = mw.getCurrentFrame():extensionTag('math', value)
else
if params.urlpattern then
showntext = mw.text.nowiki(showntext or value)
value = mw.ustring.gsub(value, '%%', '%%%%') -- escape '%'
value = '[' .. mw.ustring.gsub(mw.ustring.gsub(params.urlpattern, '$1', value), ' ', '%%20') .. ' ' .. showntext .. ']'
elseif params.pattern then
local pattern = mw.ustring.gsub(params.pattern, '%%', '%%%%')
value = mw.ustring.gsub(value, '%%', '%%%%')
value = mw.ustring.gsub(pattern, '$1', value)
else
if displayformat ~= 'raw' then
value = mw.text.nowiki(value)
end
end
end
end
end
end
if label == nil or label == "" then return nil end
return value
return label
end
return getValue(errorOrentity, claims, propertyID, ", ", labelHook);
end


-- This is used to get a value, or a comma separated list of them if multiple values exist
elseif datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
-- from an arbitrary entry by using its QID.
if displayformat == 'raw' then
-- Use : {{#invoke:Wikidata|getValueFromID|<ID>|<Property>|FETCH_WIKIDATA}}
return value.time
-- E.g.: {{#invoke:Wikidata|getValueFromID|Q151973|P26|FETCH_WIKIDATA}} - to fetch value of 'spouse' (P26) from 'Richard Burton' (Q151973)
else
-- Please use sparingly - this is an *expensive call*.
return objectToText(dateObject(value), params)
p.getValueFromID = function(frame)
local itemID = mw.text.trim(frame.args[1] or "")
local propertyID = mw.text.trim(frame.args[2] or "")
local input_parm = mw.text.trim(frame.args[3] or "")
if input_parm == "FETCH_WIKIDATA" then
local entity = mw.wikibase.getEntity(itemID)
local claims
if entity and entity.claims then
claims = entity.claims[propertyID]
end
end
if claims then
 
return getValue(entity, claims, propertyID, ", ")
elseif datatype == 'globecoordinate' then
-- retourne une table avec clés latitude, longitude, précision et globe à formater par un autre module (à changer ?)
if displayformat == 'latitude' then
return value.latitude
elseif displayformat == 'longitude' then
return value.longitude
elseif displayformat == 'qualifier' then
local coord = require 'Module:Coordinates'
value.globe = mw.loadData('Module:Wikidata/Globes')[value.globe]
value.precision = nil
return coord._coord(value)
else
else
return ""
value.globe = mw.loadData('Module:Wikidata/Globes')[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohack
return value -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?
end
end
else
 
return input_parm
elseif datatype == 'quantity' then -- todo : gérer les paramètre précision
end
if displayformat == 'raw' then
end
return tonumber(value.amount)
local function getQualifier(frame, outputHook)  
else
local propertyID = mw.text.trim(frame.args[1] or "")
local formatNum = require 'Module:Formatnum'
local qualifierID = mw.text.trim(frame.args[2] or "")
local number = formatNum.formatNum(value.amount, params.lang)
local input_parm = mw.text.trim(frame.args[3] or "")
local unit = mw.ustring.match(value.unit, '(Q%d+)')
if input_parm == "FETCH_WIKIDATA" then
if unit then
local entity = mw.wikibase.getEntity()
number = number .. '&nbsp;' .. p.formatEntity(unit, params)
if entity.claims[propertyID] ~= nil then
local out = {}
for k, v in pairs(entity.claims[propertyID]) do
for k2, v2 in pairs(v.qualifiers[qualifierID]) do
if v2.snaktype == 'value' then
out[#out + 1] = outputHook(v2);
end
end
end
end
return table.concat(out, ", "), true
return number
else
return "", false
end
end
elseif datatype == 'monolingualtext' then
return '<span lang="' .. value.language .. '">' .. value.text .. '</span>'
else
else
return input_parm, false
return formatError( 'unknown-datavalue-type', datatype )
end
end
end
end
p.getQualifierValue = function(frame)
 
local function outputValue(value)
local function getMultipleClaims(args)
local qnumber = "Q" .. value.datavalue.value["numeric-id"]
local newargs = args
if (mw.wikibase.getSitelink(qnumber)) then
local claims = {}
return "[[" .. mw.wikibase.getSitelink(qnumber) .. "]]"
for i, j in pairs(args.property) do
else
newargs.property = j
return "[[:d:" .. qnumber .. "|" ..qnumber .. "]]<abbr title='" .. i18n["errors"]["local-article-not-found"] .. "'>[*]</abbr>"
local newclaims = p.getClaims(args)
if newclaims then
for k, l in pairs(newclaims) do
table.insert(claims, l)
end
end
end
end
end
return (getQualifier(frame, outputValue))
return claims
end
end


-- This is used to get a value like 'male' (for property p21) which won't be linked and numbers without the thousand separators
function p.getClaims( args ) -- returns a table of the claims matching some conditions given in args
p.getRawValue = function(frame)
args = removeBlanks(args)
local go, errorOrentity, claims, propertyID = parseInput(frame)
if not args.property then
if not go then
return formatError( 'property-param-not-provided' )
return errorOrentity
end
if type(args.property) == 'table' then
return getMultipleClaims(args)
end
--Get entity
if args.item then -- synonyms
args.entity = args.item
end
end
local entity = errorOrentity
local property = string.upper(args.property)
local result = entity:formatPropertyValues(propertyID, mw.wikibase.entity.claimRanks).value
local allClaims
-- if number type: remove thousand separators, bounds and units
local entity = args.entity
if isType(claims, "quantity") then
if type(entity) == 'table' then
result = mw.ustring.gsub(result, "(%d),(%d)", "%1%2")
allClaims = (entity and entity.claims and entity.claims[property]) or {}
result = mw.ustring.gsub(result, "(%d)±.*", "%1")
else
allClaims = mw.wikibase.getAllStatements(entity, property)
end
if #allClaims == 0 then
return nil
end
end
return result
end


-- This is used to get the unit name for the numeric value returned by getRawValue
if not args.rank then
p.getUnits = function(frame)
args.rank = 'best'
local go, errorOrentity, claims, propertyID = parseInput(frame)
end
if not go then
local claims = {}
return errorOrentity
for _, statement in ipairs(allClaims) do
if
(
not args.excludespecial
or
not (isSpecial(statement.mainsnak))
)
and
(
not args.targetvalue
or
hasTargetValue(statement, args.targetvalue)
)
and
(
not args.qualifier
or
hasQualifier(statement, args.qualifier, args.qualifiervalues or args.qualifiervalue)
)
and
(
not args.withsource or args.withsource == '-'
or
hasSource(statement, args.withsource, args.sourceproperty)
)
and
(
not args.isinlanguage
or
isInLanguage(statement.mainsnak, args.isinlanguage)
)
and
(
args.rank == 'best' -- rank == best est traité à a fin
or
hasRank(statement, args.rank)
)
then
table.insert(claims, statement)
end
end
end
local entity = errorOrentity
if #claims == 0 then
local result = entity:formatPropertyValues(propertyID, mw.wikibase.entity.claimRanks).value
return nil
if isType(claims, "quantity") then
result = mw.ustring.sub(result, mw.ustring.find(result, " ")+1, -1)
end
end
return result
if args.rank == 'best' then
claims = bestRanked(claims)
end
if args.sorttype then
claims = p.sortclaims(claims, args.sorttype)
end
 
if args.numval then
return numval(claims, args.numval)
end
return claims
end
end


-- This is used to get the unit's QID to use with the numeric value returned by getRawValue
function p.formatClaimList(claims, args)
p.getUnitID = function(frame)
if not claims then
local go, errorOrentity, claims = parseInput(frame)
return nil
if not go then
return errorOrentity
end
end
local entity = errorOrentity
for i, j in pairs(claims) do
local result
claims[i] = p.formatStatement(j, args)
if isType(claims, "quantity") then
-- get the url for the unit entry on Wikidata:
result = claims[1].mainsnak.datavalue.value.unit
-- and just reurn the last bit from "Q" to the end (which is the QID):
result = mw.ustring.sub(result, mw.ustring.find(result, "Q"), -1)
end
end
return result
return claims
end
end


p.getRawQualifierValue = function(frame)
function p.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation
local function outputHook(value)
local claims = p.getClaims(args)
if value.datavalue.value["numeric-id"] then
return p.formatClaimList(claims, args)
return mw.wikibase.getLabel("Q" .. value.datavalue.value["numeric-id"])
end
else
 
return value.datavalue.value
local function getQualifiers(statement, qualifs, params)
if not statement.qualifiers then
return nil
end
local vals = {}
for i, j in pairs(qualifs) do
j = string.upper(j)
if statement.qualifiers[j] then
local inserted = false
if statement.qualifiers[j][1].datatype == 'monolingualtext' then
local in_preferred_lang
for _, language in ipairs(fb.fblist(params.lang or defaultlang, true)) do
for _, snak in ipairs(statement.qualifiers[j]) do
if isInLanguage(snak, language) then
in_preferred_lang = snak
break
end
end
if in_preferred_lang then
break
end
end
if in_preferred_lang then
table.insert(vals, in_preferred_lang)
inserted = true
end
end
if not inserted then
for _, snak in pairs(statement.qualifiers[j]) do
table.insert(vals, snak)
end
end
end
end
end
end
local ret, gotData = getQualifier(frame, outputHook)
if #vals == 0 then
if gotData then
return nil
ret = string.upper(string.sub(ret, 1, 1)) .. string.sub(ret, 2)
end
end
return ret
return vals
end
end


-- This is used to get a date value for date_of_birth (P569), etc. which won't be linked
function p.getFormattedQualifiers(statement, qualifs, params)
-- Dates and times are stored in ISO 8601 format (sort of).
if not params then params = {} end
-- At present the local formatDate(date, precision, timezone) function doesn't handle timezone
local qualiftable = getQualifiers(statement, qualifs, params)
-- So I'll just supply "Z" in the call to formatDate below:
if not qualiftable then
p.getDateValue = function(frame)
return nil
local date_format = mw.text.trim(frame.args[3] or i18n["datetime"]["default-format"])
local date_addon = mw.text.trim(frame.args[4] or i18n["datetime"]["default-addon"])
local go, errorOrentity, claims = parseInput(frame)
if not go then
return errorOrentity
end
end
local entity = errorOrentity
for i, j in pairs(qualiftable) do
local out = {}
local params = params
for k, v in pairs(claims) do
if j.datatype == 'globe-coordinate' then
if v.mainsnak.datavalue.type == 'time' then
params.displayformat = 'qualifier'
local timestamp = v.mainsnak.datavalue.value.time
local dateprecision = v.mainsnak.datavalue.value.precision
-- A year can be stored like this: "+1872-00-00T00:00:00Z",
-- which is processed here as if it were the day before "+1872-01-01T00:00:00Z",
-- and that's the last day of 1871, so the year is wrong.
-- So fix the month 0, day 0 timestamp to become 1 January instead:
timestamp = timestamp:gsub("%-00%-00T", "-01-01T")
out[#out + 1] = parseDateFull(timestamp, dateprecision, date_format, date_addon)
end
end
qualiftable[i] = p.formatSnak(j, params)
end
end
return table.concat(out, ", ")
return linguistic.conj(qualiftable, params.lang or defaultlang)
end
end
p.getQualifierDateValue = function(frame)
 
local date_format = mw.text.trim(frame.args[4] or i18n["datetime"]["default-format"])
function p.formatStatement( statement, args )
local date_addon = mw.text.trim(frame.args[5] or i18n["datetime"]["default-addon"])
if not statement.type or statement.type ~= 'statement' then
local function outputHook(value)
return formatError( 'unknown-claim-type', statement.type )
local timestamp = value.datavalue.value.time
end
return parseDateValue(timestamp, date_format, date_addon)
if not args then args = {} end
local lang = args.lang or defaultlang
local str = p.formatSnak( statement.mainsnak, args )
if args.showlang == true then
str = showLang(statement, str)
end
end
return (getQualifier(frame, outputHook))
end


-- This is used to fetch all of the images with a particular property, e.g. image (P18), Gene Atlas Image (P692), etc.
local qualifs = args.showqualifiers
-- Parameters are | propertyID | value / FETCH_WIKIDATA / nil | separator (default=space) | size (default=frameless)
if qualifs then
-- It will return a standard wiki-markup [[File:Filename | size]] for each image with a selectable size and separator (which may be html)
if type(qualifs) == 'string' then
-- e.g. {{#invoke:Wikidata|getImages|P18|FETCH_WIKIDATA}}
qualifs = mw.text.split(qualifs, ',')
-- e.g. {{#invoke:Wikidata|getImages|P18|FETCH_WIKIDATA|<br>|250px}}
end
-- If a property is chosen that is not of type "commonsMedia", it will return empty text.
local foundvalues = p.getFormattedQualifiers(statement, qualifs, args)
p.getImages = function(frame)
if foundvalues then
local sep = mw.text.trim(frame.args[3] or " ")
if args.delimiter then
local imgsize = mw.text.trim(frame.args[4] or "frameless")
str = str .. args.delimiter .. foundvalues
local go, errorOrentity, claims = parseInput(frame)
else
if not go then
str = str .. linguistic.inparentheses(foundvalues, lang)
return errorOrentity
end
end
end
end
local entity = errorOrentity
 
if (claims[1] and claims[1].mainsnak.datatype == "commonsMedia") then
if args.showdate then -- when "showdate and p.chronosort are both set, date retrieval is performed twice
local out = {}
local timedata = p.getDate(statement)
for k, v in pairs(claims) do
if timedata then
local filename = v.mainsnak.datavalue.value
local formatteddate = objectToText(timedata, args)
out[#out + 1] = "[[File:" .. filename .. "|" .. imgsize .. "]]"
formatteddate = linguistic.inparentheses(formatteddate, lang)
str = str .. '<small>' .. formatteddate ..'</small>'
end
end
return table.concat(out, sep)
else
return ""
end
end
end


-- This is used to get the TA98 (Terminologia Anatomica first edition 1998) values like 'A01.1.00.005' (property P1323)
if args.showsource and statement.references then
-- which are then linked to http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/01.1.00.005%20Entity%20TA98%20EN.htm
local cite = require 'Module:Cite'
-- uses the newer mw.wikibase calls instead of directly using the snaks
local frame = mw.getCurrentFrame()
-- formatPropertyValues returns a table with the P1323 values concatenated with ", " so we have to split them out into a table in order to construct the return string
local sourcestring = ''
p.getTAValue = function(frame)
for _, ref in ipairs(statement.references) do
local ent = mw.wikibase.getEntity()
if ref.snaks.P248 then
local props = ent:formatPropertyValues('P1323')
for j, source in pairs(ref.snaks.P248) do
local out = {}
if not isSpecial(source) then
local t = {}
local page
for k, v in pairs(props) do
if ref.snaks.P304 and not isSpecial(ref.snaks.P304[1]) then
if k == 'value' then
page = ref.snaks.P304[1].datavalue.value
t = mw.text.split( v, ", ")
end
for k2, v2 in pairs(t) do
local s = cite.citeitem(source.datavalue.value.id, lang, page)
out[#out + 1] = "[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/" .. string.sub(v2, 2) .. "%20Entity%20TA98%20EN.htm " .. v2 .. "]"
s = frame:extensionTag( 'ref', s )
sourcestring = sourcestring .. s
end
end
elseif ref.snaks.P854 and not isSpecial(ref.snaks.P854[1]) then
s = frame:extensionTag( 'ref', p.getDatavalue(ref.snaks.P854[1]) )
sourcestring = sourcestring .. s
end
end
end
end
str = str .. sourcestring
end
end
local ret = table.concat(out, "<br> ")
return str
if #ret == 0 then
end
ret = "Invalid TA"
 
function p.getmainid(claim)
if claim and not isSpecial(claim.mainsnak) then
return claim.mainsnak.datavalue.value.id
end
end
return ret
return nil
end
end


--[[
function p.formatSnak(snak, params)
This is used to return an image legend from Wikidata
--local params = params or {} pour faciliter l'appel depuis d'autres modules
image is property P18
if snak.snaktype == 'value' then
image legend is property P2096
return p.getDatavalue(snak, params)
elseif snak.snaktype == 'somevalue' then
return formatTheUnknown()
elseif snak.snaktype == 'novalue' then
return i18n('novalue') --todo
else
return formatError( 'unknown-snak-type', snak.snaktype )
end
end


Call as {{#invoke:Wikidata |getImageLegend | <PARAMETER> | lang=<ISO-639code> |id=<QID>}}
local function defaultLabel(entity, displayformat) -- label when no label is available
Returns PARAMETER, unless it is equal to "FETCH_WIKIDATA", from Item QID (expensive call)
if displayformat == 'id' then
If QID is omitted or blank, the current article is used (not an expensive call)
if type(entity) ~= 'table' then
If lang is omitted, it uses the local wiki language, otherwise it uses the provided ISO-639 language code
return entity
ISO-639: https://docs.oracle.com/cd/E13214_01/wli/docs92/xref/xqisocodes.html#wp1252447
else
return entity.id
end
end
return i18n('no-label')
end


Ranks are: 'preferred' > 'normal'
function p._getLabel(entity, lang, default, fallback)
This returns the label from the first image with 'preferred' rank
if not entity then
Or the label from the first image with 'normal' rank if preferred returns nothing
return nil
Ranks: https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua
]]
 
p.getImageLegend = function(frame)
-- look for named parameter id; if it's blank make it nil
local id = frame.args.id
if id and (#id == 0) then
id = nil
end
end
 
if not lang then
-- look for named parameter lang
lang = defaultlang
-- it should contain a two-character ISO-639 language code
-- if it's blank fetch the language of the local wiki
local lang = frame.args.lang
if (not lang) or (#lang < 2) then
lang = mw.language.getContentLanguage().code
end
end
 
if type(entity) ~= 'table' and lang == defaultlang then
-- first unnamed parameter is the local parameter, if supplied
local label, lg = mw.wikibase.getLabelWithLang(entity)
local input_parm = mw.text.trim(frame.args[1] or "")
if label and (fallback ~= '-' or lg == lang) then
if input_parm == "FETCH_WIKIDATA" then
return label
local ent = mw.wikibase.getEntity(id)
local imgs
if ent and ent.claims then
imgs = ent.claims.P18
end
end
local imglbl
else
if imgs then
entity = p.getEntity(entity)
-- look for an image with 'preferred' rank
if entity and entity.labels then
for k1, v1 in pairs(imgs) do
if fallback ~= '-' then
if v1.rank == "preferred" and v1.qualifiers and v1.qualifiers.P2096 then
for _, lg in ipairs(fb.fblist(lang, true)) do
local imglbls = v1.qualifiers.P2096
if entity.labels[lg] then
for k2, v2 in pairs(imglbls) do
return entity.labels[lg].value
if v2.datavalue.value.language == lang then
imglbl = v2.datavalue.value.text
break
end
end
end
end
end
end
else
-- if we don't find one, look for an image with 'normal' rank
if entity.labels[lang] then
if (not imglbl) then
return entity.labels[lang].value
for k1, v1 in pairs(imgs) do
if v1.rank == "normal" and v1.qualifiers and v1.qualifiers.P2096 then
local imglbls = v1.qualifiers.P2096
for k2, v2 in pairs(imglbls) do
if v2.datavalue.value.language == lang then
imglbl = v2.datavalue.value.text
break
end
end
end
end
end
end
end
end
end
return imglbl
else
return input_parm
end
end
return defaultLabel(entity, default)
end
end


-- This is used to get the QIDs of all of the values of a property, as a comma separated list if multiple values exist
function p._getDescription(entity, lang, fallback)
-- Usage: {{#invoke:Wikidata |getPropertyIDs |<PropertyID> |FETCH_WIKIDATA}}
if not entity then
-- Usage: {{#invoke:Wikidata |getPropertyIDs |<PropertyID> |<InputParameter> |qid=<QID>}}
return i18n('no description')
 
end
p.getPropertyIDs = function(frame)
if not lang then
local go, errorOrentity, propclaims = parseInput(frame)
lang = defaultlang
if not go then
return errorOrentity
end
end
local entity = errorOrentity
if type(entity) ~= 'table' and lang == defaultlang then
-- if wiki-linked value collect the QID in a table
local description, lg = mw.wikibase.getDescriptionWithLang(entity)
if (propclaims[1] and propclaims[1].mainsnak.snaktype == "value" and propclaims[1].mainsnak.datavalue.type == "wikibase-entityid") then
if description and (fallback ~= '-' or lg == lang) then
local out = {}
return description
for k, v in pairs(propclaims) do
out[#out + 1] = "Q" .. v.mainsnak.datavalue.value["numeric-id"]
end
end
return table.concat(out, ", ")
else
else
-- not a wikibase-entityid, so return empty
entity = p.getEntity(entity)
return ""
if entity and entity.descriptions then
if fallback ~= '-' then
for _, lg in ipairs(fb.fblist(lang, true)) do
if entity.descriptions[lg] then
return entity.descriptions[lg].value
end
end
else
if entity.descriptions[lang] then
return entity.descriptions[lang].value
end
end
end
end
end
return i18n('no description')
end
end


-- returns the page id (Q...) of the current page or nothing of the page is not connected to Wikidata
local function formattedLabel(label, entity, args)
function p.pageId(frame)
local link = getLink(entity, args.link, args.lang)
return mw.wikibase.getEntityIdForCurrentPage()
if not link then
link = getLink(entity, defaultlink, args.lang)
end
if not link then
return label
else
return '[[' .. link .. '|' .. label .. ']]'
end
end
end


function p.claim(frame)
function p.formatEntity( entity, args )
local property = frame.args[1] or ""
local id = frame.args["id"]
local qualifierId = frame.args["qualifier"]
local parameter = frame.args["parameter"]
local list = frame.args["list"]
local references = frame.args["references"]
local showerrors = frame.args["showerrors"]
local default = frame.args["default"]
if default then showerrors = nil end
 
-- get wikidata entity
local entity = mw.wikibase.getEntity(id)
if not entity then
if not entity then
if showerrors then return printError("entity-not-found") else return default end
return nil
end
end
-- fetch the first claim of satisfying the given property
if not args then
local claims = findClaims(entity, property)
args = {}
if not claims or not claims[1] then
if showerrors then return printError("property-not-found") else return default end
end
end
local label = p._getLabel(entity, args.lang, 'id', args.fallback)
return formattedLabel(label, entity, args)
end


-- get initial sort indices
function p.getLabel(frame) -- simple for simple templates like {{Q|}}}
local sortindices = {}
local args = frame.args
for idx in pairs(claims) do
local entity = args.entity
sortindices[#sortindices + 1] = idx
local lang = args.lang
if not entity then
return i18n('invalid-id')
end
end
-- sort by claim rank
 
local comparator = function(a, b)
if string.sub(entity, 1, 10) == 'Property:P' then
local rankmap = { deprecated = 2, normal = 1, preferred = 0 }
entity = string.sub(entity, 10)
local ranka = rankmap[claims[a].rank or "normal"] .. string.format("%08d", a)
elseif string.sub(entity, 1, 8) == 'Lexeme:L' then
local rankb = rankmap[claims[b].rank or "normal"] .. string.format("%08d", b)
entity = string.sub(entity, 8)
return ranka < rankb
elseif not ({L = 1, P = 1, Q = 1})[string.sub(entity, 1, 1)] or not tonumber(string.sub(entity, 2)) then
return i18n('invalid-id')
end
end
table.sort(sortindices, comparator)


local result
if not args.link or args.link == '' or args.link == '-' then -- by default: no link
local error
if lang == '' then
if list then
lang = defaultlang
local value
-- iterate over all elements and return their value (if existing)
result = {}
for idx in pairs(claims) do
local claim = claims[sortindices[idx]]
value, error = getValueOfClaim(claim, qualifierId, parameter)
if not value and showerrors then value = error end
if value and references then value = value .. getReferences(frame, claim) end
result[#result + 1] = value
end
end
result = table.concat(result, list)
return p._getLabel(entity, lang, args.default, args.fallback)
else
else
-- return first element
return p.formatEntity(entity, args)
local claim = claims[sortindices[1]]
result, error = getValueOfClaim(claim, qualifierId, parameter)
if result and references then result = result .. getReferences(frame, claim) end
end
end
end


if result then return result else
function p._formatStatements( args )--Format statements and concat them cleanly
if showerrors then return error else return default end
if args.value == '-' then
return nil
end
end
--If a value is already set, use it
if args.value and args.value ~= '' then
return args.value
end
local valuetable = p.stringTable(args)
return tableToText(valuetable, args)
end
end


-- look into entity object
function p.showQualifier( args )
function p.ViewSomething(frame)
local qualifs = args.qualifiers or args.qualifier
local f = (frame.args[1] or frame.args.id) and frame or frame:getParent()
if type(qualifs) == 'string' then
local id = f.args.id
qualifs = mw.text.split(qualifs, ',')
if id and (#id == 0) then
end
id = nil
if not qualifs then
return formatError( 'property-param-not-provided' )
end
end
local data = mw.wikibase.getEntity(id)
local claims = p.getClaims(args)
if not data then
if not claims then
return nil
return nil
end
end
local str = ''
local new
for _, cl in ipairs(claims) do
new = p.getFormattedQualifiers(cl, qualifs, args) or ''
str = str .. new
end
return str
end


local i = 1
function p._formatAndCat(args)
while true do
local val = p._formatStatements(args)
local index = f.args[i]
if val then
if not index then
return val .. addTrackingCat(args.property)
if type(data) == "table" then
end
return mw.text.jsonEncode(data, mw.text.JSON_PRESERVE_KEYS + mw.text.JSON_PRETTY)
return nil
else
end
return tostring(data)
 
end
function p.getTheDate(args)
local claims = p.getClaims(args)
if not claims then
return nil
end
local formattedvalues = {}
for _, cl in ipairs(claims) do
table.insert(formattedvalues, p.getFormattedDate(cl))
end
local val = linguistic.conj(formattedvalues)
if val and args.addcat == true then
return val .. addTrackingCat(args.property)
else
return val
end
end
 
--[[
Returns a Wikipedia page name (with interwiki prefix) which is equivalent page
to given page in given language.
Args:
* sourceTitle: wiki page title which exists on sourceLang Wikipedia.
* sourceLang: language code of Wikipedia where `sourceTitle` is an
article. Defaults to `en`.
* targetLang: language code of Wikipedia from which you want to get
equivalent article to `sourceTitle`. Defaults to `defaultlang`.
Bug: return value should not start with a colon because of T14974
]]
function p._getEquivalentWPArticle(sourceTitle, sourceLang, targetLang)
if not sourceLang or sourceLang == '' then
sourceLang = 'en'
end
if not targetLang or targetLang == '' then
targetLang = defaultlang
end
local sourceLink = ' :' .. sourceLang .. ':' .. sourceTitle
if sourceLang == targetLang then
return sourceLink
end
local sourceWiki = sourceLang .. 'wiki' -- e.g. "enwiki" is global site id for English Wikipedia
local id = mw.wikibase.getEntityIdForTitle( sourceTitle, sourceWiki )
if not id then
return sourceLink --source page has no linked Wikidata element, let’s keep it
end
local fallbackLangs = mw.language.getFallbacksFor(targetLang)
table.insert(fallbackLangs, 1, targetLang) --keeps targetLang as first lang to try
for _, fallbackLang in ipairs(fallbackLangs) do
local existingEquivalent = getLink(id, 'anywikipedia', fallbackLang)
if existingEquivalent then
return ' ' .. existingEquivalent
end
end
end
return sourceLink --source page has no interwiki, let’s keep it
end
function p.formatStatements( args )
return p._formatStatements( args )
end
function p.getEntityFromId(id)
return p.getEntity(id)
end
----------------------------------------
-- Functions callable from a template --
----------------------------------------
function p.getaDate(frame)
return p.getTheDate(frame.args)
end
function p.getQualifier(frame)
return p.showQualifier(frame.args)
end
function p.getDescription(frame) -- simple for simple templates like {{Q|}}}
local entity = frame.args.entity
if not entity then
return i18n('invalid-id')
end
local lang = frame.args.lang
local fallback = frame.args.fallback
return p._getDescription(entity, lang, fallback)
end


data = data[index] or data[tonumber(index)]
function p.formatStatementsE(frame)
if not data then
local args = {}
return
if frame == mw.getCurrentFrame() then
args = frame:getParent().args -- paramètres du modèle appelant (est-ce vraiment une bonne idée ?)
for k, v in pairs(frame.args) do
args[k] = v
end
end
else
args = frame
end
return p._formatStatements( args )
end


i = i + 1
function p.formatAndCat(frame)
local args = {}
if frame == mw.getCurrentFrame() then
args = frame:getParent().args -- paramètres du modèle appelant (est-ce vraiment une bonne idée ?)
for k, v in pairs(frame.args) do
args[k] = v
end
else
args = frame
end
end
return p._formatAndCat( args )
end
end


-- getting sitelink of a given wiki
function p.labelOf(frame)
-- get sitelink of current item if qid not supplied
local id = frame.args[1]
function p.getSiteLink(frame)
-- returns the label of the given entity/property id
local qid = frame.args.qid
-- if no id is given, the one from the entity associated with the calling Wikipedia article is used
if qid == "" then qid = nil end
if not id then
local f = mw.text.trim( frame.args[1] or "")
local entity = mw.wikibase.getEntity()
local entity = mw.wikibase.getEntity(qid)
if not entity then return printError("entity-not-found") end
if not entity then
id = entity.id
return
end
local link = entity:getSitelink( f )
if not link then
return
end
end
return link
return mw.wikibase.label(id)
end
end


function p.Dump(frame)
function p.getLink(frame)
local f = (frame.args[1] or frame.args.id) and frame or frame:getParent()
local entity = frame.args.entity
local data = mw.wikibase.getEntity(f.args.id)
if not entity then
if not data then
return i18n('invalid-id')
return i18n.warnDump
end
end


local i = 1
local fallback = frame.args.fallback
while true do
local lang = frame.args.lang
local index = f.args[i]
local label = frame.args.label
if not index then
local typelink = 'anywikipedia'
return "<pre>"..mw.dumpObject(data).."</pre>".. i18n.warnDump
end


data = data[index] or data[tonumber(index)]
if (fallback == '') or (fallback == '-') then typelink = 'wikipedia' end
if not data then
return i18n.warnDump
end


i = i + 1
if (label == '') or (label == '-') then
label = p._getLabel(entity, lang, 'id', fallback)
end
end
return '[[' .. getLink(entity, typelink, lang) .. '|' .. label .. ']]'
end
--[[
Returns a Wikipedia page name with interwiki prefix which is equivalent page
to given page in given language.
Args:
* sourceTitle: wiki page title which exists on sourceLang Wikipedia.
* sourceLang: language code of Wikipedia where `sourceTitle` is an
article. Defaults to `en`.
* targetLang: language code of Wikipedia from which you want to get
equivalent article to `sourceTitle`. Defaults to `defaultlang`.
]]
function p.getEquivalentWPArticle(frame)
return p._getEquivalentWPArticle(
frame.args.sourceTitle,
frame.args.sourceLang,
frame.args.targetLang
)
end
end


return p
return p

०२:०९, ६ जनवरी २०२२ का अवतरण

"इस मॉड्यूल हेतु प्रलेख Module:Wikidata/doc पर बनाया जा सकता है"

--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua

local p = {}

local linguistic = require('Module:Linguistic')
--local formatDate = require('Module:Complex date') only loaded when needed to save memory in large pages like Wikidata:List of properties/all
local fb = require('Module:Fallback')
local i18nmessages = mw.loadData('Module:i18n/wikidata')

-- Wiki-specific parameters
local defaultlang = mw.getCurrentFrame():preprocess("{{int:lang}}")
local defaultlink = 'wikidata'

local function i18n(str)
	local message = i18nmessages[str]
	if type(message) == 'string' then
		return message
	end
	return fb._langSwitch(message, defaultlang) .. ''
end

local function formatError( key, text )
	return error(i18n(key) .. (text or ''))
end

local function addTrackingCat(prop, cat)
	if not prop and not cat then
		return error("no property provided")
	end
	if not cat then
		cat = i18nmessages.trackingcat .. '/' .. string.upper(prop)
	end
	return '[[Category:' .. cat .. ']]'
end

local function removeBlanks(args)
	for i, j in pairs(args) do -- does not work ??
		if (j == '') or (j == '-') then args[i] = nil end
	end
	return args
end

local function formatTheUnknown() -- voir si on peut accorder/adapter l'usage de "inconnu"
	return i18n('somevalue')
end

local function isSpecial(snak)
	return snak.snaktype ~= 'value'
end

local function sameValue(snak, target)
	return not isSpecial(snak) and p.getRawvalue(snak) == target
end

local function showLang(statement, str) -- TODO (not yet in proper format)
	--adds a lang indication at the start of the string, based on data in statement
	local mainsnak = statement.mainsnak
	if isSpecial(mainsnak) then
		return str
	end

	local langlist = {}
	if mainsnak.datavalue.type == 'monolingualtext' then
		langlist = {mainsnak.datavalue.value.language}
	elseif statement.qualifiers and statement.qualifiers.P407 then
		local convertlangcode = mw.loadData('Module:Dictionary/lang codes')
		for i, j in pairs( statement.qualifiers.P407 ) do
			if not isSpecial(j) then
				local val = convertlangcode[j.datavalue.value['numeric-id']]
				table.insert(langlist, val)
			end
		end
	end
	if #langlist == 0 then
		return str
	else
		return '(' .. table.concat(langlist) .. ')' .. str
	end
end

function p.getEntity( val )
	if type(val) == 'table' then
		return val
	end
	return mw.wikibase.getEntityObject(val)
end

-- DATE FUNCTIONS
local function splitTimestamp(timestamp, calendar)
	local pattern = "(%W)(%d+)%-(%d+)%-(%d+)"
	local era, year, month, day = timestamp:match(pattern)

	if calendar == 'julian' then
	--todo  year, month, day = formatdate.gregorianToJulian( era .. year, month, day )
	end

	return {day = day, month = month, year = year, era = era, timestamp = timestamp, type = 'dateobject'}
end

local function rangeObject(begin, ending)
	local timestamp
	if begin then
		timestamp = begin.timestamp
	elseif ending then
		timestamp = ending.timestamp
	end
	return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'}
end

local function dateObject(orig, params) -- transforme un snak en un nouvel objet utilisable par Module:Date complexe
	if not params then
		params = {}
	end

	local newobj = splitTimestamp(orig.time, orig.calendar) -- initalise l'object en mettant la valeur des dates

	newobj.precision = params.precision or orig.precision
	newobj.type = 'dateobject'
	return newobj
end

local function formatDatepoint(obj, params) -- TO IMPROVE
	if not obj then
		return nil
	end
	local formatDate = require('Module:Complex date')
	local lang = params.lang or defaultlang
	local precision = math.min(obj.precision, params.precision or 15) -- if we don't want to show the value to its full detail
	if precision >= 11 then
		return formatDate.complex_date{args={date1 = obj.year .. '-' .. obj.month .. '-' .. obj.day, lang= lang}}
	elseif precision == 10 then
		return formatDate.complex_date{args={date1 = obj.year .. '-' .. obj.month, lang= lang}}
	elseif precision == 9 then
		return formatDate.complex_date{args={date1 = tostring(obj.year), lang= lang}}
	elseif precision == 8 then
		return formatDate.complex_date{args={date1 = string.sub(tostring(obj.year), 1, 3) .. '0', lang = lang, precision = 'decade'}}
	elseif precision == 7 then
		return formatDate.complex_date{args={date1 = string.sub(tostring(obj.year + 100), 1, 2), lang = lang, precision = 'century'}}
	end
	return nil
end

local function formatDaterange(obj, params) --TODO
	local begin = formatDatepoint(obj.begin, params) or ''
	local ending = formatDatepoint(obj.ending, params) or ''
	return begin .. '-' .. ending
end

local function objectToText(obj, params)
	if obj.type == 'dateobject' then
		return formatDatepoint(obj, params)
	elseif obj.type == 'rangeobject' then
		return formatDaterange(obj, params)
	end
	return nil
end

local function tableToText(values, params) -- takes a list of already formatted values and make them a text
	if not values then
		return nil
	end
	return linguistic.conj(values, params.lang or defaultlang, params.conjtype)--linguistic.conj( values, params.lang, params.conjtype )
end

function p.getDate(obj)
--[[
returns an object containing a timestamp for easy sorting, and other data
	possible types of object:
		dateobject
			{timestamp = string, year = number, month = number, day = number, calendar = string}
		rangeobject
			{timestamp = string, begin = dateobject, ending = dateobject}
]]--
	if not obj then
		return nil
	end
	if type(obj) == 'string' then
		obj = p.getEntity(obj)
	end

	-- if obj is a statement with date, get it
	if obj.mainsnak and not isSpecial(obj.mainsnak) and obj.mainsnak.datatype == 'time' then
		return dateObject(obj.mainsnak.datavalue.value)
	end

	-- else preload relevant data
	local qualifs = obj.qualifiers -- when obj is a statement, look in qualifiers
	local claims = obj.claims -- when obj is an item, look in claims

	local pointprop = {'P585', 'P571'} -- dates corresponding to a punctual fact
	local beginprop = {'P580', 'P569'} -- start date, birth date == start of a date range
	local endingprop = {'P582', 'P570'}

	local function getval(prop)
		local val
		if claims and claims[prop] and not isSpecial(claims[prop][1].mainsnak) then
			val = claims[prop][1].mainsnak.datavalue.value
		elseif qualifs and qualifs[prop] and not isSpecial(qualifs[prop][1]) then
			val = qualifs[prop][1].datavalue.value
		end
		if val then
			return dateObject(val)
		end
		return nil
	end

	for i, prop in pairs(pointprop) do
		local val = getval(prop)
		if val then return val end
	end
	--if no date has not been found, look for startdate or enddate
	local begin, ending
	for i, prop in pairs(beginprop) do
		begin = getval(prop)
		if begin then
			break
		end
	end
	for i, prop in pairs(endingprop) do
		ending = getval(prop)
		if ending then
			break
		end
	end
	if begin or ending then
		return rangeObject(begin, ending)
	end
	return nil
end

function p.getFormattedDate(statement, params)
	local datetable = p.getDate(statement)
	if not datetable then
		return nil
	end
	return objectToText(datetable, params)
end

local function hasTargetValue(claim, target)
	if target == nil then
		return true
	end
	return sameValue(claim.mainsnak, target)
end

local function hasRank(claim, target)
	if target == 'valid' then
		return hasRank(claim, 'preferred') or hasRank(claim, 'normal')
	else
		return claim.rank == target
	end
end

local function bestRanked(claims)
	if not claims then
		return nil
	end
	local preferred, normal = {}, {}
	for _, j in ipairs(claims) do
		if j.rank == 'preferred' then
			table.insert(preferred, j)
		elseif j.rank == 'normal' then
			table.insert(normal, j)
		end
	end
	if #preferred > 0 then
		return preferred
	else
		return normal
	end
end

local function hasQualifier(claim, qualifier, qualifiervalues)
	if not qualifier then -- si aucun qualificatif est demandé, ça passe
		return true
	end

	qualifier = string.upper(qualifier)
	if not claim.qualifiers or not claim.qualifiers[qualifier] then
		return false
	end

	if type(qualifiervalues) == 'string' then
		qualifiervalues = mw.text.split(qualifiervalues, ',')
	end

	if (not qualifiervalues) or (qualifiervalues == {}) then
		return true -- si aucune valeur spécifique n'est exigée
	end

	for _, j in ipairs(claim.qualifiers[qualifier]) do
		for _, l in ipairs(qualifiervalues) do
			if p.sameValue(j, l) then
				return true
			end
		end
	end
	return false
 end

local function hasSource(statement, source, sourceproperty)
	if not statement.references then
		return false
	end
	sourceproperty = string.upper(sourceproperty or 'P248')
	local sourcevalue = string.upper(source or '')
	for _, ref in ipairs(statement.references) do
		for prop, content in pairs(ref.snaks) do
			if prop == sourceproperty then
				if sourcevalue == '' then
					return true
				else
					for _, k in ipairs(content) do
						if sameValue(k, source) then
							return true
						end
					end
				end
			end
		end
	end
	return false
end

local function hasDate(statement)
	if not statement.qualifiers then
		return false
	end
	local dateprops = {'P580', 'P585', 'P582'}
	for i, prop in pairs(dateprops) do
		if statement.qualifiers[prop] then
			return true
		end
	end
	return false
end

local function isInLanguage(snak, lang) -- ne fonctionne que pour les monolingualtext / étendre aux autres types en utilisant les qualifiers ?
	return not isSpecial(snak) and snak.datavalue.type == 'monolingualtext' and snak.datavalue.value.language == lang
end

local function numval(claims, numval) -- retourn les numval premières valeurs de la table claims
	local numval = tonumber(numval) or 0 -- raise an error if numval is not a positive integer ?
	if #claims <= numval then
		return claims
	end
	local newclaims = {}
	while #newclaims < numval do
		table.insert(newclaims, claims[#newclaims + 1])
	end
	return newclaims
end

local function wikipediaLink(entity, lang)
	local link

	local lg = string.gsub(lang, '-', '_')
	if (lg == 'be_tarask') then lg = 'be_x_old' end
	if (lg == 'nb') then lg = 'no' end

	if type(entity) == 'table' then
		link = entity:getSitelink(lg .. 'wiki')
	else
		link = mw.wikibase.getSitelink(entity, lg .. 'wiki')
	end
	if link then
		return ':' .. lang .. ':' .. link
	end
	return nil
end

local function getLink(entity, typelink, lang)
	if typelink == 'wikidata' then
		if type(entity) == 'table' then
			if entity.type == 'property' then
				return 'd:P:' .. entity.id
			elseif entity.type == 'lexeme' then
				return 'd:L:' .. entity.id
			else
				return 'd:' .. entity.id
			end
		else
			if string.sub(entity, 1, 1) == 'P' then
				return 'd:P:' .. entity
			elseif string.sub(entity, 1, 1) == 'L' then
				return 'd:L:' .. entity
			else
				return 'd:' .. entity
			end
		end

	elseif typelink == 'wikipedia' then
		return wikipediaLink(entity, lang or defaultlang)

	elseif typelink == 'anywikipedia' then
		for _, lg in ipairs(fb.fblist(lang or defaultlang, true)) do
			local link = wikipediaLink(entity, lg)
			
			if link then
				return link
			end
		end
	end
	return nil
end

function p.comparedate(a, b) -- returns true if a is earlier than B or if a has a date but not b
	if a and b then
		return a.timestamp < b.timestamp
	elseif a then
		return true
	end
	return false
end

function p.chronosort(objs, inverted)
	table.sort(objs, function(a, b)
		local timeA = p.getDate(a)
		local timeB = p.getDate(b)
		if inverted then
			return p.comparedate(timeB, timeA)
		else
			return p.comparedate(timeA, timeB)
		end
	end)

	return objs
end

function p.sortclaims(claims, sorttype)
	if type(sorttype) == 'function' then
		table.sort(claims, sorttype)
	elseif sorttype == 'chronological' then
		return p.chronosort(claims)
	elseif sorttype == 'inverted' then
		return p.chronosort(claims, true)
	end
	return claims
end

function p.getRawvalue(snak)
	return p.getDatavalue(snak, { displayformat = 'raw' })
end

function p.showentity(entity, lang)
	if not entity then
		return nil
	end
	local label, link, id = p._getLabel(entity, lang), getLink(entity, 'wikidata')
	if type(entity) == 'table' then
		id = entity.id
	else
		id = entity
	end
	return '[[' .. link .. '|' .. label .. ']] <small>(' .. id .. ')</small>'
end

function p.getDatavalue(snak, params)
	if isSpecial(snak) then
		return nil
	end

	if not params then
		params = {}
	end

	local displayformat = params.displayformat
	local datatype = snak.datavalue.type
	local value = snak.datavalue.value

	if datatype == 'wikibase-entityid' then
		if type(displayformat) == 'function' then
			return displayformat(snak, params)
		end
		local id = snak.datavalue.value.id
		if displayformat == 'raw' then
			return id
		elseif displayformat == 'wikidatastyle' then
			return p.showentity(id, params.lang)
		else
			return p.formatEntity(id, params)
		end

	elseif datatype == 'string' then
		local showntext = params.showntext
		if displayformat == 'weblink' then
			if showntext then
				return '[' .. value .. ' ' .. showntext .. ']'
			else
				return value
			end
		end
		if snak.datatype == 'math' and displayformat ~= 'raw' then
			value = mw.getCurrentFrame():extensionTag('math', value)
		else
			if params.urlpattern then
				showntext = mw.text.nowiki(showntext or value)
				value = mw.ustring.gsub(value, '%%', '%%%%') -- escape '%'
				value = '[' .. mw.ustring.gsub(mw.ustring.gsub(params.urlpattern, '$1', value), ' ', '%%20') .. ' ' .. showntext .. ']'
			elseif params.pattern then
				local pattern = mw.ustring.gsub(params.pattern, '%%', '%%%%')
				value = mw.ustring.gsub(value, '%%', '%%%%')
				value = mw.ustring.gsub(pattern, '$1', value)
			else
				if displayformat ~= 'raw' then
					value = mw.text.nowiki(value)
				end
			end
		end
		return value

	elseif datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
		if displayformat == 'raw' then
			return value.time
		else
			return objectToText(dateObject(value), params)
		end

	elseif datatype == 'globecoordinate' then
		-- retourne une table avec clés latitude, longitude, précision et globe à formater par un autre module (à changer ?)
		if displayformat == 'latitude' then
			return value.latitude
		elseif displayformat == 'longitude' then
			return value.longitude
		elseif displayformat == 'qualifier' then
			local coord = require 'Module:Coordinates'
			value.globe = mw.loadData('Module:Wikidata/Globes')[value.globe]
			value.precision = nil
			return coord._coord(value)
		else
			value.globe = mw.loadData('Module:Wikidata/Globes')[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohack
			return value -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?
		end

	elseif datatype == 'quantity' then -- todo : gérer les paramètre précision
		if displayformat == 'raw' then
			return tonumber(value.amount)
		else
			local formatNum = require 'Module:Formatnum'
			local number = formatNum.formatNum(value.amount, params.lang)
			local unit = mw.ustring.match(value.unit, '(Q%d+)')
			if unit then
				number = number .. '&nbsp;' .. p.formatEntity(unit, params)
			end
			return number
		end
	elseif datatype == 'monolingualtext' then
		return '<span lang="' .. value.language .. '">' .. value.text .. '</span>'
	else
		return formatError( 'unknown-datavalue-type', datatype )
	end
end

local function getMultipleClaims(args)
	local newargs = args
	local claims = {}
	for i, j in pairs(args.property) do
		newargs.property = j
		local newclaims = p.getClaims(args)
		if newclaims then
			for k, l in pairs(newclaims) do
				table.insert(claims, l)
			end
		end
	end
	return claims
end

function p.getClaims( args ) -- returns a table of the claims matching some conditions given in args
	args = removeBlanks(args)
	if not args.property then
		return formatError( 'property-param-not-provided' )
	end
	if type(args.property) == 'table' then
		return getMultipleClaims(args)
	end
	--Get entity
	if args.item then -- synonyms
		args.entity = args.item
	end
	local property = string.upper(args.property)
	local allClaims
	local entity = args.entity
	if type(entity) == 'table' then
		allClaims = (entity and entity.claims and entity.claims[property]) or {}
	else
		allClaims = mw.wikibase.getAllStatements(entity, property)
	end
	if #allClaims == 0 then
		return nil
	end

	if not args.rank then
		args.rank = 'best'
	end
	local claims = {}
	for _, statement in ipairs(allClaims) do
		if
			(
			not args.excludespecial
			or
			not (isSpecial(statement.mainsnak))
		)
		and
		(
			not args.targetvalue
			or
			hasTargetValue(statement, args.targetvalue)
		)
		and
		(
			not args.qualifier
			or
			hasQualifier(statement, args.qualifier, args.qualifiervalues or args.qualifiervalue)
		)
		and
		(
			not args.withsource or args.withsource == '-'
			or
			hasSource(statement, args.withsource, args.sourceproperty)
		)
		and
		(
			not args.isinlanguage
			or
			isInLanguage(statement.mainsnak, args.isinlanguage)
		)
		and
		(
			args.rank == 'best' -- rank == best est traité à a fin
			or
			hasRank(statement, args.rank)
		)
		then
			table.insert(claims, statement)
		end
	end
	if #claims == 0 then
		return nil
	end
	if args.rank == 'best' then
		claims = bestRanked(claims)
	end
	if args.sorttype then
		claims = p.sortclaims(claims, args.sorttype)
	end

	if args.numval then
		return numval(claims, args.numval)
	end
	return claims
end

function p.formatClaimList(claims, args)
	if not claims then
		return nil
	end
	for i, j in pairs(claims) do
		claims[i] = p.formatStatement(j, args)
	end
	return claims
end

function p.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation
	local claims = p.getClaims(args)
	return p.formatClaimList(claims, args)
end

local function getQualifiers(statement, qualifs, params)
	if not statement.qualifiers then
		return nil
	end
	local vals = {}
	for i, j in pairs(qualifs) do
		j = string.upper(j)
		if statement.qualifiers[j] then
			local inserted = false
			if statement.qualifiers[j][1].datatype == 'monolingualtext' then
				local in_preferred_lang
				for _, language in ipairs(fb.fblist(params.lang or defaultlang, true)) do
					for _, snak in ipairs(statement.qualifiers[j]) do
						if isInLanguage(snak, language) then
							in_preferred_lang = snak
							break
						end
					end
					if in_preferred_lang then
						break
					end
				end
				if in_preferred_lang then
					table.insert(vals, in_preferred_lang)
					inserted = true
				end
			end
			if not inserted then
				for _, snak in pairs(statement.qualifiers[j]) do
					table.insert(vals, snak)
				end
			end
		end
	end
	if #vals == 0 then
		return nil
	end
	return vals
end

function p.getFormattedQualifiers(statement, qualifs, params)
	if not params then params = {} end
	local qualiftable = getQualifiers(statement, qualifs, params)
	if not qualiftable then
		return nil
	end
	for i, j in pairs(qualiftable) do
		local params = params
		if j.datatype == 'globe-coordinate' then
			params.displayformat = 'qualifier'
		end
		qualiftable[i] = p.formatSnak(j, params)
	end
	return linguistic.conj(qualiftable, params.lang or defaultlang)
end

function p.formatStatement( statement, args )
	if not statement.type or statement.type ~= 'statement' then
		return formatError( 'unknown-claim-type', statement.type )
	end
	if not args then args = {} end
	local lang = args.lang or defaultlang
	local str = p.formatSnak( statement.mainsnak, args )
	if args.showlang == true then
		str = showLang(statement, str)
	end

	local qualifs = args.showqualifiers
	if qualifs then
		if type(qualifs) == 'string' then
			qualifs = mw.text.split(qualifs, ',')
		end
		local foundvalues = p.getFormattedQualifiers(statement, qualifs, args)
		if foundvalues then
			if args.delimiter then
				str = str .. args.delimiter .. foundvalues
			else
				str = str .. linguistic.inparentheses(foundvalues, lang)
			end
		end
	end

	if args.showdate then -- when "showdate and p.chronosort are both set, date retrieval is performed twice
		local timedata = p.getDate(statement)
		if timedata then
			local formatteddate = objectToText(timedata, args)
			formatteddate = linguistic.inparentheses(formatteddate, lang)
			str = str .. '<small>' .. formatteddate ..'</small>'
		end
	end

	if args.showsource and statement.references then
		local cite = require 'Module:Cite'
		local frame = mw.getCurrentFrame()
		local sourcestring = ''
		for _, ref in ipairs(statement.references) do
			if ref.snaks.P248 then
				for j, source in pairs(ref.snaks.P248) do
					if not isSpecial(source) then
						local page
						if ref.snaks.P304 and not isSpecial(ref.snaks.P304[1]) then
							page = ref.snaks.P304[1].datavalue.value
						end
						local s = cite.citeitem(source.datavalue.value.id, lang, page)
						s = frame:extensionTag( 'ref', s )
						sourcestring = sourcestring .. s
					end
				end
			elseif ref.snaks.P854 and not isSpecial(ref.snaks.P854[1]) then
				s = frame:extensionTag( 'ref', p.getDatavalue(ref.snaks.P854[1]) )
				sourcestring = sourcestring .. s
			end
		end
		str = str .. sourcestring
	end
	return str
end

function p.getmainid(claim)
	if claim and not isSpecial(claim.mainsnak) then
		return claim.mainsnak.datavalue.value.id
	end
	return nil
end

function p.formatSnak(snak, params)
	--local params = params or {} pour faciliter l'appel depuis d'autres modules
	if snak.snaktype == 'value' then
		return p.getDatavalue(snak, params)
	elseif snak.snaktype == 'somevalue' then
		return formatTheUnknown()
	elseif snak.snaktype == 'novalue' then
		return i18n('novalue') --todo
	else
		return formatError( 'unknown-snak-type', snak.snaktype )
	end
end

local function defaultLabel(entity, displayformat) -- label when no label is available
	if displayformat == 'id' then
		if type(entity) ~= 'table' then
			return entity
		else
			return entity.id
		end
	end
	return i18n('no-label')
end

function p._getLabel(entity, lang, default, fallback)
	if not entity then
		return nil
	end
	if not lang then
		lang = defaultlang
	end
	if type(entity) ~= 'table' and lang == defaultlang then
		local label, lg = mw.wikibase.getLabelWithLang(entity)
		if label and (fallback ~= '-' or lg == lang) then
			return label
		end
	else
		entity = p.getEntity(entity)
		if entity and entity.labels then
			if fallback ~= '-' then
				for _, lg in ipairs(fb.fblist(lang, true)) do
					if entity.labels[lg] then
						return entity.labels[lg].value
					end
				end
			else
				if entity.labels[lang] then
					return entity.labels[lang].value
				end
			end
		end
	end
	return defaultLabel(entity, default)
end

function p._getDescription(entity, lang, fallback)
	if not entity then
		return i18n('no description')
	end
	if not lang then
		lang = defaultlang
	end
	if type(entity) ~= 'table' and lang == defaultlang then
		local description, lg = mw.wikibase.getDescriptionWithLang(entity)
		if description and (fallback ~= '-' or lg == lang) then
			return description
		end
	else
		entity = p.getEntity(entity)
		if entity and entity.descriptions then
			if fallback ~= '-' then
				for _, lg in ipairs(fb.fblist(lang, true)) do
					if entity.descriptions[lg] then
						return entity.descriptions[lg].value
					end
				end
			else
				if entity.descriptions[lang] then
					return entity.descriptions[lang].value
				end
			end
		end
	end
	return i18n('no description')
end

local function formattedLabel(label, entity, args)
	local link = getLink(entity, args.link, args.lang)
	if not link then
		link = getLink(entity, defaultlink, args.lang)
	end
	if not link then
		return label
	else
		return '[[' .. link .. '|' .. label .. ']]'
	end
end

function p.formatEntity( entity, args )
	if not entity then
		return nil
	end
	if not args then
		args = {}
	end
	local label = p._getLabel(entity, args.lang, 'id', args.fallback)
	return formattedLabel(label, entity, args)
end

function p.getLabel(frame) -- simple for simple templates like {{Q|}}}
	local args = frame.args
	local entity = args.entity
	local lang = args.lang
	if not entity then
		return i18n('invalid-id')
	end

	if string.sub(entity, 1, 10) == 'Property:P' then
		entity = string.sub(entity, 10)
	elseif string.sub(entity, 1, 8) == 'Lexeme:L' then
		entity = string.sub(entity, 8)
	elseif not ({L = 1, P = 1, Q = 1})[string.sub(entity, 1, 1)] or not tonumber(string.sub(entity, 2)) then
		return i18n('invalid-id')
	end

	if not args.link or args.link == '' or args.link == '-' then -- by default: no link
		if lang == '' then
			lang = defaultlang
		end
		return p._getLabel(entity, lang, args.default, args.fallback)
	else
		return p.formatEntity(entity, args)
	end
end

function p._formatStatements( args )--Format statements and concat them cleanly
	if args.value == '-' then
		return nil
	end
	--If a value is already set, use it
	if args.value and args.value ~= '' then
		return args.value
	end
	local valuetable = p.stringTable(args)
	return tableToText(valuetable, args)
end

function p.showQualifier( args )
	local qualifs = args.qualifiers or args.qualifier
	if type(qualifs) == 'string' then
		qualifs = mw.text.split(qualifs, ',')
	end
	if not qualifs then
		return formatError( 'property-param-not-provided' )
	end
	local claims = p.getClaims(args)
	if not claims then
		return nil
	end
	local str = ''
	local new
	for _, cl in ipairs(claims) do
		new = p.getFormattedQualifiers(cl, qualifs, args) or ''
		str = str .. new
	end
	return str
end

function p._formatAndCat(args)
	local val = p._formatStatements(args)
	if val then
		return val .. addTrackingCat(args.property)
	end
	return nil
end

function p.getTheDate(args)
	local claims = p.getClaims(args)
	if not claims then
		return nil
	end
	local formattedvalues = {}
	for _, cl in ipairs(claims) do
		table.insert(formattedvalues, p.getFormattedDate(cl))
	end
	local val = linguistic.conj(formattedvalues)
	if val and args.addcat == true then
		return val .. addTrackingCat(args.property)
	else
		return val
	end
end

--[[
	Returns a Wikipedia page name (with interwiki prefix) which is equivalent page
	to given page in given language.
	
	Args:
	* sourceTitle: wiki page title which exists on sourceLang Wikipedia.
	* sourceLang: language code of Wikipedia where `sourceTitle` is an
	article. Defaults to `en`.
	* targetLang: language code of Wikipedia from which you want to get
	equivalent article to `sourceTitle`. Defaults to `defaultlang`.
	
	Bug: return value should not start with a colon because of T14974
]]
function p._getEquivalentWPArticle(sourceTitle, sourceLang, targetLang)
	if not sourceLang or sourceLang == '' then
		sourceLang = 'en'
	end
	
	if not targetLang or targetLang == '' then
		targetLang = defaultlang
	end
	
	local sourceLink = ' :' .. sourceLang .. ':' .. sourceTitle
	
	if sourceLang == targetLang then
		return sourceLink
	end
	
	local sourceWiki = sourceLang .. 'wiki' -- e.g. "enwiki" is global site id for English Wikipedia
	local id = mw.wikibase.getEntityIdForTitle( sourceTitle, sourceWiki )
	
	if not id then
		return sourceLink --source page has no linked Wikidata element, let’s keep it
	end
	
	local fallbackLangs = mw.language.getFallbacksFor(targetLang)
	table.insert(fallbackLangs, 1, targetLang) --keeps targetLang as first lang to try
	
	for _, fallbackLang in ipairs(fallbackLangs) do
		local existingEquivalent = getLink(id, 'anywikipedia', fallbackLang)
		if existingEquivalent then
			return ' ' .. existingEquivalent
		end
	end
	
	return sourceLink --source page has no interwiki, let’s keep it
end

function p.formatStatements( args )
	return p._formatStatements( args )
end

function p.getEntityFromId(id)
	return p.getEntity(id)
end

----------------------------------------
-- Functions callable from a template --
----------------------------------------

function p.getaDate(frame)
	return p.getTheDate(frame.args)
end

function p.getQualifier(frame)
	return p.showQualifier(frame.args)
end

function p.getDescription(frame) -- simple for simple templates like {{Q|}}}
	local entity = frame.args.entity
	if not entity then
		return i18n('invalid-id')
	end
	local lang = frame.args.lang
	local fallback = frame.args.fallback

	return p._getDescription(entity, lang, fallback)
end

function p.formatStatementsE(frame)
	local args = {}
	if frame == mw.getCurrentFrame() then
		args = frame:getParent().args -- paramètres du modèle appelant (est-ce vraiment une bonne idée ?)
		for k, v in pairs(frame.args) do
			args[k] = v
		end
	else
		args = frame
	end
	return p._formatStatements( args )
end

function p.formatAndCat(frame)
	local args = {}
	if frame == mw.getCurrentFrame() then
		args = frame:getParent().args -- paramètres du modèle appelant (est-ce vraiment une bonne idée ?)
		for k, v in pairs(frame.args) do
			args[k] = v
		end
	else
		args = frame
	end
	return p._formatAndCat( args )
end

function p.labelOf(frame)
	local id = frame.args[1]
	-- returns the label of the given entity/property id
	-- if no id is given, the one from the entity associated with the calling Wikipedia article is used
	if not id then
		local entity = mw.wikibase.getEntity()
		if not entity then return printError("entity-not-found") end
		id = entity.id
	end
	return mw.wikibase.label(id)
end

function p.getLink(frame)
	local entity = frame.args.entity
	if not entity then
		return i18n('invalid-id')
	end

	local fallback = frame.args.fallback
	local lang = frame.args.lang
	local label = frame.args.label
	local typelink = 'anywikipedia'

	if (fallback == '') or (fallback == '-') then typelink = 'wikipedia' end

	if (label == '') or (label == '-') then
		label = p._getLabel(entity, lang, 'id', fallback)
	end

	return '[[' .. getLink(entity, typelink, lang) .. '|' .. label .. ']]'
end

--[[
	Returns a Wikipedia page name with interwiki prefix which is equivalent page
	to given page in given language.
	
	Args:
	* sourceTitle: wiki page title which exists on sourceLang Wikipedia.
	* sourceLang: language code of Wikipedia where `sourceTitle` is an
	article. Defaults to `en`.
	* targetLang: language code of Wikipedia from which you want to get
	equivalent article to `sourceTitle`. Defaults to `defaultlang`.
]]
function p.getEquivalentWPArticle(frame)
	return p._getEquivalentWPArticle(
		frame.args.sourceTitle,
		frame.args.sourceLang,
		frame.args.targetLang
	)
end

return p