Modul:Brug Wikidata

Fra Wikipedia, den frie encyklopædi
Gå til: navigation, søg
[vis] [rediger]Template-info.svg Skabeloninformation
Modul:Brug Wikidata/doc
-- Module to fetch information from Wikidata, primarily for use in infoboxes

-- Overførte parametre til funktioner hent_emne, hent_tid, hent_tal, hent_streng, hent_tekst:
-- Fælles:
-- 1: Wikidata-egenskab (Pxxx)
-- 2: Lokal angivet værdi. Wikidata bruges kun hvis tom eller ikke angivet
-- feltnavn. Bruges til at undersøge om Wikidata skal bruges for dette felt.
-- wikidata: Liste med feltnavne. Brug Wikidata hvis ''feltnavn'' er på listen eller hvis værdien er "ja" eller "alle",
--           og ''navn'' ikke på listen ''ingen_wikidata''.
-- ingen_wikidata: Liste med feltnavne. Brug ikke Wikidata hvis ''feltnavn'' er på listen.
-- adskil: Adskillelse mellem flere værdier
-- liste: Opstil flere værdier i punktliste
-- ingen: Tekst for novalue
-- ukendt: Tekst for somevalue
-- maks: Maksimalt antal resultater
-- q: Wikidata-emne (Qxxx). Hvis defineret brug dette Wikidata-emne i stedet for det som er tilknyttet den viste side (mest til testning)
-- ikon: Lav et ikon med link til Wikidata hvis værdien er 'ja'
-- tid: Hvis værdien er 'ja': Angiv tidspunkt eller start- og sluttid hentet fra kvalifikatorer. (ikke for hent_tid)
-- kvalifikator1, kvalifikator2, kvalifikator3 osv.: kvalifikatorer
-- kvalifikatorformat1, kvalifikatorformat2, kvalifikatorformat3 osv.: format for visning af værdier med kvalifikatorer
-- kvalifikatorformatuden1, kvalifikatorformatuden2, kvalifikatorformatuden3 osv.: format for visning af værdier uden kvalifikatorer
-- kvalifikatorbrug1, kvalifikatorbrug2, kvalifikatorbrug3 osv.: 'med': kun værdier uden kvalifikator, 'med': kun værdier uden kvalifikator,
-- ental: tekst efter resultatet hvis der er et resultat
-- flertal: tekst efter resultaterne hvis der er flere

-- For hent_emne:
-- sprogkat: Brug kategori ved sprogfallback
-- sprognote: Brug note ved sprogfallback
-- sprognotegroup: group-attribute ved sprognote
-- msk: Vis MSK-tidzone for UTC-tidszoner i Rusland
-- parti: Skriv politisk parti for emnet i det angivne format
-- medstort: 'ja': skriv første værdi med stort begyndelsesbogstav,
--           'alle': skriv alle værdier med stort begyndelsesbogstav
-- kursiv: Skriv værdierne i kursiv hvis værdien er 'ja'
-- land: Skriv land for emnet i det angivne format
-- kun: Medtag kun resultater med det anførte emne som værdi

-- For hent_tal:
-- decimaler: Antal decimaler i tal
-- enhed: Omregn tallet til den anførte enhed (skal være i tabellen wanted_units)
-- visusikkerhed: Vis usikkerheden (default: ja)
-- visenhed: Vis enheden (default: ja)
-- arealogtæthed: Vis værdi, areal og tæthed i det angivne format ($1 = tallet, $2 = arealet, $3 = tætheden)
-- arealogtætheduden: Vis værdi i det angivne format hvis ikke er areal ($1 = tallet)

-- For hent_tekst:
-- sprog: Liste med sprogkoder for ønskede sprog. 'alle' for alle sprog. (default: da)
-- skrivsprog: Skriv navnet på sprog i parentes hvis 'ja'

-- For hent_streng:
-- format: Format for streng. $1 i formatet vil blive udskiftet med strengen.

-- For hent_tid:
-- kunår: Skriv kun årstallet hvis værdien er 'ja' (gælder også andre datatyper med tidskvalifikatorer)
-- alder: Beregn alder i forhold til en tidsværdi
-- alderformat: Format for angivelse af alder

local preferred_language = 'da'
local fallback_languages = { 'nb', 'nn', 'sv', 'en', 'de', 'nl', 'fr', 'sp', 'it', 'pt' }
local fallback_languages_humans = { 'nb', 'nn', 'sv' }
local fallback_note = '<span style="color:gray; cursor:help;"><small>Navnet er anført på %s og stammer fra [[d:%s|Wikidata]] hvor navnet endnu ikke findes på dansk.</small></span>'
local bc = ' f.v.t.'
local months = {
	['1'] = 'januar ',		['2'] = 'februar ',		['3'] = 'marts ',
	['4'] = 'april ',		['5'] = 'maj ',			['6'] = 'juni ',
	['7'] = 'juli ',		['8'] = 'august ',		['9'] = 'september ',
	['10'] = 'oktober ',	['11'] = 'november ',	['12'] = 'december ' }

-- Units used for quantity values. For each give name to display, conversion factor, 
local wd_units = {
	-- area units
	Q712226 = { name = 'km2', show_as = 'km<sup>2</sup>', conv = 1e6, type = 'area' },
	Q25343 = { name = 'm2', show_as = 'm<sup>2</sup>', conv = 1, type = 'area' },
	Q232291 = { name = 'mi2', show_as = 'mi<sup>2</sup>', conv_to = 'km2', conv = 2589988.110336, type = 'area'},
	-- currency units
    Q25417 = { name = 'DKK', show_as = "dkk", conv = 1, type = 'currency' },
    Q4916 = { name = 'EUR', show_as = "€", conv = 1, type = 'currency' },
    Q25224 = { name = 'GBP', show_as = "£", conv = 1, type = 'currency' },
    Q132643 = { name = 'NOK', show_as = "nok", conv = 1, type = 'currency' },
    Q122922 = { name = 'SEK', show_as = "sek", conv = 1, type = 'currency' },
    Q4917 = { name = 'USD', show_as = "$", conv = 1, type = 'currency' },
	-- length units
	Q11573 = { name = 'm', show_as = 'm', conv = 1, type = 'length' },
	Q828224 = { name = 'km', show_as = 'km', conv = 1e3, type = 'length' },
	Q253276 = { name = 'mile', show_as = 'mi', conv_to = 'km', conv = 1609.344, type = 'length' },
	Q174728 = { name = 'cm', show_as = 'cm', conv = 0.01, type = 'length' },
	Q218593 = { name = 'in', show_as = 'm', conv = 0.0254, type = 'length' },
	-- mass units
	Q11570 = { name = 'kg', show_as = 'kg', conv = 1, type = 'mass' },
	Q100995 = { name = 'lb', show_as = "lb", conv = 0.45359237, type = 'mass' },
	-- time units
	Q11574 = { name = 's', show_as = 's', conv = 1, type = 'time' },
	Q7727 = { name = 'minut', show_as = 'min.', conv = 60, type ='time' },
	Q25235 = { name = 'time', show_as = 't', conv = 3600, type = 'time' },
	-- speed units
	Q182429 = { name = 'm/s', show_as = 'm/s', conv = 1, type = 'speed' },
	Q180154 = { name = 'km/t', show_as = 'km/t', conv = 0.2777777777777777778, type = 'speed' },
	Q128822 = { name = 'knob', show_as = 'kn', conv = 0.51444444444444444444, type = 'speed' },
	Q748716 = { name = 'ft/s', show_as = 'ft/s', conv = 0,3048, type = 'speed' }
}

