Modulo:Wikidata

Da WikiPoesia.
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

La documentazione per questo modulo può essere creata in Modulo:Wikidata/man

--[[
* Modulo per implementare le funzionalità dei template:
* {{Wikidata}}, {{WikidataQ}}, {{WikidataIdx}}, {{WikidataN}}, {{WikidataLabel}}, {{WikidataDescription}}
* {{WikidataLink}}, {{WikidataId}}, {{WikidataTipo}} e {{WikidataIstanza}}.
* Permette di accedere a Wikidata in modo più avanzato rispetto a {{#property}}.

* Per la maggior parte riscritto e ampliato a partire dalla versione iniziale a:
* http://test2.wikipedia.org/w/index.php?title=Module:Wikidata&oldid=52322
]]

-- =============================================================================
-- Non utilizzare mai mw.wikibase.getEntity, per esempio un solo utilizzo di
-- mw.wikibase.getEntity('Q183') fa aumentare di 7 MB l'utilizzo di memoria
-- per Lua ed è molto lenta se ripetuta (unico utilizzo in getDatatype,
-- solo per proprietà, non essendoci alternative).
-- =============================================================================

require('Module:No globals')

local getArgs = require('Module:Arguments').getArgs
local mConvert = require('Module:Conversione')
local mLanguages = require('Module:Linguaggi')

-- Categoria per le pagine con errori
local errorCategory = '[[Categoria:Voci con errori del modulo Wikidata]]'

-- Messaggi
local i18n = {
	errors = {
		['entityid-param-not-provided'] = "Parametro ''entityid'' non fornito",
		['property-param-not-provided'] = "Parametro ''property'' non fornito",
		['qualifier-param-not-provided'] = "Parametro ''qualifier'' non fornito",
		['value-param-not-provided'] = "Parametro ''valore'' da ricercare non fornito",
		['entity-not-found'] = 'Entità non trovata',
		['unknown-claim-type'] = 'Tipo asserzione sconosciuta',
		['unknown-snak-type'] = 'Tipo di snak sconosciuto',
		['unknown-datavalue-type'] = 'Tipo di dato sconosciuto',
		['unknown-entity-type'] = 'Tipo di entità sconosciuta'
	},
	somevalue = "''valore sconosciuto''",
	novalue = "''nessun valore''",
	datatypes = {
		['commonsMedia'] = 'file multimediale su Commons',
		['external-id'] = 'identificativo esterno',
		['geo-shape'] = 'forma geografica',
		['globe-coordinate'] = 'coordinate geografiche',
		['math'] = 'espressione matematica',
		['monolingualtext'] = 'testo monolingua',
		['quantity'] = 'quantità',
		['string'] = 'stringa',
		['tabular-data'] = 'tabular data',
		['time'] = 'data e ora',
		['url'] = 'URL',
		['wikibase-item'] = 'elemento',
		['wikibase-property'] = 'proprietà'
	}
}

local p = {}

-------------------------------------------------------------------------------
--                             Formatters
-------------------------------------------------------------------------------

local function errhandler(msg)
	local cat = mw.title.getCurrentTitle().namespace == 0 and errorCategory or ''
	return string.format('<span class="error">%s</span>%s', msg, cat)
end

local function formatList(values, ordered)
	local fmt = ordered and '<ol><li>%s</li></ol>' or '<ul><li>%s</li></ul>'
	return #values > 0 and string.format(fmt, mw.text.listToText(values, '</li><li>', '</li><li>')) or ''
end

local function formatExtLink(url)
	local protocols = { ftp = true, http = true, https = true }

	local success, uri = pcall(function() return mw.uri.new(url) end)
	if success and uri.protocol and protocols[uri.protocol] then
		local dest = tostring(uri)
		return string.format('<span style="word-break: break-all;">[%s %s]</span>', dest, dest:gsub(uri.protocol .. '://', ''))	
	else
		return url
	end
end

local function formatEntityId(entityId)
	local label = mw.wikibase.getLabel(entityId)
	local siteLink = mw.wikibase.getSitelink(entityId)
	local ret
	if entityId == mw.wikibase.getEntityIdForCurrentPage() then
		ret = siteLink
	elseif siteLink and label then
		ret = mw.getContentLanguage():ucfirst(label) == siteLink and
			  string.format('[[%s]]', label) or
			  string.format('[[%s|%s]]', siteLink, label)
	elseif siteLink then
		ret = string.format('[[%s]]', siteLink)
	elseif label then
		ret = label
	else
		ret = ''
	end
	return ret
end

local function formatMonolingualtext(value, args)
	local ret = ''
	if not args.includelang or args.includelang:match('%f[a-z]' .. value.language .. '%f[^a-z]') then
		if not args.excludelang or not args.excludelang:match('%f[a-z]' .. value.language .. '%f[^a-z]') then
			ret = value.text
			if args.showlang then
				ret = mLanguages.lingue({ value.language }) .. '&nbsp;' .. ret
			end
		end
	end
	return ret
end

local function formatTimeWithPrecision(time, precision)
	local months = {
		'gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno',
		'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'
	}
	local ret, year, month, day
 
	year, month, day = time:match('(%d+)%-(%d%d)%-(%d%d).+')
	year, month, day = tonumber(year), tonumber(month), tonumber(day)
	if precision == 9 then
		ret = year
	elseif precision == 10 then
		ret = months[month] .. ' ' .. year
	elseif precision == 11 then
		ret = day .. ' ' .. months[month] .. ' ' .. year
		ret = ret:gsub('^1%s', '1º ')
	end
	if precision >= 9 and precision <= 11 then
		ret = ret .. (time:sub(1, 1) == '-' and ' a.C.' or '')
	end

	return ret
end

local function formatTime(value, args)
	local ret
 
	if args.time == 'precision' then
		ret = value.precision
	elseif args.time == 'calendarmodel' then
		ret = value.calendarmodel
	elseif args.time == 'year' and value.precision >= 9 then
		ret = formatTimeWithPrecision(value.time, 9)
	elseif args.time == 'month' and value.precision >= 10 then
		ret = formatTimeWithPrecision(value.time, 10)
	elseif args.time == 'day' and value.precision >= 11 then
		ret = formatTimeWithPrecision(value.time, 11)
	elseif not args.time then
		ret = formatTimeWithPrecision(value.time, value.precision)
	end

	return ret or ''
end

local function formatGlobecoordinate(value, args)
	local ret
	if args.coord == 'latitude' then
		ret = value.latitude
	elseif args.coord == 'longitude' then
		ret = value.longitude
	elseif args.coord == 'globe' then
		ret = value.globe
	else
		ret = string.format('%s, %s', value.latitude, value.longitude)
	end
	return ret
end

local function formatFromPattern(str, args)
	local pattern = args.pattern
	pattern = mw.ustring.gsub(pattern, '\\{', '{')
	pattern = mw.ustring.gsub(pattern, '\\}', '}')
	return mw.getCurrentFrame():preprocess(mw.message.newRawMessage(pattern, str):plain())
end

local function formatUserValue(value, args)
	if args.extlink then
		value = formatExtLink(value)
	end
	return args.pattern and formatFromPattern(value, args) or value
end

local function getEntityIdFromValue(value)
	local prefix = ''
	if value['entity-type'] == 'item' then
		prefix = 'Q'
	elseif value['entity-type'] == 'property' then
		prefix = 'P'
	else
		error(i18n.errors['unknown-entity-type'])
	end
	return prefix .. value['numeric-id']
end

local function formatUnitSymbol(entityId, args)
	local ret
	for _, lang in ipairs({ 'mul', 'it', 'en' }) do
		ret = p._getProperty({ 'P5061', includelang = lang, from = entityId })
		if ret and ret ~= '' then
			break
		else
			ret = nil
		end
	end
	local space = ret == '°' and '' or ' '
	if ret and args.showunitlink then
		local link = mw.wikibase.getSitelink(entityId)
		if link then
			ret = string.format('[[%s|%s]]', link, ret)
		end
	end
	return ret and (space .. ret) or ''
end

-- http://lua-users.org/wiki/SimpleRound
local function round(num, idp)
	local mult = 10 ^ (idp or 0)
	return math.floor(num * mult + 0.5) / mult
end


local function formatQuantity(value, args)
	local ret = tonumber(value.amount)

	if (args.unit or args.showunit) and value.unit ~= '1' then
		local unitId = mw.ustring.match(value.unit, 'Q%d+')
		if args.unit then
			local opts = {
				showunit = args.showunit,
				showunitlink = args.showunitlink,
				formatnum = args.formatnum,
				rounding = args.rounding
			}
			ret = mConvert._main(ret, unitId, args.unit, opts)
		else
			-- se è richiesto solo il simbolo dell'unità
			-- senza la conversione lo ottiene da P5061
			ret = args.rounding and round(ret, args.rounding) or ret
			if args.formatnum then
				ret = mw.language.getContentLanguage():formatNum(ret)
			end
			ret = ret .. formatUnitSymbol(unitId, args)
		end
	elseif args.formatnum then
		ret = args.rounding and round(ret, args.rounding) or ret
		ret = mw.language.getContentLanguage():formatNum(ret)
	elseif args.formatduration and value.unit ~= '1' then
		local unitId = mw.ustring.match(value.unit, 'Q%d+')
		ret = mConvert._main(ret, unitId, 'second')
		ret = ret and mw.language.getContentLanguage()
				:formatDuration(tonumber(ret), { 'days', 'hours', 'minutes', 'seconds' })
	end

	return ret
end

local function formatDatavalue(datavalue, snakdatatype, args)
	local ret

	if datavalue.type == 'wikibase-entityid' then
		local entityId = getEntityIdFromValue(datavalue.value)
		if args.showprop then
			ret = p._getProperty({ args.showprop, n = 1, from = entityId }) or ''
		else
			ret = args.formatting == 'raw' and entityId or formatEntityId(entityId)
		end
	elseif datavalue.type == 'string' then
		ret = datavalue.value
		if args.extlink and snakdatatype == 'url' then
			ret = formatExtLink(ret)
		elseif args.urlencode then
			ret = mw.uri.encode(ret)
		end
	elseif datavalue.type == 'monolingualtext' then
		ret = formatMonolingualtext(datavalue.value, args)
	elseif datavalue.type == 'time' then
		if args.formatting == 'raw' then
			ret = datavalue.value.time
		else
			ret = formatTime(datavalue.value, args)
		end
	elseif datavalue.type == 'globecoordinate' then
		ret = formatGlobecoordinate(datavalue.value, args)
	elseif datavalue.type == 'quantity' then
		ret = formatQuantity(datavalue.value, args)
	else
		error(i18n.errors['unknown-datavalue-type'])
	end

	return ret
end

local function formatSnak(snak, args)
	if snak.snaktype == 'somevalue' then
		return i18n['somevalue']
	elseif snak.snaktype == 'novalue' then
		return i18n['novalue']
	elseif snak.snaktype == 'value' then
		return formatDatavalue(snak.datavalue, snak.datatype, args)
	else
		error(i18n.errors['unknown-snak-type'])
	end
end

-- È al plurale perché anche i qualifier possono avere più di un valore
-- (si ottiene inserendo due volte lo stesso qualifier)
local function formatQualifiers(claim, qualifierId, args, rawTable, retTable)
	local formattedQualifiers = retTable or {}

	if claim.qualifiers and claim.qualifiers[qualifierId] then
		local qualifiers = claim.qualifiers[qualifierId]
		-- con args.nq seleziona solo l'n-esimo qualifier
		if args.nq then
			local n = tonumber(args.nq)
			qualifiers = (n and n <= #qualifiers) and { qualifiers[n] } or {}
		end
		-- qualifier filtrati per snaktype, default "value"
		args.snaktype = args.snaktype or 'value'
		for _, qualifier in ipairs(qualifiers) do
			if qualifier.snaktype == args.snaktype or args.snaktype == 'all' then
				local formattedQualifier = formatSnak(qualifier, args)
				if formattedQualifier ~= '' then
					if args.pattern then
						formattedQualifier = formatFromPattern(formattedQualifier, args)
						if formattedQualifier ~= '' then
							table.insert(formattedQualifiers, formattedQualifier)
						end
					else
						table.insert(formattedQualifiers, formattedQualifier)
					end
				end
			end
		end
	end

	if rawTable then
		return formattedQualifiers
	end

	return #formattedQualifiers > 0 and
		   mw.text.listToText(formattedQualifiers, args.separator, args.conjunction) or nil
end

local function appendQualifiers(statement, text, args)
	local formattedQualifiers = {}
	local qualifierIds = mw.text.split(args.showqualifiers, ',')
	for _, qualifierId in ipairs(qualifierIds) do
		if statement.qualifiers[qualifierId] then
			local formattedQualifier = formatQualifiers(statement, qualifierId, args)
			table.insert(formattedQualifiers, formattedQualifier)
		end
	end
	if #formattedQualifiers > 0 then
		text = string.format('%s (%s)', text, mw.text.listToText(formattedQualifiers, ', ', ', '))
	end
	return text
end

local function formatStatement(statement, args)
	if not statement.type or statement.type ~= 'statement' then
		error(i18n.errors['unknown-claim-type'])
	end
 
	local ret = formatSnak(statement.mainsnak, args)
	-- eventuale showqualifiers
	if args.showqualifiers and statement.qualifiers then
		ret = appendQualifiers(statement, ret, args)
	end

	return ret
end

local function formatStatements(claims, args, rawTable)
	local formattedStatements = {}

	for _, claim in ipairs(claims) do
		local formattedStatement = formatStatement(claim, args)
		if formattedStatement ~= '' then
			-- eventuale pattern
			if args.pattern then
				formattedStatement = formatFromPattern(formattedStatement, args)
				if formattedStatement ~= '' then
					table.insert(formattedStatements, formattedStatement)
				end
			else
				table.insert(formattedStatements, formattedStatement)
			end
		end
	end
	if rawTable then
		return formattedStatements
	end

	return ((args.list or args.orderedlist) and #formattedStatements > 1) and
		   formatList(formattedStatements, args.orderedlist ~= nil) or 
		   mw.text.listToText(formattedStatements, args.separator, args.conjunction)
end

-------------------------------------------------------------------------------
--                      Lettura e selezione statement
-------------------------------------------------------------------------------

-- Restituisce true se lo statement contiene il qualifier richiesto con un dato valore (o uno tra più valori separati da virgola)
local function hasQualifierValue(statement, qualifierId, qualifierValue)
	local ret = false
	for _, qualifier in ipairs(statement.qualifiers[qualifierId]) do
		local isItem = qualifier.snaktype == 'value' and
					   qualifier.datavalue.type == 'wikibase-entityid'
		local qualifierValues = mw.text.split(qualifierValue, ',')
		for _, qualifierHas in ipairs(qualifierValues) do
			-- per le proprietà di tipo item il confronto è eseguito sull'id
			if formatSnak(qualifier, isItem and { formatting = 'raw' } or {}) == qualifierHas then
				ret = true
				break
			end
		end
	end
	return ret
end

-- Restituisce i claim con il rank richiesto
local function filterRankValue(claims, rank)
	local ret = {}
	for _, claim in ipairs(claims) do
		if claim.rank == rank then
			table.insert(ret, claim)
		end
	end
	return ret
end

-- Restituisce una sequence Lua contenente gli statement per la property richiesta,
-- anche vuota se la proprietà non esiste, o non ci sono valori che soddisfano i criteri
-- ("rank", "qualifier", "qualifiertype", "noqualifier", ...).
-- Restituisce nil solo se la pagina non è collegata a un elemento Wikidata e non è indicato il from.
local function getClaims(propertyId, args)
	local entityId, claims, filteredClaims
	
	entityId = args.from or mw.wikibase.getEntityIdForCurrentPage()
	if not entityId then
		return nil
	end
	
	-- il default rank è 'best'
	args.rank = args.rank or 'best'
	if args.rank == 'best' then
		claims = mw.wikibase.getBestStatements(entityId, propertyId)
	else
		-- statements filtrati per rank
		claims = mw.wikibase.getAllStatements(entityId, propertyId)
		claims = filterRankValue(claims, args.rank)
	end

	-- statements filtrati per snaktype, default "value"
	args.snaktype = args.snaktype or 'value'
	if args.snaktype and args.snaktype ~= 'all' then
		filteredClaims = {}
		for _, claim in ipairs(claims) do
			if claim.mainsnak.snaktype == args.snaktype then
				table.insert(filteredClaims, claim)
			end
		end
		claims = filteredClaims
	end

	-- statements filtrati per qualifier
	if args.qualifier then
		filteredClaims = {}
		for _, claim in ipairs(claims) do
			if claim.qualifiers and claim.qualifiers[args.qualifier] then
				if args.qualifiervalue then
					if hasQualifierValue(claim, args.qualifier, args.qualifiervalue) then
						table.insert(filteredClaims, claim)
					end
				else
					table.insert(filteredClaims, claim)
				end
			end
		end
		claims = filteredClaims
	end

	-- statements filtrati per essere senza un qualifier
	if args.noqualifier then
		filteredClaims = {}
		for _, claim in ipairs(claims) do
			if not (claim.qualifiers and claim.qualifiers[args.noqualifier]) then
				table.insert(filteredClaims, claim)
			end
		end
		claims = filteredClaims
	end

	-- statements filtrati per non avere un certo valore a un certo qualifier opzionale
	if args.qualifieroptnovalue and args.qualifiervalue then
		filteredClaims = {}
		for _, claim in ipairs(claims) do
			if claim.qualifiers and claim.qualifiers[args.qualifieroptnovalue] then
				if not hasQualifierValue(claim, args.qualifieroptnovalue, args.qualifiervalue) then
					table.insert(filteredClaims, claim)
				end
			else
				table.insert(filteredClaims, claim)
			end
		end
		claims = filteredClaims
	end

	-- con args.qualifiertype=latest restituisce solo il più recente
	if args.qualifier and args.qualifiertype == 'latest' then
		local latest, latestTime
		for _, claim in ipairs(claims) do
			if claim.qualifiers and claim.qualifiers[args.qualifier] then
				for _, qualifier in ipairs(claim.qualifiers[args.qualifier]) do
					if qualifier.datavalue.type == 'time' then
						if not latestTime or qualifier.datavalue.value.time > latestTime then
							latest = claim
							latestTime = qualifier.datavalue.value.time
						end
					end
				end
			end
		end
		claims = latest and { latest } or {}
	end

	-- con args.n restituisce solo l'n-esimo elemento
	if args.n then
		local n = tonumber(args.n)
		claims = (n and n <= #claims) and { claims[n] } or {}
	end

	return claims
end

-------------------------------------------------------------------------------
--                  Funzioni esportate per altri moduli
-------------------------------------------------------------------------------

function p._getClaims(propertyId, args)
	return getClaims(propertyId, args or {})
end

function p._formatStatement(statement, args)
	return formatStatement(statement, args or {})
end

function p._formatQualifiers(claim, qualifierId, args, rawTable, retTable)
	return formatQualifiers(claim, qualifierId, args or {}, rawTable, retTable)
end

-- Restituisce il valore di una proprietà di Wikidata oppure nil se l'entity o
-- la proprietà non esistono, o se per parametri di selezione gli statement sono zero.
function p._getProperty(args, rawTable)
	local propertyId, value, claims, ret

	-- parametri posizionali
	propertyId = args[1] and string.upper(args[1])
	if not propertyId then
		error(i18n.errors['property-param-not-provided'], 2)
	end
	value = args[2]
	-- fix uppercase
	args.qualifier = args.qualifier and string.upper(args.qualifier)

	if value then
		ret = formatUserValue(value, args)
	elseif args.wd ~= 'no' then
		claims = getClaims(propertyId, args)
		ret = (claims and #claims > 0) and formatStatements(claims, args, rawTable) or nil
	end

	return ret
end

-- Restituisce il valore di un qualifier di una proprietà di Wikidata,
-- o nil se l'entity o la proprietà non esistono, o se per parametri di selezione non ci sono risultati.
function p._getQualifier(args)
	local propertyId, qualifierId, value, claims, ret

	-- parametri posizionali
	propertyId = args[1] and string.upper(args[1])
	if not propertyId then
		error(i18n.errors['property-param-not-provided'], 2)
	end
	qualifierId = args[2] and string.upper(args[2])
	if not qualifierId then
		error(i18n.errors['qualifier-param-not-provided'], 2)
	end
	value = args[3]

	if value then
		ret = formatUserValue(value, args)
	elseif args.wd ~= 'no' then
		claims = getClaims(propertyId, args)
		if claims and #claims > 0 then
			local formattedQualifiers = {}
			for _, claim in ipairs(claims) do
				formattedQualifiers = formatQualifiers(claim, qualifierId, args, true, formattedQualifiers)
			end
			ret = #formattedQualifiers > 0 and
				  mw.text.listToText(formattedQualifiers, args.separator, args.conjunction) or nil
		end
	end

	return ret
end

-- Restituisce l'indice dello statement con il valore richiesto, o nil se non trovato.
function p._indexOf(args)
	local ret, propertyId, value, claims

	-- parametri posizionali
	propertyId = args[1] and string.upper(args[1])
	if not propertyId then
		error(i18n.errors['property-param-not-provided'], 2)
	end
	value = args[2]
	if not value then
		error(i18n.errors['value-param-not-provided'], 2)
	end

	claims = getClaims(propertyId, args)
	if claims and #claims > 0 then
		args.formatting = 'raw'
		for i, claim in ipairs(claims) do
			if formatStatement(claim, args) == value then
				ret = i
				break
			end
		end
	end

	return ret
end

-- Restituisce il numero di statement di una proprietà di Wikidata.
function p._N(args)
	local propertyId, claims

	-- parametri posizionali
	propertyId = args[1] and string.upper(args[1])
	if not propertyId then
		error(i18n.errors['property-param-not-provided'], 2)
	end
	-- get claims
	claims = getClaims(propertyId, args)

	return claims and #claims or 0
end

-- Restituisce true se la propriertà specificata ha come valore
-- almeno uno tra gli entityId passati come argomento.
function p._propertyHasEntity(propertyId, args)
	local statements = p._getProperty({ propertyId, from = args.from, formatting = 'raw' }, true)
	if statements then
		for _, statement in ipairs(statements) do
			for _, entityId in ipairs(args) do
				if statement == entityId then
					return true
				end
			end
		end

		-- Se non è stato trovato alcun valore, controlla se questo sia ereditato
		-- tramite la proprietà "sottoclasse di" (P279) scavando in profondità
		-- fino all'esaurirsi del numero specificato in args.recursion.
		--[[ TODO: Valutare se sia opportuna una ricerca ricorsiva potenzialmente infinita.
			Per farlo si può aggiungere un parametro (opzionale) maxDepth
			che svolga l'attuale funzione di recursion e cambiare quest'ultimo
			in un parametro booleano.
		]]
		args.recursion = tonumber(args.recursion) or 0
		if args.recursion > 0 then
			local recursion = args.recursion
			if type(args.loadedEntities) ~= 'table' then
				args.loadedEntities = setmetatable({}, {
					__newindex = function(t, k, v)
						rawset(t, k, v)
						rawset(t, #t+1, k)
					end })
				args.loadedEntities[args.from or mw.wikibase.getEntityIdForCurrentPage()] = true
			end
			for _, statement in ipairs(statements) do
				if not args.loadedEntities[statement] then
					args.loadedEntities[statement] = true
					args.recursion = args.recursion - 1
					args.from = statement
					if p._propertyHasEntity('P279', args) then
						return true, args.loadedEntities
					end
					args.recursion = recursion
				end
			end
		end
	end

	return false, args.loadedEntities
end

-- Restituisce true se la proprietà P31 (instance of) ha come valore almeno uno tra gli entityId specificati
function p._instanceOf(args)
	return p._propertyHasEntity('P31', args)
end

-- Restituisce true se la proprietà P279 (subclass of) ha come valore almeno uno tra gli entityId specificati
function p._subclassOf(args)
	return p._propertyHasEntity('P279', args)
end

-- Restituisce l'etichetta di un item o di una proprietà Wikidata.
function p._getLabel(args)
	local entityId = args[1] and string.upper(args[1])
	local ret
	if args[2] then
		ret = mw.wikibase.getLabelByLang(entityId, args[2])
	else
		ret = mw.wikibase.getLabel(entityId)
	end
	return ret
end

-- Restituisce la descrizione di un item o di una proprietà Wikidata.
function p._getDescription(args)
	local entityId = args[1] and string.upper(args[1])
	local ret = mw.wikibase.getDescription(entityId)
	return ret
end

-- Restituisce il titolo della pagina collegata a un dato item Wikidata.
function p._getLink(args) 
	-- parametri posizionali
	local entityId = args[1] and string.upper(args[1])
	if not entityId then
		error(i18n.errors['entityid-param-not-provided'], 2)
	end
	
	return entityId:sub(1, 1) == 'Q' and formatEntityId(entityId) or nil
end

-- Restituisce il datatype di una proprietà Wikidata.
function p._getDatatype(args) 
	local propertyId, entity

	-- parametri posizionali
	propertyId = args[1] and string.upper(args[1])
	if not propertyId then
		error(i18n.errors['property-param-not-provided'], 2)
	end

	entity = mw.wikibase.getEntity(propertyId)
	if not entity then
		error(i18n.errors['entity-not-found'], 2)
	end

	if not i18n.datatypes[entity.datatype] then
		error(i18n.errors['unknown-datavalue-type'], 2)
	end

	return i18n.datatypes[entity.datatype]
end

-- Restituisce l'ID dell'item Wikidata collegato alla pagina corrente o a una pagina specificata
-- (nota: segue i redirect fermandosi al primo redirect collegato a un elemento)
function p._getId(args)
	local ret
	if args[1] then
		local title = mw.title.new(args[1])
		while title do
			local id = mw.wikibase.getEntityIdForTitle(title.prefixedText)
			if id then
				ret = id
				break
			else
				title = title.redirectTarget
			end
		end
	else
		ret = mw.wikibase.getEntityIdForCurrentPage()
	end
	return ret
end

-------------------------------------------------------------------------------
--                  Funzioni esportate per i template
-------------------------------------------------------------------------------

-- Funzione per il template {{Wikidata}}
function p.getProperty(frame)
	return select(2, xpcall(function()
		return p._getProperty(getArgs(frame, { parentOnly = true }))
	end, errhandler))
end

-- Funzione per il template {{WikidataQ}}
function p.getQualifier(frame)
	return select(2, xpcall(function()
		return p._getQualifier(getArgs(frame, { parentOnly = true }))
	end, errhandler))
end

-- Funzione per il template {{WikidataIdx}}
function p.indexOf(frame)
	return select(2, xpcall(function()
		return p._indexOf(getArgs(frame, { parentOnly = true }))
	end, errhandler))
end

-- Funzione per il template {{WikidataN}}
function p.N(frame)
	return select(2, xpcall(function()
		return p._N(getArgs(frame, { parentOnly = true }))
	end, errhandler))
end

-- Funzione per il template {{WikidataLabel}}
function p.getLabel(frame)
	return select(2, xpcall(function()
		return p._getLabel(getArgs(frame, { parentOnly = true }))
	end, errhandler))
end

-- Funzione per il template {{WikidataDescription}}
function p.getDescription(frame)
	return select(2, xpcall(function()
		return p._getDescription(getArgs(frame, { parentOnly = true }))
	end, errhandler))
end

-- Funzione per il template {{WikidataLink}}
function p.getLink(frame)
	return select(2, xpcall(function()
		return p._getLink(getArgs(frame, { parentOnly = true }))
	end, errhandler))
end

-- Funzione per il template {{WikidataIstanza}}
function p.instanceOf(frame)
	return select(2, xpcall(function()
		return p._instanceOf(getArgs(frame, { parentOnly = true })) and 1 or ''
	end, errhandler))
end

-- Funzione per il template {{WikidataTipo}}
function p.getDatatype(frame)
	return select(2, xpcall(function()
		return p._getDatatype(getArgs(frame, { parentOnly = true }))
	end, errhandler))
end

-- Funzione per il template {{WikidataId}}
function p.getId(frame)
	return select(2, xpcall(function()
		return p._getId(getArgs(frame, { parentOnly = true }))
	end, errhandler))
end

-- Funzione per il template {{WikidataValido}}
function p.checkProperty(frame)
	return select(2, xpcall(function()
		return p._N(getArgs(frame, { parentOnly = true })) > 0 and 1 or ''
	end, errhandler))
end

return p