-- Units as requested in parameter 'enhed'
local wanted_units = {
	m2 = { show_as = 'm<sup>2</sup>', conv = 1, type = 'area' },
	km2 = { show_as = 'km<sup>2</sup>', conv = 1e-6, type = 'area' },
	m = { show_as = 'm', conv = 1, type = 'length' },
	km = { show_as = 'km', conv = 1e-3, type = 'length' },
	cm = { show_as = 'cm', conv = 100, type = 'length' },
	kg = { show_as = 'kg', conv = 1, type = 'mass' },
	['km/t'] = { show_as = 'km/t', conv = 3.6, type = 'speed' },
	['m/s'] = { show_as = 'm/s', conv = 1, type = 'speed' }
}

local msk_timezones = {
	Q6723 = ' ([[Moskva tid|MSK]]-1)', -- UTC+2
	Q6760 = ' ([[Moskva tid|MSK]])', -- UTC+3
	Q6779 = ' ([[Moskva tid|MSK]]+1)', -- UTC+4
	Q6806 = ' ([[Moskva tid|MSK]]+2)', -- UTC+5
	Q6906 = ' ([[Moskva tid|MSK]]+3)', -- UTC+6
	Q6940 = ' ([[Moskva tid|MSK]]+4)', -- UTC+7
	Q6985 = ' ([[Moskva tid|MSK]]+5)', -- UTC+8
	Q7041 = ' ([[Moskva tid|MSK]]+6)', -- UTC+9
	Q7056 = ' ([[Moskva tid|MSK]]+7)', -- UTC+10
	Q7069 = ' ([[Moskva tid|MSK]]+8)', -- UTC+11
	Q7105 = ' ([[Moskva tid|MSK]]+9)', -- UTC+12
}

local fallback_langues_after_country = {
	Q20 = 'nb', --Norway
	Q30 = 'en', -- USA
	Q34 = 'sv', -- Sweden
	Q142 = 'fr', -- France
	Q145 = 'en', -- UK
	Q183 = 'de' -- Germany
}

local fallback_langues_for_persons = {
	Q29 = 'sp', -- Spain
	Q30 = 'en', -- USA
	Q33 = 'fi', -- Finland
	Q38 = 'it', -- Italy
	Q40 = 'de', -- Austria
	Q45 = 'pt', -- Portugal
	Q55 = 'nl', -- Netherlands
	Q96 = 'sp', -- Mexico
	Q142 = 'fr', -- France
	Q145 = 'en', -- UK
	Q155 = 'pt', -- Brazil
	Q183 = 'de', -- Germany
	Q189 = 'is', -- Iceland
	Q298 = 'sp', -- Chile
	Q408 = 'en', -- Australia
	Q414 = 'sp', -- Argentina
	Q664 = 'en' -- New Zealand
}

local p = {}

local fallback_category ='[[Kategori:Oplysninger fra Wikidata på et andet sprog end dansk]]'
local many_p463_category ='[[Kategori:Mange oplysninger fra Wikidata for P463 (medlem af)]]'
local many_p106_category='[[Kategori:Mange oplysninger fra Wikidata for P106 (beskæftigelse)]]'
local tiplus_p106_category='[[Kategori:Mere end 10 oplysninger fra Wikidata for P106 (beskæftigelse)]]'
local many_p1344_category ='[[Kategori:Mange oplysninger fra Wikidata for P1344 (deltog i)]]'
local many_p802_category ='[[Kategori:Mange oplysninger fra Wikidata for P802 (elev)]]'
local many_p800_category ='[[Kategori:Mange oplysninger fra Wikidata for P800 (hovedværk)]]'
local many_p737_category ='[[Kategori:Mange oplysninger fra Wikidata for P737 (påvirket af)]]'
local many_p166_category ='[[Kategori:Mange oplysninger fra Wikidata for P166 (udmærkelser)]]'
local category_unrecognized_unit = '[[Kategori:Enhed for størrelse på Wikidata ikke genkendt]]'
local category_missing_russian_name = '[[Kategori:Navn mangler på Wikidata for russer eller sted i Rusland]]'
local category_repeated_ref = '[[Kategori:Wikidata-reference bruger samme egenskab mere end en gang]]'
local category_unknown_ref = '[[Kategori:Wikidata-reference bruger ikke-genkendt egenskab]]'
local tracking_categories = ''
local add_tracking_category = function(cat)
	tracking_categories = tracking_categories .. cat
end

-- The following values are set by get_statements
local the_entity		-- Used when finding additionel information from statements for other properties
local the_qid			-- Used to make links to Wikidata
local the_pid			-- Used to make links to Wikidata
-- The following value is set by get_references
local ref_texts = {}

local function has_value (tab, val)
    for index, value in ipairs(tab) do
        if value == val then
            return index
        end
    end
    return false
end

-- Get and format a reference to a statement
local get_reference = function(ref, kunurl)
	local refargs = {}
	-- go throug all reference properties
	local snaks = ref.snaks
	for refpid, ref in pairs(snaks) do
		--mw.logObject(refpid, 'refpid')
		--mw.logObject(ref, 'ref')
		-- There may be more than more than value with the same property, but ignore all but the first
		ref1 = ref[1]
		if ref[2] then
			add_tracking_category(category_repeated_ref)
		end
		if ref1.snaktype == 'value' then
			-- We have a reel value, as in not 'somevalue' or 'novalue'  
			if refpid == 'P248' then
				-- P248 is stated in
				-- Get the item the rerefence is stated in
				local refentity = mw.wikibase.getEntity(ref1.datavalue.value.id)
				-- Check these: for book editions
				-- P1476 is title (monolingual text, the language can be default for language)
				-- P1680 is subtitle (monolingual text, include this?)
				-- P50 is author (item)
				-- P2093 is author name strin (string), check also for qualifier P1545 (series ordinal, warning: it is type string!)
				-- P393 is edition number (string)
				-- P98 is editor (item, check no)
				-- P123 is publisher (item)
				-- P291 is place of publication (item)
				-- P577 is publication date (time)
				-- P407 is language of work or name (item)
				-- P1683 is quote (monolinual text)
				-- for journals
				-- P433 is issue (string)
				-- P1433 is published in (item, go to it for journal=title, other info from this?)
				-- P478 is volume
				-- P698 is PubMed ID (pmid, external identifier)
				-- P932 is PMCID (pmc, external identifier)
				-- P356 is DOI (doi, external identifier)
				-- P1065 is archive URL (URL)
				-- P2960 is archive date (time)
				-- P179 is series (item) (Should this be used??)
				-- P31 is instance of (could be checked to know which properties to look for?)
				local reftypestatements = refentity:getBestStatements('P31')
				local instancesof = {}
				if reftypestatements then
					mw.logObject(reftypestatements, 'reftypestatements')
					for _, io in pairs(reftypestatements) do
						mw.logObject(io, 'io')
						if io.mainsnak.snaktype == 'value' then
							instancesof[io.mainsnak.datavalue.value.id] = true
						end
					end
				end
			elseif refpid == 'P854' then
				-- P854 is reference URL
				refargs.url = ref1.datavalue.value
			elseif refpid == 'P813' then
				-- P813 is retrieved
				refargs.accessdate = p.format_time({}, ref1.datavalue.value)
			elseif refpid == 'P304' then
				-- P304 is page(s)
				refargs.page = ref1.datavalue.value
			elseif refpid == 'P792' then
				-- P792 is chapter
				refargs.chapter = ref1.datavalue.value
			elseif refpid == 'P143' then
				-- P143 is "imported from"
				refargs.importedfrom = ref1.datavalue.value
				local refentity = mw.wikibase.getEntity(ref1.datavalue.value.id)
				local label, lang = refentity:getLabelWithLang(preferred_language)
				if label then refargs.importedfrom["label"] = label end
				--mw.logObject(ref1,'ref1')
			else
				add_tracking_category(category_unknown_ref)
			end
		end
	end
	local text = ''
	-- mw.logObject(refargs, 'refargs')
	if refargs.url then 
		local reftext = refargs.url
		-- skab en linktekst ud fra url'en
		-- find startposition
		local j1 = string.find(reftext,'//',1,true)
		-- fjern første del af strengen til og med de to skråstreger, hvis de findes
		if j1 then reftext = string.sub(reftext,j1+2,string.len(reftext)) else reftext = '' end
		-- mw.logObject(reftext,'reftext')
		-- hvis strengen ikke er tom
		if reftext ~= '' then
			-- find positionen af næste skråstreg i strengen
			local i1 = string.find(reftext,'/',1,true)
			-- brug kun den del af strengen der ligger før skråstregen, hvis den findes
		    if i1 then reftext = string.sub(reftext,1,i1-1) end
		    text = 'Oplysningen er fra [[d:' .. the_qid .. '#' .. the_pid .. '|Wikidata]] som angiver kilden: ['..refargs.url..' '..reftext..']. ' 
		end
	end
	if refargs.accessdate and text ~= '' then text = text..'Hentet '..refargs.accessdate..'. ' end
	if refargs.importedfrom and kunurl ~= 'ja' then text = text..'Importeret fra '..refargs.importedfrom.label..'. ' end
	mw.logObject(text,'text')
	return text
end

-- Get and format all references to a statement
-- Append the references to text and return the new text
-- If text is nil, return nil again
local get_references = function(args, text, references)
	-- This function is work in progess. 
	-- parameter ref er sat til 'ja' hvis der ønskes referencer
	local kilderef = mw.text.trim(args['ref'] or '')
	if kilderef ~= 'ja' then return text end
	if not text then return nil end
	-- kunurl er sat til 'ja' hvis der kun ønskes reference-url'er (P854)
	local kunurl = mw.text.trim(args['kunurl'] or '')
	-- viskm er sat til 'ja' hvis der skal være en note om at kilder mangler
	local viskm = mw.text.trim(args['viskm'] or '')
	local reference = ''
	-- refs er en tabel med de fundne referencer
	local refs = {}
	if not references or not next(references) then
		--refs[1] = '\nOplysningen er fra [[d:' .. the_qid .. '#' .. the_pid .. '|Wikidata]] som ikke har kilder til den.'
	else 
		for _ ,ref in pairs(references) do
			reference = get_reference(ref, kunurl)
			if reference ~= '' then table.insert(refs, reference) end
		end
	end
	-- indholdet af noten
	indhold = ''
	-- antallet af fundne referencer
	if #refs == 1 then
		indhold = '\n'..refs[1]
	elseif #refs > 1 then
		indhold = '\nOplysningen er fra [[d:' .. the_qid .. '#' .. the_pid .. '|Wikidata]] som har disse kilder: \n*'..table.concat(refs, '\n*')
	elseif viskm == 'ja' then indhold = '\nOplysningen er fra [[d:' .. the_qid .. '#' .. the_pid .. '|Wikidata]] som ikke har kilder til den.'
	end
	--mw.logObject(table.concat(refs, '\n*'),'slutprodukt')
	if indhold ~= '' then 
		local nr -- index for indhold i ref_texts
		local itabel = has_value(ref_texts, indhold)
		if not itabel then 
			table.insert(ref_texts, indhold) 
			nr = #ref_texts 
		else nr = itabel end
        local ref_args = { name = 'kilde ' .. the_pid .. the_qid .. nr }
		text = text .. p.frame:extensionTag{ name = 'ref', content = indhold, args = ref_args }
	    --mw.logObject(indhold..nr,'indhold')
	end
	--elseif viskm == 'ja' then text = text .. '<sup><small>[kilde mangler]</small></sup>' end
	return text 
end

-- Looks at the arguments 'sprog' og 'skrivsprog' in args
-- Returns 3 values: 1) get_all: true if all languages are wanted
-- 2) show_language: true if language name is wanted
-- 3) a table with keys for wanted languages.
local get_lang_args = function(args)
	local languages = mw.text.trim(args.sprog or preferred_language)
	local show_language = mw.text.trim(args.skrivsprog or '') == 'ja'
	local get_all = false
	local wanteds = {}
	if languages == 'alle' then
		get_all = true
	else
		for key in mw.text.gsplit(languages, '%s*,%s*') do
			wanteds[key] = true 
		end
	end
	return get_all, show_language, wanteds
end

-- Get values from a qualifier with data type time
-- Insert the values in the table given as first argument
-- The table elements are tables with the uformatted and the formatted value
-- Return the table
local get_time_qualifier = function(args, times, qualifiers)
	if qualifiers then
		for key, qualifier in pairs(qualifiers) do
			if qualifier.snaktype == "value" then
				local value = qualifier.datavalue.value
				local text = p.format_time(args, value)
				if text then
					table.insert(times, { value.time, text })
				end
			end
		end
	end
	return times
end

-- combine the formated dates in the second element of the element in the dates table
local combine_dates = function(dates)
	local text = ''
	if dates and dates[1] then
		text = dates[1][2]
	end
	for i = 2, #dates do
		text = text .. '/' .. dates[i][2]
	end
	return text
end

-- Get time values from the qualifiers and add them to the table times
-- The elements of times are tables with unformated and formated time values
-- Returns the times table 
local get_qualifier_times = function(args, times, qualifiers)
	if qualifiers then
		get_time_qualifier(args, times, qualifiers.P585) -- P585 is point of time
		local starts = get_time_qualifier(args, {}, qualifiers.P580) -- P580 is start time
		local ends = get_time_qualifier(args, {}, qualifiers.P582) -- P582 is end time
		if #starts > 0 then
			-- There can more than one start time, e.g. if the sources don't agree
			if #ends > 0 then
				-- Period with start and end time
				table.insert(times, { starts[1][1], combine_dates(starts) .. '-' .. combine_dates(ends) } )
			else
				-- Only start time
				table.insert(times, { starts[1][1], 'fra ' .. combine_dates(starts) } )
			end
		else
			if #ends > 0 then
				-- Only end time
				table.insert(times, { ends[1][1], 'til ' .. combine_dates(ends) } )
			end
		end
	end
	return times
end

-- Sort and combine the qualifier time values in the table times.
-- The elements of times are tables with unformated and formated time values.
-- Returns text ready to append to the value.
local format_qualifier_times = function(times)

	local text = ''
	if #times > 0 then
		table.sort(times, function (a,b)
			-- Use the unformated ISO 8601 time string for sorting
			local signa, signb = a[1]:sub(1, 1), b[1]:sub(1, 1)
			if signa == '+' then
				if signb == '+' then
					return a[1] < b[1] -- 2 AD times: The higher number is greater
				else
					return false -- AD time is greater than BC time
				end
			else
				if signb == '+' then
					return true -- BC time is lesser than AD time
				else
					return a[1] > b[1] -- 2 BC times: The higher number is lesser
				end
			end
		end)
		text = text .. ' (' .. times[1][2]
		for i = 2, #times do
			text = text .. ', ' .. times[i][2]
		end
		text = text .. ')'
	end
	return text
end

-- Handle a qualifier
-- Return new text inkl. the qualifier or nil to remove the statement from the results
local get_qualifier = function(args, text, qual, format, formatwithout, use)
	if not qual then
		-- No such qualifier
		if use == 'med' then
			-- Only statements with the qualifier is wanted, so remove this statement
			return nil
		else
			-- Otherwise return the statement with the formatwithout applied
			-- Use the table version of string.gsub to avoid having to escape % chars
			return (string.gsub(formatwithout, '$1', { ['$1'] = text }))
		end
	end
	if use == 'uden' then
		-- Only statements without the qualifier is wanted, so remove this statement
		return nil
	end

	-- These are used for monolingual texts. We will only get values for them if necessary
	local get_all, show_language, wanteds = false, false, false

	-- Get the qualifier. There can be several values, loop over them and separate with comma
	local qualtext, qualpure, testUseValue = {}, {}, ( use ~= 'alle' and use ~= 'med' and use~= '' ) -- 'uden' er elimineret her
	for key, q in pairs(qual) do
		if q.snaktype == 'novalue' then
			table.insert(qualtext, 'ingen')
		elseif q.snaktype == 'somevalue' then
			table.insert(qualtext, 'ukendt')
		else
			local datatype = q.datavalue.type
			if datatype == 'time' then
				table.insert(qualtext, p.format_time(args, q.datavalue.value))
			elseif datatype == 'monolingualtext' then
				if not wanteds then
					-- wanteds will be true if the language args are already fetched
					get_all, show_language, wanteds = get_lang_args(args)
				end
				if get_all or wanteds[q.datavalue.value.language] then
					if show_language then
						table.insert(qualtext, mw.text.nowiki(q.datavalue.value.text) .. ' (' ..
							mw.language.fetchLanguageName(q.datavalue.value.language, preferred_language) .. ')')
					else
						table.insert(qualtext, mw.text.nowiki(q.datavalue.value.text))
					end
				end
			elseif datatype == 'string' then
				table.insert(qualtext, mw.text.nowiki(q.datavalue.value))
			elseif datatype == 'url' or datatype == 'commonsMedia' or datatype == 'external-id' then
				table.insert(qualtext, q.datavalue.value)
			elseif datatype == 'quantity' then
				local textvalue = p.format_number(args, q.datavalue.value)
				table.insert(qualtext, textvalue)
			elseif datatype == 'wikibase-entityid' then
				table.insert(qualtext, p.get_label(args, q.datavalue.value.id, nil, nil))
				if testUseValue then
					-- q-value
					table.insert(qualpure, q.datavalue.value.id)
					-- label without link
					local entity = mw.wikibase.getEntity(q.datavalue.value.id)
					if entity then
						local label, lang = entity:getLabelWithLang(preferred_language)
						if label then table.insert(qualpure,label) end
					end
				end
			end
		end
	end
	if testUseValue then
		local function useValueInTable( tbl )
			for _, qualTextHere in pairs( tbl ) do
				if qualTextHere == use then return true end
			end
			return false
		end
		if ( not useValueInTable( qualtext ) and ( not useValueInTable( qualpure ) ) ) then
			return nil
		end
	end
	
	return (string.gsub(format, '$[12]', { ['$1'] = text, ['$2'] = table.concat(qualtext, ', ') }))
end

-- Handle requets for qualifiers for a statement
-- text is the already formated statement
-- Return the new text with qualifiers or nil to remove the statement from the results
local get_qualifiers = function(args, text, qualifiers, notime)
	if not notime and mw.text.trim(args.tid or '') == 'ja' then
		-- Check qualifiers for point of time, start time, and end time
		local times = get_qualifier_times(args, {}, qualifiers)
		text = text .. format_qualifier_times(times)
	end
   -- mw.logObject(qualifiers,'qualifiers')
	local qualno = 1
	repeat
		local qual = mw.text.trim(args['kvalifikator' .. tostring(qualno)] or '')
		if qual == '' then break end
		local format = mw.text.trim(args['kvalifikatorformat' .. tostring(qualno)] or '$1 ($2)')
		local formatwithout = mw.text.trim(args['kvalifikatorformatuden' .. tostring(qualno)] or '$1')
		local use = mw.text.trim(args['kvalifikatorbrug' .. tostring(qualno)] or 'alle')
		text = get_qualifier(args, text, qualifiers and qualifiers[qual], format, formatwithout, use)
		qualno = qualno + 1
	until not text
	return text
end

-- Determine if the string 'name' is in the list 'list' med comma separated names
-- Returns true if 'name' is in the list. 
local function inlist (name, list)
	for n in mw.text.gsplit(list, '%s*,%s*') do
		if n == name then return true end
	end
	return false
end

-- Determine if Wikidata should be used in this case
-- Uses the arguments ingen_wikidata (blacklist), wikidata (whitelist), navn (fieldname)
-- Returns true if OK to use Wikidata
p.use_wikidata = function(args)
	-- The name of the field that this function is called from is passed in the parameter "feltnavn"
	local fieldname = mw.text.trim(args.feltnavn or '')
	-- Use Wikidata if not specified
	if #fieldname == 0 then return true end

	-- The blacklist is passed in the parameter "ingen_wikidata"
	local blacklist = args.ingen_wikidata
	if blacklist and inlist(fieldname, blacklist) then return false end

	-- The whitelist is passed in the parameter "wikidata"
	local whitelist = mw.text.trim(args.wikidata or '')
	if whitelist == 'alle' or whitelist == 'ja' or inlist(fieldname, whitelist) then
		return true
	else
		return false
	end
end


-- Get the best statements (rank preferred if it exist, or else rank normal) for the required item and property
-- Sets the values the_entity, the_pid, the_qid
p.get_statements = function(args)
	-- Get the item to use from either the parameter q or the item connected to the current page
	the_qid = mw.text.trim(args.q or '')
	if the_qid == '' then
		the_qid = mw.wikibase.getEntityIdForCurrentPage()
	end
	the_entity = mw.wikibase.getEntity(the_qid)
	if not the_entity then return nil end

	-- Get the best statements (rank preferred if exist, else rank normal) for the required property
	the_pid = mw.text.trim(args[1] or "")
	return the_entity:getBestStatements(the_pid)
end

-- Make a link if wanted from a label
p.make_link = function(args, label, entity)
	-- Convert characters with special meaning in wikitext to HTML entities
	label = mw.text.nowiki(label)

	-- Use italics if requested
	local use_italics = mw.text.trim(args.kursiv or "")
	if use_italics == 'ja' then
		label = "''" .. label .. "''"
	end

	local link = mw.text.trim(args.link or "")
	if  link == 'nej' then
		-- link is not wanted
		return label
	end
	local sitelink = entity:getSitelink()
	if sitelink == nil then
		-- link is not possible
		return label
	end
	if sitelink == label then
		return '[[' .. sitelink .. ']]'
	else
		return '[[' .. sitelink .. '|' .. label .. ']]'
	end
end

-- Make text with message, reference, and category about using a fallback language
p.make_language_message = function(args, langcode, qid)
	local language = mw.language.fetchLanguageName(langcode, preferred_language)
	-- No language in parenthesis for now
	-- local text =' (' .. language .. ')'
	local text = ''
	local language_note = mw.text.trim(args.sprognote or '')
	if language_note ~= 'nej' then
		local ref_args = { name = 'sprog ' .. langcode .. qid }
		local language_notegroup = mw.text.trim(args.sprognotegroup or '')
		if language_notegroup ~= '' then
			ref_args.group = language_notegroup
		end
		text = text .. p.frame:extensionTag{ name = 'ref', content = string.format(fallback_note, language, qid), args = ref_args }
	end
	local language_cat = mw.text.trim(args.sprogkat or '')
	if language_cat ~= 'nej' then
		add_tracking_category(fallback_category)
	end
	return text
end

-- Get a value of type item from an entity if it is unique.
local get_unique_item_value = function(entity, pid)
	local statements = entity:getBestStatements(pid)
	if statements and #statements == 1 and
			statements[1].mainsnak and
			statements[1].mainsnak.snaktype == 'value' then
		return statements[1].mainsnak.datavalue.value.id
	end
	return nil
end

-- Format the label with upper case, link, extras (party or country), and language note
-- make_note is the item ID to link to if a note wanted, or else nil
local format_label = function(args, format, text, extra_text, entity, lang, use_ucfirst, make_note)
	if use_ucfirst then
		text = mw.getLanguage(lang):ucfirst(text)
	end
	local text = p.make_link(args, text, entity)
	if make_note then
		text = text .. p.make_language_message(args, lang, make_note)
	end
	if extra_text then
		return (string.gsub(format, '$[12]', { ['$1'] = text, ['$2'] = extra_text }))
	else
		return text
	end
end

-- Get the label of an item.
-- Creates a link using the sitelink if it exists unless the link argument is set to no
-- Converts the first character of the label to uppercase if use_ucfirst
-- Finds and adds country or political party for the item if requested in the args and get_extras is true
p.get_label = function(args, qid, use_ucfirst, get_extras)

	-- first we need the entity
	local entity = mw.wikibase.getEntity(qid)
	if not entity then return nil end

	local extra_text, extra_format
	local country_id, tried_to_get_country_id

	if get_extras then

		-- Find political party of the item if requested
		extra_format = mw.text.trim(args.parti or '')
		if extra_format ~= '' then
			-- P102 is member of political party
			local party_id = get_unique_item_value(entity, 'P102')
			if party_id then
				-- First try a shortname/abbreviation for the party 
				local party_entity = mw.wikibase.getEntity(party_id)
				if party_entity then
					-- P1813 is short name
					shortname_statements = party_entity:getBestStatements('P1813')
					for key, statement in pairs(shortname_statements) do
						if statement.mainsnak.snaktype == 'value' and
							statement.mainsnak.datavalue.value.language == 'da' then
							extra_text = statement.mainsnak.datavalue.value.text
							break -- one is enough. As we don't know which is best, take the first
						end
					end
					if not extra_text then
						-- No shortname, get the label
						local label, lang = party_entity:getLabelWithLang(preferred_language)
						if lang == preferred_language then
							extra_text =label
						end
					end
					if extra_text then
						extra_text = p.make_link(args, extra_text, party_entity)
					end
				end
			end
		else
			-- Party was not requested. Get country if requested	
			extra_format = mw.text.trim(args.land or "")
			if extra_format ~= '' then
				-- P17 is country
				country_id, tried_to_country_id = get_unique_item_value(entity, 'P17'), true
				-- Don't repeat the country if it refers to itself
				if country_id and country_id ~= qid then
					local label, lang = mw.wikibase.getLabelWithLang(country_id)
					if lang == preferred_language then
						extra_text = mw.text.nowiki(label)
						if mw.text.trim(args.link or "") ~= 'nej' then
						-- link to the country
							local link = mw.wikibase.sitelink(country_id)
							if link then
								-- There is an article (consider to use tracking category otherwise)
								extra_text = '[[' .. link .. '|' .. extra_text .. ']]'
							end
						end
					end
				end
			end
		end
	end

	-- Try the preferred language
	local label, lang = entity:getLabelWithLang(preferred_language)
	if lang == preferred_language then
		return format_label(args, extra_format, label, extra_text, entity, lang, use_ucfirst, nil)
	end

	-- Next try the local language if the item is located in certain countries
	if not country_id and not tried_to_get_country_id then
		-- P17 is country
		country_id, tried_to_country_id = get_unique_item_value(entity, 'P17'), true
	end
	if country_id then
		local fallback = fallback_langues_after_country[country_id]
		if fallback then
			local label, lang = entity:getLabelWithLang(fallback)
			if lang == fallback then
				return format_label(args, extra_format, label, extra_text, entity, lang, use_ucfirst, nil)
			end
		end
		if country_id == 'Q159' then		-- Q159 is Russia
			add_tracking_category(category_missing_russian_name)
		end
	end

	-- Find out if the item is a human
	local ishuman = nil
	-- P31 is instance of
	local instanceof = entity:getBestStatements('P31')
	for key, statement in pairs(instanceof) do
		-- Q5 is human
		if statement.mainsnak.snaktype == 'value' and statement.mainsnak.datavalue.value.id == 'Q5' then
			ishuman = true
			break
		end
	end

	if ishuman then
		-- Next for humans try first group of fallback languags for humans (Norgewian and Swedish)
		for i, fallback in ipairs(fallback_languages_humans) do
			local label, lang = entity:getLabelWithLang(fallback)
			if lang == fallback then
				return format_label(args, extra_format, label, extra_text, entity, lang, use_ucfirst, nil)
			end
		end

		-- Next for humans try the language of their country if there is one main language
		-- and it is written in a Latin script (the table of these is probably incomplete)
		-- P27 is country of citizenship
		local citizenship = get_unique_item_value(entity, 'P27')
		if citizenship then
			local fallback = fallback_langues_after_country[citizenship]
			if fallback then
				local label, lang = entity:getLabelWithLang(fallback)
				if lang == fallback then
					return format_label(args, extra_format, label, extra_text, entity, lang, use_ucfirst, nil)
				end
			end
			if citizenship == 'Q159' then		-- Q159 is Russia
				add_tracking_category(category_missing_russian_name)
			end
		end
	end

	-- Try the fallback languages
	for i, fallback in ipairs(fallback_languages) do
		local label, lang = entity:getLabelWithLang(fallback)
		if lang == fallback then
			return format_label(args, extra_format, label, extra_text, entity, lang, use_ucfirst, qid)
		end
	end

	-- Last resort: try any label
	local labels = entity.labels
	if labels then
		local lang, labeltable = next(labels)
		if lang then
			return format_label(args, extra_format, labeltable.value, extra_text, entity, lang, use_ucfirst, qid)
		else
			return nil
		end
	end
	return nil
end

p.format_statement_group = function(args, statements, startrange, endrange, use_ucfirst)
	local text = nil
	if statements[startrange].sortkey == 'novalue' then
		text = mw.text.trim(args.ingen or "")
		if (text == '') then return nil end
	elseif statements[startrange].sortkey == "somevalue" then
		text = mw.text.trim(args.ukendt or "")
		if (text == '') then return nil end
	else
		text = p.get_label(args, statements[startrange].sortkey, use_ucfirst, true)

		-- if the entity is a timezone in Russia, then add the MSK time if requested
		if mw.text.trim(args.msk or '') == 'ja' and msk_timezones[statements[startrange].sortkey] then
			text = text .. msk_timezones[statements[startrange].sortkey]
		end

		-- Go through all statements for time qualifiers if requested
		if mw.text.trim(args.tid or '') == 'ja' then
			local times = {}
			for i = startrange, endrange - 1 do
				local qualifiers = statements[i].qualifiers
				 get_qualifier_times(args, times, qualifiers)
			end
			text = text .. format_qualifier_times(times)
		end
	end

	-- handle qualifier arguments except for "tid" which may be used for statement groups
	-- and is handled separately above. When grouping is used, this call will have no effect
	-- because the qualifier arg will have turned grouping off in hent_emne().
	text = get_qualifiers(args, text, statements[startrange].qualifiers, true)
	text = get_references(args, text, statements[startrange].references)
	return text
end

-- format the list of statements with the wanted separator
p.output_all_statements = function(args, output)

	-- Avoid empty lists
	if #output == 0 then
		-- No tracking categories for empty results, as infoboxes or others may test if the result is empty or not
		return ''
	end

	-- Prepare an icon with link to Wikidata
	local icon = ''
	if mw.text.trim(args.ikon or '') == 'ja' then
		icon = '[[File:Blue pencil.svg|frameless|text-top|5px|alt=Rediger på Wikidata|link=d:' ..
				the_qid .. '#' .. the_pid .. '|Rediger på Wikidata]]'
	end

	local max = tonumber(args.maks or 1e6)
	local number = math.min(#output, max)
	local suffix
	if number == 1 then
		suffix = mw.text.trim(args.ental or '')
	else
		suffix = mw.text.trim(args.flertal or '')
	end

	local list = args.liste or ''
	if (list == 'ja') then
		return '<ul><li>' .. table.concat(output, '</li><li>', 1, number) .. '</li></ul>' .. icon .. suffix .. tracking_categories
	else
		local separator = args.adskil or ', '
		return table.concat(output, separator, 1, number) .. icon .. suffix .. tracking_categories
	end
end

p.hent_emne = function(frame)

	p.frame = frame

	-- If called via #invoke, use the args passed into the invoking template.
	-- Otherwise, for testing purposes, assume args are being passed directly in.
	local args
    if frame == mw.getCurrentFrame() then
		args = frame:getParent().args
	else
		args = frame.args
	end

	-- There may be a local parameter supplied. If set and not empty, return it unconditionally
	local input_parm =  mw.text.trim(args[2] or "")
	if input_parm and (#input_parm > 0) then return input_parm end

	-- Now test if Wikidate should be used for the current field.
	if not p.use_wikidata(args) then return '' end

	local statements = p.get_statements(args)
	if statements == nil or #statements == 0 then return end

	-- Sort the statements after snaktype (value, novalue, somevalue) and id
	-- This makes id possible to find and group equal values together
	for key, statement in pairs(statements) do
		if statement.mainsnak.snaktype == 'value' then
			if statement.mainsnak.datatype ~= 'wikibase-item' then
				-- The property has a wrong datatype, ignore it.
				return ''
			end
			statement.sortkey = statement.mainsnak.datavalue.value.id
		else
			statement.sortkey = statement.mainsnak.snaktype
		end
	end
	table.sort(statements, function (a,b)
		return (a.sortkey < b.sortkey)
	end)
	
    if #statements > 5 then -- finder sider hvor meget hentes fra Wikidata
    	if the_pid =='P463' then add_tracking_category(many_p463_category)
    	elseif the_pid =='P106' and #statements > 10 then add_tracking_category(tiplus_p106_category)
    	elseif the_pid =='P106' then add_tracking_category(many_p106_category)
    	elseif the_pid =='P1344' then add_tracking_category(many_p1344_category)
    	elseif the_pid =='P802' then add_tracking_category(many_p802_category)
    	elseif the_pid =='P800' then add_tracking_category(many_p800_category)
    	elseif the_pid =='P737' then add_tracking_category(many_p737_category)
    	elseif the_pid =='P166' then add_tracking_category(many_p166_category)
    	end
    end

	local output = {}
	local upper_case_labels = mw.text.trim(args.medstort or '')
	local firstvalue = true

	local qual1 = mw.text.trim(args.kvalifikator1 or '')
	local no_statement_grouping = qual1 ~= ''

	-- Go thru the statements and format them for displaying
	local startrange = 1
	local endrange = 1
	local only = mw.text.trim(args.kun or '')
	-- We need to keep track of the maximal allowed number of results here, so references 
	-- made with frame:extensionTag() for removed results will not stay behind. 
	local max = tonumber(args.maks)
	if not max then max = 1e6 end -- if no limit then set some limit we will never reach 
	while max > 0 and startrange <= #statements do
		local sortkey = statements[startrange].sortkey
		while endrange <= #statements and statements[endrange].sortkey == sortkey do
			endrange = endrange + 1
			if no_statement_grouping then
				-- We have qualifiers to check for each statement, so we cannot group
				-- statements with equal values together.
				break
			end
		end
		-- Check if only results with a certain value is requested 
		if only == '' or only == sortkey then
			local use_ucfirst = upper_case_labels == 'alle' or (upper_case_labels == 'ja' and firstvalue)
			local text = p.format_statement_group(args, statements, startrange, endrange, use_ucfirst)
			if text then
				table.insert(output, text)
				max = max - 1
				firstvalue = false
			end
		end
		startrange = endrange
	end

	return p.output_all_statements(args, output)
end

-- Format a time value
-- Return formatted text for the date
p.format_time = function(args, value)
	-- Parse the ISO 8601 time value
	-- The format is like '+2000-00-00T00:00:00Z'.
	-- For now the value can only contain date. Their is no timezone or time.

	local sign, year, month, day = string.match(value.time, '^([%+-])0*(%d+)-0?(%d+)-0?(%d+)T')
	if not sign then
		-- No match. We can consider an error message or category for this, but ignore for now
		return nil
	end

	-- handle year and AD/BC
	local bc_text = ''	-- text for AD or BC
	if sign == '-' then
		-- Year 0 doesn't exist, year 1,2,3 BC etc is given as -1,-2,-3 etc.
		-- (or maybe also in some cases as 0,-1,-2 etc.?)
		-- (see also [[d:Help:Dates#Years BC]])
		bc_text = bc
	end

	if value.precision >= 9 then
		-- precision is year or greater
		local date = year .. bc_text
		local yearonly = mw.text.trim(args['kunår'] or "") == 'ja'
		if not yearonly and value.precision >= 10 then
			-- precision is month or greater: Prepend the month
			date = months[month] .. date
			if value.precision >= 11 then
				-- precision is day or greater: Prepend the day
				date = day .. '. ' .. date

				-- Compare the date with another date and calculate age if requested
				local agepid =  mw.text.trim(args['alder'] or "")
				if agepid ~= '' then
					local ageformat = mw.text.trim(args['alderformat'] or "$1 ($3 år)")
					local agestatements = the_entity:getBestStatements(agepid)
					local agevalue = agestatements and #agestatements == 1 and
						agestatements[1].mainsnak and agestatements[1].mainsnak.snaktype == 'value' and
						agestatements[1].mainsnak.datavalue.value
					if agevalue and agevalue.precision >= 11 then
						agedate = p.format_time({['kunår']=args['kunår']}, agevalue)
						local age
						local agesign, ageyear, agemonth, ageday = string.match(agevalue.time, '^([%+-])0*(%d+)-0?(%d+)-0?(%d+)T')
						-- First get the difference in the years. Remember that year 0 doesn't exist.
						if agesign == '-' then
							if sign == '-' then
								age = tonumber(ageyear) - tonumber(year)		-- e.g. 100 BC - 50 BC
							else
								age = tonumber(ageyear) + tonumber(year) - 1	-- e.g. 10 BC - 40 AD
							end
						else
							if sign == '-' then
								age = 1 - tonumber(year) - tonumber(ageyear)		-- e.g 40 AD - 10 BC (negative gives no sense)
							else
								age = tonumber(year) - tonumber(ageyear)			-- e.g. 50 AD - 100 AD
							end
						end
						--Substract a year if the birthday isn't reached in the last year
						if tonumber(month) < tonumber(agemonth) or
							(tonumber(month) == tonumber(agemonth) and tonumber(day) < tonumber(ageday)) then
							age = age - 1
						end
						return (string.gsub(ageformat, '$[123]', { ['$1'] = date, ['$2'] = agedate, ['$3'] = tostring(age) }))
					end -- if agevalue
				end -- if agepid
			end -- if value.precision >= 11 then
		end -- if value.precision >= 10 then
		return date
	end --if value.precision >= 9 then

	-- precision is less than year
	local year_num = tonumber(year)
	if value.precision == 8 then
		-- precision is decade
		-- 10 (or any number in the period) seems to mean years 10-19,
		-- 20 (or any number in the period) seems to mean years 20-29,
		-- 2010 (or any number in the period) seems to mean years 2010-2019
		if year_num <= 10 then
			return '1. årti' .. bc_text
		else
			-- Make sure the number ends with 0
			return tostring(math.floor(year_num/10)*10) .. "'erne" .. bc_text
		end
	end

	if value.precision == 7 then
		-- precision is century
		-- 100 (or any number in the period) seems to mean years 1-100,
		-- 200 (or any number in the period) seems to mean years 101-200,
		-- 2100 (or any number in the period) seems to mean years 2001-2100
		if year_num <= 100 then
			return '1. århundrede' .. bc_text
		else
			-- Make sure the number ends with 00 and convert the period one year down
			return tostring(math.floor((year_num - 1)/100)*100) .. '-tallet' .. bc_text
		end
	end

	-- precision less than century is not handled
	return nil
	-- if value.precision == 6 then -- precision is 1000 years
	-- if value.precision == 5 then -- precision is 10.000 years
	-- if value.precision == 4 then -- precision is 100.000 years
	-- if value.precision == 3 then -- precision is million years
	-- if value.precision == 2 then -- precision is 10 million years
	-- if value.precision == 1 then -- precision is 100 million years
	-- if value.precision == 0 then -- precision is 1 billion years
end

p.hent_tid = function(frame)

	p.frame = frame

	-- If called via #invoke, use the args passed into the invoking template.
	-- Otherwise, for testing purposes, assume args are being passed directly in.
	local args
    if frame == mw.getCurrentFrame() then
		args = frame:getParent().args
	else
		args = frame.args
	end

	-- There may be a local parameter supplied. If set and not empty, return it unconditionally
	local input_parm =  mw.text.trim(args[2] or "")
	if input_parm and (#input_parm > 0) then return input_parm end

	-- Now test if Wikidate should be used for the current field.
	if not p.use_wikidata(args) then return '' end

	local statements = p.get_statements(args)
	if statements == nil or #statements == 0 then return end

	local output = {}

	-- Go thru the statements and format them for displaying
	for key, statement in pairs(statements) do
		local text = nil
		if statement.mainsnak.snaktype == 'value' then
			if statement.mainsnak.datatype == 'time' then
				text = p.format_time(args, statement.mainsnak.datavalue.value)
			end
		elseif statement.mainsnak.snaktype == 'novalue' then
			text = mw.text.trim(args.ingen or "")
		elseif statement.mainsnak.snaktype == 'somevalue' then
			text = mw.text.trim(args.ukendt or "")
		end
		if text then
			text = get_qualifiers(args, text, statement.qualifiers)
			text = get_references(args, text, statement.references)
			if text and text ~= '' then
				table.insert(output, text)
			end
		end
	end

	return p.output_all_statements(args, output)
end

local insert_delimiters = function(number)
	-- first change the decimal mark to comma
	number = string.gsub(number, '%.', ',')
	repeat
		-- Find the group of 3 digits and insert delimiter
		local matches
		number, matches = string.gsub(number, "^(-?%d+)(%d%d%d)", '%1.%2')
	until matches == 0
	return number
end

local make_the_number = function(args, amount, diff, unit)

	local decimals = mw.text.trim(args.decimaler or '0')
	if decimals == 'smart' then
		if amount > 100 then decimals = '0'
		elseif amount > 10 then decimals = '1'
		elseif amount > 1 then decimals = '2'
		else decimals = '3' end
	else
		decimals = tostring(math.floor(tonumber(decimals) or 0))
	end

	-- First the amount
	local text = insert_delimiters(string.format('%.' .. decimals .. 'f', amount))

	-- Second the variation
	if diff > 0 and mw.text.trim(args.visusikkerhed or "") ~= 'nej' then
		local difftext = string.format('%.' .. decimals .. 'f', diff)
		-- Don't show the diff it if is rounded to 0
		if tonumber(difftext) ~= 0 then
			text = text .. '±' .. insert_delimiters(difftext)
		end
	end

	-- Third the unit
	if unit and mw.text.trim(args.visenhed or "") ~= 'nej' then
		text = text .. ' ' .. unit
	end
	return text
end

-- Format a quantity value and return the formated value
-- Also return the amount as a number if it a quantity with a recogniced unit
-- (used to get area)
p.format_number = function(args, value)

	local amount = tonumber(value.amount)
	local diff1, diff2 = 0, 0
	if value.lowerBound then
		diff1 = amount - tonumber(value.lowerBound)
	end
	if value.upperBound then
		diff2 = tonumber(value.upperBound) - amount
	end
	local diff = math.max(diff1, diff2)

	local unit = value.unit
	if unit == '1' then
		-- Number without unit
		local text = make_the_number(args, amount, diff, nil)

		-- We may have to find the area and calculate the density
		local densityformat = mw.text.trim(args['arealogtæthed'] or '')
		if densityformat ~= '' then
			local area_text, area_number, density_text
			-- P2046 is area
			area_statements = the_entity:getBestStatements('P2046')
			if area_statements and #area_statements == 1 and area_statements[1].mainsnak.snaktype == 'value' then
				area_text, area_number =
					p.format_number({ enhed='km2', visenhed='nej', visusikkerhed=args.visusikkerhed, decimaler='smart' },
						area_statements[1].mainsnak.datavalue.value)
				if area_number then
					density_text = p.format_number({ decimaler='smart' }, { amount=amount / area_number, unit='1' })
					return (string.gsub(densityformat, '$[123]', { ['$1'] = text, ['$2'] = area_text, ['$3'] = density_text }))
				end
			end
			-- No area found. Return the value with densityformatwithout applied
			local densityformatwithout = mw.text.trim(args['arealogtætheduden'] or '$1')
			return (string.gsub(densityformatwithout, '$1', { ['$1'] = text }))
		end
		-- area and density was not asked for.
		return text
	end

	unit_qid = string.match(unit, 'http://www%.wikidata%.org/entity/(Q%d+)$')
	if not unit_qid then
		-- Unknown unit format
		return nil
	end

	wd_unit = wd_units[unit_qid]
	if not wd_unit then
		-- The unit is not in our table. Here we could read information
		-- about the unit entity. Maybe to be done later
		add_tracking_category (category_unrecognized_unit)
		return make_the_number(args, amount, diff, nil)
	end
	wanted_unit = mw.text.trim(args.enhed or "")
	if wanted_unit == '' and wd_unit.conv_to then
		wanted_unit = wd_unit.conv_to
	end

	local wanted = wanted_units[wanted_unit]
	if wanted and wd_unit.name ~= wanted_unit and wd_unit.type == wanted.type then
		amount = amount * wd_unit.conv * wanted.conv
		diff = diff * wd_unit.conv * wanted.conv
		return make_the_number(args, amount, diff, wanted.show_as), amount
	end
	return make_the_number(args, amount, diff, wd_unit.show_as), amount
end

p.hent_tal = function(frame)

	p.frame = frame

	-- If called via #invoke, use the args passed into the invoking template.
	-- Otherwise, for testing purposes, assume args are being passed directly in.
	local args
    if frame == mw.getCurrentFrame() then
		args = frame:getParent().args
	else
		args = frame.args
	end

	-- There may be a local parameter supplied. If set and not empty, return it unconditionally
	local input_parm =  mw.text.trim(args[2] or "")
	if input_parm and (#input_parm > 0) then return input_parm end

	-- Now test if Wikidate should be used for the current field.
	if not p.use_wikidata(args) then return '' end

	local statements = p.get_statements(args)
	if statements == nil or #statements == 0 then return end
	local output = {}

	-- Go thru the statements and format them for displaying
	for key, statement in pairs(statements) do
		local text = nil
		if statement.mainsnak.snaktype == 'value' then
			if statement.mainsnak.datatype == 'quantity' then
				text = p.format_number(args, statement.mainsnak.datavalue.value)
			end
		elseif statement.mainsnak.snaktype == 'novalue' then
			text = mw.text.trim(args.ingen or "")
		elseif statement.mainsnak.snaktype == 'somevalue' then
			text = mw.text.trim(args.ukendt or "")
		end
		-- mw.logObject(statement.references,'statement.references')
		if text then
			text = get_qualifiers(args, text, statement.qualifiers)
			text = get_references(args, text, statement.references)
			if text and text ~= '' then
				table.insert(output, text)
			end
		end
	end

	return p.output_all_statements(args, output)
end

-- Handle datatypes string, url, commonsMedia, external-id which have similar structures
p.hent_streng = function(frame)

	p.frame = frame

	-- If called via #invoke, use the args passed into the invoking template.
	-- Otherwise, for testing purposes, assume args are being passed directly in.
	local args
	if frame == mw.getCurrentFrame() then
		args = frame:getParent().args
	else
		args = frame.args
	end

	-- There may be a local parameter supplied. If set and not empty, return it unconditionally
	local input_parm =  mw.text.trim(args[2] or "")
	if input_parm and (#input_parm > 0) then return input_parm end

	-- Now test if Wikidate should be used for the current field.
	if not p.use_wikidata(args) then return '' end

	local statements = p.get_statements(args)
	if statements == nil or #statements == 0 then return end
	local output = {}

	-- For formating of strings. $1 in the format vil be replaced by the string 
	local format = mw.text.trim(args.format or '')
	if format == '' then
		format = nil
	end

	-- Go thru the statements and format them for displaying
	for key, statement in pairs(statements) do
		local text = nil
		if statement.mainsnak.snaktype == 'value' then
			if statement.mainsnak.datatype == 'string' then
				text = mw.text.nowiki(statement.mainsnak.datavalue.value)
			elseif statement.mainsnak.datatype == 'url' or
				statement.mainsnak.datatype == 'commonsMedia' or
				statement.mainsnak.datatype == 'external-id' then
				text = statement.mainsnak.datavalue.value
			end
			if format and text then
				-- We have to escape any % in the found string with another % before using it as repl in string.gsub
				text = string.gsub(text, '%%', '%%%%')
				text = string.gsub(format, '$1', text)
			end
		elseif statement.mainsnak.snaktype == 'novalue' then
			text = mw.text.trim(args.ingen or "")
		elseif statement.mainsnak.snaktype == 'somevalue' then
			text = mw.text.trim(args.ukendt or "")
		end
		if text then
			text = get_qualifiers(args, text, statement.qualifiers)
			text = get_references(args, text, statement.references)
			if text and text ~= '' then
				table.insert(output, text)
			end
		end
	end

	return p.output_all_statements(args, output)
end

p.hent_tekst = function(frame)

	p.frame = frame

	-- If called via #invoke, use the args passed into the invoking template.
	-- Otherwise, for testing purposes, assume args are being passed directly in.
	local args
	if frame == mw.getCurrentFrame() then
		args = frame:getParent().args
	else
		args = frame.args
	end

	-- There may be a local parameter supplied. If set and not empty, return it unconditionally
	local input_parm =  mw.text.trim(args[2] or "")
	if input_parm and (#input_parm > 0) then return input_parm end

	-- Now test if Wikidate should be used for the current field.
	if not p.use_wikidata(args) then return '' end

	local statements = p.get_statements(args)
	if statements == nil or #statements == 0 then return end
	local output = {}

	local get_all, show_language, wanteds = get_lang_args(args)	

	-- Go thru the statements and format them for displaying
	for key, statement in pairs(statements) do
		local text = nil
		if statement.mainsnak.snaktype == 'value' then
			if statement.mainsnak.datatype == 'monolingualtext' then
				if get_all or wanteds[statement.mainsnak.datavalue.value.language] then
					text = mw.text.nowiki(statement.mainsnak.datavalue.value.text)
					if show_language then
						text = text .. ' (' ..
							mw.language.fetchLanguageName(statement.mainsnak.datavalue.value.language, preferred_language) .. ')'
					end
				end
			end
		elseif statement.mainsnak.snaktype == 'novalue' then
			text = mw.text.trim(args.ingen or "")
		elseif statement.mainsnak.snaktype == 'somevalue' then
			text = mw.text.trim(args.ukendt or "")
		end
		if text then
			text = get_qualifiers(args, text, statement.qualifiers)
			text = get_references(args, text, statement.references)
			if text and text ~= 0 then
				table.insert(output, text)
			end
		end
	end

	return p.output_all_statements(args, output)
end

-- Get Wikidata entity ID for the current page.
-- Arguments: format: format string with $1 for the ID, ingen: text to return for no entity
p.hent_id = function(frame)
	local args
	 if frame == mw.getCurrentFrame() then
		args = frame:getParent().args
	else
		args = frame.args
	end
	
-- Get the item to use from either the parameter q or the item connected to the current page
	local qid = mw.text.trim(args.q or '')
	if qid == '' then
		qid = mw.wikibase.getEntityIdForCurrentPage()
	end	
	
	if qid then
		local format = mw.text.trim(args.format or '$1')
		return (string.gsub(format, '$1', { ['$1'] = qid }))
	else
		return mw.text.trim(args.ingen or '')
	end
end

return p