WikiMilano - Protagonisti metropolitani è organo ufficiale dell'Osservatorio Metropolitano di Milano

non sei un utente registrato: al momento non puoi creare e modificare pagine.

Questa pagina è protetta dallo spostamento
Questa pagina è protetta

Modulo:Bio

Jump to navigation Jump to search

Modulo Lua che implementa le funzionalità del Template:Bio.

Ha le seguenti sottopagine di configurazione:

  • Configurazione: parametri di configurazione principali
  • Link attività: tabella di conversione per il link all'attività
  • Link nazionalità: tabella di conversione per il link alla nazionalità
  • Plurale attività: tabella di conversione per il plurale dell'attività
  • Plurale nazionalità: tabella di conversione per il plurale della nazionalità
  • Parametri: tabella per configurare i parametri accettati dal modulo e i rispettivi valori validi
  • Cat luoghi: tabella di casi particolari per le categorie "Nati/morti a [luogo]"

Funzionamento interno

Lo schema seguente rappresenta l'ordine in cui vengono chiamate le principali funzioni, facendo riferimento a dei parametri di esempio:

{{Bio
|Nome = Giulia
|Cognome = Rossi
|Sesso = F
|LuogoNascita = Roma
|GiornoMeseNascita = 15 gennaio
|AnnoNascita = 1910
|LuogoMorte = Firenze
|GiornoMeseMorte = 15 febbraio
|AnnoMorte = 1990
|Attività = scienziata
|Nazionalità = italiana
}}

--[[
* Modulo per implementare le funzionalità del template Bio.
*
* Nota: non esistendo in Lua una differenziazione tra metodi pubblici e privati, 
* per convenzione, quelli privati iniziano con un underscore.
]]

require("Module:No globals")

-- Variabili globali
local args             -- argomenti passati al template
local errorTable = {}  -- table per contenere gli errori
local attivitaParams = { "Attività", "Attività2", "Attività3" }
local nazionalitaParams = { "Nazionalità", "NazionalitàNaturalizzato", "Cittadinanza" }
local cfg = mw.loadData("Modulo:Bio/Configurazione")
local mString = require("Modulo:String")
local mWikidata = require("Modulo:Wikidata")

-------------------------------------------------------------------------------
--                           Funzioni di utilità
-------------------------------------------------------------------------------

-- Aggiunge l'output del [[Template:Avviso]] e una categoria di warning a errorTable
local function addAvviso(testo, category)
	local text

	text = mw.getCurrentFrame():expandTemplate {
		title = "Avviso",
		args = {
			tipo = "stile",
			immagine = "[[File:Nuvola apps important.svg|40px]]",
			["immagine a destra"] = "[[File:Crystal Clear app Login Manager.svg|40px]]",
			testo = testo
		}
	}

	table.insert(errorTable, text)
	if mw.title.getCurrentTitle().namespace == 0 then
		table.insert(errorTable, string.format("[[Categoria:%s]]\n", cfg.categorie[category]))
	end
end

-- Wrapper di mw.title.exists, verifica sia che name sia valido, sia che esista
local function titleExists(name)
	local title = mw.title.new(name)
	return title and title.exists
end

local function currentTitleEquals(name)
	local title = mw.title.getCurrentTitle().text
	return mw.text.split(title, " %(")[1] == name
end

-- Se date inizia con "1 " o "1°" ritorna una nuova data che inizia per "1º", altrimenti date
local function fixFirstOfMonth(date)

	date = date:gsub("^1%s", "1º ")
	date = date:gsub("^1\194\176", "1º")
	return string.lower(date) --bruno
end

-- Ritorna "ed" se nextWord inizia con "e", altrimenti "e"
local function getEufonica(nextWord)
	return nextWord:sub(1, 1) == "e" and "ed" or "e"
end

--bruno Ritorna "ad" se nextWord inizia con "A", altrimenti "a"
local function getAfonica(nextWord)
	return nextWord:sub(1, 1)== "A" and "ad" or "a"
end

-- Ritorna true se uno degli argomenti del modulo specificati (params) ha almeno
-- un valore tra quelli indicati (values), altrimenti false
local function argsSearch(params, values)
	local ret = false
	for _, param in ipairs(params) do
		for _, value in ipairs(values) do
			if args[param] == value then
				ret = true
				break
			end
		end
	end
	return ret
end

-- Parsifica un TimeValue di Wikidata e ne ritorna "giornomese, anno"
local function parseWikidataTimeValue(property)
	local timestamp, precision, year, month, day, daymonth

	timestamp = mWikidata._getProperty( { property, n = 1, formatting = "raw" } )
	precision = mWikidata._getProperty( { property, n = 1, time = "precision" } )
	if timestamp and precision then
		year, month, day = timestamp:match("(%d+)%-(%d%d)%-(%d%d).+")
		if precision == 11 then
			month = mw.getLanguage("it"):formatDate("F", tonumber(year) .. "-" .. month .. "-" .. day)
			daymonth = tonumber(day) .. " " .. month
		end
		if precision == 9 or precision == 11 then
			year = tonumber(year) .. (timestamp:sub(1, 1) == "-" and " a.C." or "")
		end
	end

	return daymonth, year
end

-- Cerca alcuni parametri se mancanti su Wikidata
local function checkWikidata()
	local daymonth, year
	-- GiornoMeseNascita e AnnoNascita
	if not args.GiornoMeseNascita or not args.AnnoNascita then
		daymonth, year = parseWikidataTimeValue("P569")
		args.GiornoMeseNascita = args.GiornoMeseNascita or daymonth
		args.AnnoNascita = args.AnnoNascita or year
	end
	-- GiornoMeseMorte e AnnoMorte
	if not args.GiornoMeseMorte or not args.AnnoMorte then
		daymonth, year = parseWikidataTimeValue("P570")
		args.GiornoMeseMorte = args.GiornoMeseMorte or daymonth
		args.AnnoMorte = args.AnnoMorte or year
	end
end

-------------------------------------------------------------------------------
--                           classe ArgsParser
-------------------------------------------------------------------------------

local ArgsParser = {}

function ArgsParser:new()
	local self = {}
	setmetatable(self, { __index = ArgsParser })
	return self
end

-- Parsifica i parametri passati al modulo e aggiunge eventuali categorie di errore.
-- Ritorna i parametri conosciuti scartando quelli valorizzati a stringa vuota.
function ArgsParser:parse(origArgs)
	local paramcfg = require("Modulo:Bio/Parametri")
	local retArgs = {}

	-- controlla i parametri conosciuti e li copia
	for k, v in pairs(origArgs) do
		if paramcfg.params[k] then
			if v ~= "" then
				retArgs[k] = v
			end
		else
			addAvviso(cfg.warningParams.testo:gsub("$1", "il parametro '" ..
					  (tonumber(k) and (v == "" and " " or v) or k ) .. "' è sconosciuto"), "unknown-params")
		end
	end

	-- controlla il valore
	for i, validator in pairs(paramcfg.validators) do
		if retArgs[validator.param] then
			if not self:_checkParamValue(retArgs[validator.param], validator.valuetest, retArgs) then
				if validator.errmsg then
					addAvviso(cfg.warningParams.testo:gsub("$1", validator.errmsg), "wrong-params")
				end
			end
		end
	end

	return retArgs
end

-- Utilizzata da parse per controllare il valore di un parametro.
-- Ritorna true se il valore è valido altrimenti false.
function ArgsParser:_checkParamValue(value, valueTest, otherArgs)
	local ret = true

	if type(valueTest) == "function" then
		ret = valueTest(value, otherArgs)
	elseif type(valueTest) == "string" and not value:match(valueTest) then
		ret = false
	end

	return ret
end

-------------------------------------------------------------------------------
--                           classe CategoryManager
-------------------------------------------------------------------------------

local CategoryManager = {}

function CategoryManager:new()
	local self = {}

	setmetatable(self, { __index = CategoryManager })
	self.plurale_attivita = nil
	self.plurale_nazionalita = nil
	self.categories = {}
	-- al di fuori del namespace 0 esegue comunque il controllo di attività e nazionalità
	self.plurals = self:_getPluralsAttivitaNazionalita()

	if mw.title.getCurrentTitle().namespace == 0 or args.Debug then
		-- imposta la magic word defaultsort
		local sortkey
		if args.ForzaOrdinamento then
			sortkey = args.ForzaOrdinamento:gsub("(.-)%s*,%s*(.*)", "%1 ,%2")
		elseif args.Pseudonimo and currentTitleEquals(args.Pseudonimo) then
			local pseudonimo = mString.collate( { args = { args.Pseudonimo } } )
			if pseudonimo ~= args.Pseudonimo then
				sortkey = pseudonimo
			end
		elseif args.Cognome and args.Nome then
			sortkey = mString.collate( { args = { args.Cognome .. " ," .. args.Nome } } )
		elseif args.Nome then
			local nome = mString.collate( { args = { args.Nome } } )
			if nome ~= args.Nome then
				sortkey = nome
			end
		end
		if sortkey then
			if args.Debug then
				-- per i test di DEFAULTSORT in Modulo:Bio/test
				table.insert(self.categories, string.format("DEFAULTSORT:%s", sortkey))
			else
				mw.getCurrentFrame():preprocess("{{DEFAULTSORT:" .. sortkey .. "}}")
			end
		end
		-- Categorie impostato a "no" disabilita la categorizzazione per attività
		if args.Categorie ~= "no" then
			self:_addAttivita(self.plurals)
		end
		self:_addNatiMorti()
		self:_addCategory(cfg.categorie["bot"])
		-- categoria di servizio per AnnoMorte (o anno corrente) - AnnoNascita > 122
		local years = {
			birth = tonumber(args.AnnoNascita),
			death = not args.AnnoMorte and os.date("%Y") or tonumber(args.AnnoMorte)
		}
		if years.birth and years.death and years.death - years.birth > 122 then
			self:_addCategory(cfg.categorie["controllo-età"])
		end
		-- eventuali categorie di servizio per Wikidata
		if not args.Debug then
			self:_addCategoriesWikidata()
		end
	end

	return self
end

function CategoryManager:getCategories()
	return self.categories
end

function CategoryManager:_addCategory(cat)
	table.insert(self.categories, string.format("[[Categoria:%s]]", cat))
end

-- Aggiunge la categoria se la pagina non ha un elemento Wikidata collegato,
-- oppure è senza proprietà o non ha la proprietà indicata.
function CategoryManager:_addCategoryWikidata(property, cat)
	local entity = mw.wikibase.getEntity()
	if not entity or not entity.claims or not entity.claims[property] then
		self:_addCategory(cat)
	end
end

-- Aggiunge eventuali categorie di servizio per Wikidata, tramite controlli
-- più avanzati di quelli che si effettuano abitualmente con {{Controllo Wikidata}}.
function CategoryManager:_addCategoriesWikidata()
	--bruno tolto
end

-- Ritorna il plurale dell'attività o nil se non trovato (con eventuale warning)
function CategoryManager:_getPluralAttivita(attivita)
	local plural

	self.plurale_attivita = self.plurale_attivita or mw.loadData("Modulo:Bio/Plurale attività")
	plural = self.plurale_attivita[attivita]
	if not plural then
		addAvviso(cfg.warningA.testo .. cfg.warningA.testo2a:gsub("$1", attivita) .. cfg.warningA.testo3, "warning")
	end

	return plural
end

-- Ritorna il plurale della nazionalità o nil se non trovato (con eventuale warning)
function CategoryManager:_getPluralNazionalita(nazionalita)
	local plural

	self.plurale_nazionalita = self.plurale_nazionalita or mw.loadData("Modulo:Bio/Plurale nazionalità")
	plural = self.plurale_nazionalita[nazionalita]
	if not plural then
		addAvviso(cfg.warningN.testo .. cfg.warningN.testo2a:gsub("$1", nazionalita) .. cfg.warningN.testo3, "warning")
	end

	return plural
end

-- Ritorna il plurale dei parametri necessari per le categorie
function CategoryManager:_getPluralsAttivitaNazionalita()
	local plurals = {}

	-- Nazionalità può essere vuota solo quando c'è Categorie=no e FineIncipit
	if not args["Nazionalità"] and not (args.Categorie == "no" and args.FineIncipit) then
		addAvviso(cfg.warningN.testo .. cfg.warningN.testo2b .. cfg.warningN.testo3, "warning")
	end
	-- Nazionalità può essere sbagliata solo quando c'è Categorie=no e manca FineIncipit
	if not (args.Categorie == "no" and not args.FineIncipit) then
		for _, nazionalita in ipairs(nazionalitaParams) do
			if args[nazionalita] then
				plurals[nazionalita] = self:_getPluralNazionalita(args[nazionalita])
			end
		end
	end
	-- Attività può essere vuota solo quando c'è Categorie=no e FineIncipit
	if not args["Attività"] and not (args.Categorie == "no" and args.FineIncipit) then
		addAvviso(cfg.warningA.testo .. cfg.warningA.testo2b .. cfg.warningA.testo3, "warning")
	end
	-- Attività può essere sbagliata solo quando c'è Categorie=no e manca FineIncipit
	if not (args.Categorie == "no" and not args.FineIncipit) then
		for _, attivita in ipairs(attivitaParams) do
			if args[attivita] then
				plurals[attivita] = self:_getPluralAttivita(args[attivita])
			end
		end
	end

	return plurals
end

-- Aggiunge le categorie: Attività nazionalità [del XYZ secolo]
function CategoryManager:_addAttivita(plurals)
	local catname, epoca1, epoca2, added

	epoca1 = args.Epoca and cfg.epoche[args.Epoca]
	epoca2 = args.Epoca2 and cfg.epoche[args.Epoca2]
	for _, attivita in ipairs(attivitaParams) do
		if plurals[attivita] then
			for _, nazionalita in ipairs(nazionalitaParams) do
				if plurals[nazionalita] then
					added = false
					catname = plurals[attivita] --bruno.. " " .. plurals[nazionalita]
					for _, epoca in ipairs({ epoca1, epoca2 }) do
						if epoca and titleExists("Categoria:" .. catname .. " " .. epoca) then
							self:_addCategory(catname .. " " .. epoca)
							added = true
						end
					end
					-- se non è stata aggiunta la categoria per epoca1 e epoca2
					-- aggiunge la cat. semplice, e.g. "Scrittori italiani"
					if not added then
						self:_addCategory(catname)
					end
				end
			end
		end
	end
end

-- Utilizzata da addNatiMorti, ritorna il nome della categoria
-- se titleLink o title sono nella lista di eccezioni Cat luoghi, altrimenti nil
function CategoryManager:_getCatLuoghi(titleLink, title, catPrefix)
	local cat

	self.catLuoghi = self.catLuoghi or mw.loadData("Modulo:Bio/Cat luoghi")
	if titleLink and title then
		cat = self.catLuoghi[titleLink]
	elseif title then
		cat = self.catLuoghi[title]
	end

	return cat and (catPrefix .. " " .. cat) or nil
end

-- Aggiunge le categorie: Nati/Morti nell'anno/giorno/luogo
function CategoryManager:_addNatiMorti()
	local cat1, cat2

	if args.AnnoNascita then
		cat1 = "Nati nel " .. args.AnnoNascita
	--bruno	cat2 = "Nati nell'" .. args.AnnoNascita
	--bruno	if titleExists("Categoria:" .. cat1) then
			self:_addCategory(cat1)
	--bruno	elseif titleExists("Categoria:" .. cat2) then
	--bruno		self:_addCategory(cat2)
	--bruno	end
	end

	if args.AnnoMorte then
		if args.AnnoMorte == "?" then
			self:_addCategory(cfg.categorie["annomorte-punto-interrogativo"])
		else
			cat1 = "Morti nel " .. args.AnnoMorte
	--bruno		cat2 = "Morti nell'" .. args.AnnoMorte
	--bruno		if titleExists("Categoria:" .. cat1) then
				self:_addCategory(cat1)
	--bruno		elseif titleExists("Categoria:" .. cat2) then
	--bruno			self:_addCategory(cat2)
	--bruno		end
		end
	else
	--bruno	self:_addCategory(cfg.categorie["annomorte-assente"])
	end

	if args.GiornoMeseNascita then
		cat1 = "Nati il " .. fixFirstOfMonth(args.GiornoMeseNascita)
	--bruno	cat2 = "Nati l'" .. args.GiornoMeseNascita
	--bruno	if titleExists("Categoria:" .. cat1) then
			self:_addCategory(cat1)
	--bruno	elseif titleExists("Categoria:" .. cat2) then
	--bruno		self:_addCategory(cat2)
	--bruno	end			   
	end
	
	if args.GiornoMeseMorte then
		cat1 = "Morti il " .. fixFirstOfMonth(args.GiornoMeseMorte)
	--bruno	cat2 = "Morti l'" .. args.GiornoMeseMorte
	--bruno	if titleExists("Categoria:" .. cat1) then
			self:_addCategory(cat1)
	--bruno	elseif titleExists("Categoria:" .. cat2) then
	--bruno		self:_addCategory(cat2)
	--bruno	end			   
	end

	-- prima di verificare le categorie per LuogoNascitaLink e LuogoNascita
	-- viene controllata una lista di eccezioni
	cat1 = self:_getCatLuoghi(args.LuogoNascitaLink, args.LuogoNascita, "Nati")
	if cat1 then
		self:_addCategory(cat1)
	elseif args.LuogoNascitaLink then
		cat1 = "Nati " .. getAfonica(args.LuogoNascitaLink) .. " " .. args.LuogoNascitaLink
	--bruno	cat2 = "Nati ad " .. args.LuogoNascitaLink
	--bruno	if titleExists("Categoria:" .. cat1) then
			self:_addCategory(cat1)
	--bruno	elseif titleExists("Categoria:" .. cat2) then
	--bruno		self:_addCategory(cat2)
	--bruno	end
	elseif args.LuogoNascita then
		cat1 = "Nati " .. getAfonica(args.LuogoNascita) .. " " .. args.LuogoNascita
	--bruno	cat2 = "Nati ad " .. args.LuogoNascita
	--bruno	if titleExists("Categoria:" .. cat1) then
			self:_addCategory(cat1)
	--bruno	elseif titleExists("Categoria:" .. cat2) then
	--bruno		self:_addCategory(cat2)
	--bruno	end
	end

	-- prima di verificare le categorie per LuogoMorteLink e LuogoMorte
	-- viene controllata una lista di eccezioni
	cat1 = self:_getCatLuoghi(args.LuogoMorteLink, args.LuogoMorte, "Morti")
	if cat1 then
		self:_addCategory(cat1)
	elseif args.LuogoMorteLink then
		cat1 = "Morti " .. getAfonica(args.LuogoMorteLink) .. " " .. args.LuogoMorteLink
	--bruno	cat2 = "Morti ad " .. args.LuogoMorteLink
	--bruno	if titleExists("Categoria:" .. cat1) then
			self:_addCategory(cat1)
	--bruno	elseif titleExists("Categoria:" .. cat2) then
	--bruno		self:_addCategory(cat2)
	--bruno	end
	elseif args.LuogoMorte then
		cat1 = "Morti " .. getAfonica(args.LuogoMorte) .. " " .. args.LuogoMorte
	--bruno	cat2 = "Morti ad " .. args.LuogoMorte
	--bruno	if titleExists("Categoria:" .. cat1) then
			self:_addCategory(cat1)
	--bruno	elseif titleExists("Categoria:" .. cat2) then
	--bruno		self:_addCategory(cat2)
	--bruno	end
	end

	--bruno
	if args.StatoNascita then
		cat1 = "Nati in " .. args.StatoNascita
	
		self:_addCategory(cat1)
	end
end

-------------------------------------------------------------------------------
--                           classe Incipit
-------------------------------------------------------------------------------

local Incipit = {}

function Incipit:new()
	local self = {}

	setmetatable(self, { __index = Incipit })
	self.textTable = {}
	self:_addImmagine()
	self:_addNomeCognome()
	self:_addNascitaMorte()
	if args.Pseudonimo or args.PostCognomeVirgola or args.SecondoNome then
		self:_addText(",")
	end
	if args.FineIncipit then
		if self:_needSpace(args.FineIncipit) then
			self:_addText(' ')
		end
		self:_addText(args.FineIncipit)
	else
		self:_addAttivita()
	end
	if args.Punto ~= "no" then
		self:_addText((args.FineIncipit == "e" or
					  args.FineIncipit == "ed" or 
					  args.FineIncipit == ",") and
					  " " or ".")
	end

	return self
end

function Incipit:getIncipit()
	return table.concat(self.textTable)
end

-- Aggiunge testo alla risposta, svolge anche la funzione di concatenatore
function Incipit:_addText(...)
	local arg = {...}
	for _, val in ipairs(arg) do
		table.insert(self.textTable, val)
	end
end

-- Aggiunge un wlink alla risposta, se target è nil utilizza label come target.
-- labelPrefix, se presente, viene rimosso dalla label e anteposto al wlink.
function Incipit:_addWlink(target, label, labelPrefix)
	if target and label and labelPrefix then
		local count
		label, count = label:gsub("^" .. labelPrefix .. " ", "")
		if count == 1 then
			self:_addText(labelPrefix, " ")
		end
	end

	if target and label then
		self:_addText("[[", target, "|", label, "]]")
	else
		self:_addText("[[", target or label, "]]")
	end
end

-- bruno Aggiunge un testo alla risposta, se target è nil utilizza label come target.
-- labelPrefix, se presente, viene rimosso dalla label e anteposto al wlink.
function Incipit:_addexWlink(target, label, labelPrefix)
	if target and label and labelPrefix then
		local count
		label, count = label:gsub("^" .. labelPrefix .. " ", "")
		if count == 1 then
			self:_addText(labelPrefix, " ")
		end
	end

	if target and label then
		self:_addText(label, "")
	else
		self:_addText(target or label, "")
	end
end


-- Aggiunge una immagine alla risposta, size e caption sono opzionali
function Incipit:_addImage(name, size, caption)
	self:_addText("[[File:", name, "|thumb")

	if size then
		self:_addText("|", size, "px")
	end
	if caption then
		self:_addText("|", caption)
	end

	self:_addText("]]", "\n")
end


-- Ritorna true se text (AttivitàAltre, PostNazionalità, PostCognome e FineIncipit) necessita di uno spazio iniziale
function Incipit:_needSpace(text)
	return mw.ustring.match(mw.ustring.sub(text, 1, 1), "%w") ~= nil or
		   text:sub(1, 2) == "[[" or
		   text:sub(1, 1) == "(" or
		   text:sub(1, 1) == "'" or
		   mw.ustring.sub(text, 1, 1) == "–" or
		   text:sub(1, 5) == "<span"
end

function Incipit:_getArticleMan(attivita)
	local article
	if cfg.articoli_maschili["uno"][attivita] then
		article = "uno"
	elseif cfg.articoli_maschili["una"][attivita] then
		article = "una"
	else
		article = "un"
	end
	return article
end

function Incipit:_getArticleWoman(attivita)
	local article
	-- aggiunge anche uno spazio nel caso non usi l'apostrofo
	if cfg.articoli_femminili["un"][attivita] then
		article = "un "
	elseif attivita and attivita:match("^[aeiou]") then
		article = "un'"
	else
		article = "una "
	end
	return article
end

function Incipit:_addImmagine()
	local caption
	if args.Immagine then
		if args.Didascalia then
			caption = args.Didascalia
		else
			if args.CognomePrima and args.Nome and args.Cognome then
				caption = args.Cognome .. " " .. args.Nome
			else
				if args.Nome then
					caption = args.Nome
				end
				if args.Cognome then
					caption = (caption or "") .. " " .. args.Cognome
				end
			end
		end
		if args.Didascalia2 then
			caption = (caption or "") .. "<hr />" .. args.Didascalia2
		end
		self:_addImage(args.Immagine, args.DimImmagine, caption)
	elseif args.Didascalia2 then
		-- parentesi () extra per non ritornare anche il gsub.count
		self:_addText( (cfg.didascalia2:gsub("$1", args.Didascalia2)) )
	end
end

function Incipit:_addNomeCognome()
	if args.Titolo then
		self:_addText(args.Titolo, " ")
	end

	if args.Pseudonimo and currentTitleEquals(args.Pseudonimo) then
		self:_addText("'''", args.Pseudonimo, "'''")
		if args.PostPseudonimo then
			if self:_needSpace(args.PostPseudonimo) then
				self:_addText(" ")
			end
			self:_addText(args.PostPseudonimo)
		end
		self:_addText(", pseudonimo di ")
	end

	-- inizio grassetto
	self:_addText("'''")

	if args.CognomePrima and args.Nome and args.Cognome then
		self:_addText(args.Cognome, " ", args.Nome, mw.getCurrentFrame():expandTemplate{
			title = "Nota nome",
			args = { [1] = args.CognomePrima, [2] = args.Cognome }
			})
	else
		local no_space
		if args.Nome then
			self:_addText(args.Nome)
			-- niente spazio prima di Cognome se Nome termina con «d'»
			no_space = mw.ustring.match(args.Nome, " d'$") and ''
		end
		if args.Cognome then
			self:_addText(no_space or " ", args.Cognome)
		end
	end

	-- fine grassetto
	self:_addText("'''")

	if args.PostCognomeVirgola then
		self:_addText(", ", args.PostCognomeVirgola)
        elseif args.SecondoNome then
		self:_addText(", ", "all'anagrafe ", "'''", args.Nome, " ", args.SecondoNome, " ", args.Cognome, "'''")
	elseif args.PostCognome then
		if self:_needSpace(args.PostCognome) then
			self:_addText(" ")
		end
		self:_addText(args.PostCognome)
	end

	if args.Pseudonimo and not currentTitleEquals(args.Pseudonimo) then
		self:_addText(", ", (not args.Sesso or args.Sesso == "M") and "noto" or "nota",
					  " anche con lo pseudonimo di ", "'''", args.Pseudonimo, "'''")
		if args.PostPseudonimo then
			if self:_needSpace(args.PostPseudonimo) then
				self:_addText(" ")
			end
			self:_addText(args.PostPseudonimo)
		end
	end
end

function Incipit:_addNascitaMorte()
	-- si apre la parentesi
	self:_addText(" (")

	if args.PreData then
		 self:_addText(args.PreData, "; ")
	end

	if args.LuogoNascita then
		self:_addText(args.LuogoNascita)
		if args.LuogoNascitaAlt then
			self:_addText(" ", args.LuogoNascitaAlt)
		end
		self:_addText(", ")
	end

	if args.GiornoMeseNascita then
		if titleExists(args.GiornoMeseNascita) then
			self:_addWlink(string.lower(args.GiornoMeseNascita))
		else
			self:_addText(string.lower(args.GiornoMeseNascita))
		end
		self:_addText(" ")
	end

	if args.AnnoNascita then
		if titleExists(args.AnnoNascita) then
			self:_addWlink(args.AnnoNascita)
		else
			self:_addText(args.AnnoNascita)
		end
	else
		self:_addText("...")
	end

	if args.NoteNascita then
		self:_addText(args.NoteNascita)
	end

	if args.AnnoMorte then
		self:_addText(" – ")
		if args.LuogoMorte then
			self:_addText(args.LuogoMorte)
			if args.LuogoMorteAlt then
				self:_addText(" ", args.LuogoMorteAlt)
			end
			self:_addText(", ")
		end

		if args.GiornoMeseMorte then
			if titleExists(args.GiornoMeseMorte) then
				self:_addWlink(string.lower(args.GiornoMeseMorte))
			else
				self:_addText(string.lower(args.GiornoMeseMorte))
			end
			self:_addText(" ")
		end

		if args.AnnoMorte then
			if args.AnnoMorte == "?" then
				self:_addText("...")
			else
				if titleExists(args.AnnoMorte) then
					self:_addWlink(args.AnnoMorte)
				else
					self:_addText(args.AnnoMorte)
				end
			end
		end
	end

	if args.NoteMorte then
		self:_addText(args.NoteMorte)
	end

	-- si chiude la parentesi
	self:_addText(")")
end

function Incipit:_addAttivita()
	local link_attivita = mw.loadData("Modulo:Bio/Link attività")
	local link_nazionalita = mw.loadData("Modulo:Bio/Link nazionalità")	
	
	self:_addText(" ")
	if args["PreAttività"] then
		self:_addText(args["PreAttività"], " ")
	else
		self:_addText("è ")
		if args.AnnoMorte then
			self:_addText((not args.Sesso or args.Sesso == "M")
					 and "stato " or "stata ")
		end
		if not args.Sesso or args.Sesso == "M" then
			self:_addText(self:_getArticleMan(args["Attività"]), " ")
		else
			self:_addText(self:_getArticleWoman(args["Attività"]))
		end
	end

	self:_addWlink(link_attivita[args["Attività"]], args["Attività"] or "", "ex")

	if args["Attività2"] then
		if args["Attività3"] or args["AttivitàAltre"] then
			self:_addText(",")
		else
			self:_addText(" ", getEufonica(args["Attività2"]))
		end
		self:_addText(" ")
		self:_addWlink(link_attivita[args["Attività2"]], args["Attività2"], "ex")
	end

	if args["Attività3"] then
		if args["AttivitàAltre"] then
			self:_addText(",")
		else
			self:_addText(" ", getEufonica(args["Attività3"]))
		end
		self:_addText(" ")
		self:_addWlink(link_attivita[args["Attività3"]], args["Attività3"], "ex")
	end

	if args["AttivitàAltre"] then
		if self:_needSpace(args["AttivitàAltre"]) then
			self:_addText(" ")
		end
		self:_addText(args["AttivitàAltre"])
	end

	self:_addText(" ")
    self:_addexWlink(link_nazionalita[args["Nazionalità"]], args["Nazionalità"] or "")


	if args.Cittadinanza then
		self:_addText(" con cittadinanza ")
		self:_addWlink(link_nazionalita[args.Cittadinanza], args.Cittadinanza)
	end

	if args["NazionalitàNaturalizzato"] then
		self:_addText(" ")
		self:_addWlink("Naturalizzazione",
				  (not args.Sesso or args.Sesso == "M" or
				  (args.Sesso == "F" and self:_getArticleWoman(args["Attività"]) == "un ")) and
				  "naturalizzato" or "naturalizzata")
		self:_addText(" ")
		self:_addWlink(link_nazionalita[args["NazionalitàNaturalizzato"]], args["NazionalitàNaturalizzato"])
	end

	if args["PostNazionalità"] then
		if self:_needSpace(args["PostNazionalità"]) then
			self:_addText(" ")
		end
		self:_addText(args["PostNazionalità"])
	end
end

-------------------------------------------------------------------------------
--                                    API
-------------------------------------------------------------------------------

local p = {}

-- Funzione per {{#invoke:Bio|categorie}} utilizzato da Modulo:Bio/test
function p.categorie(frame)
	args = ArgsParser:new():parse(frame.args)
	local categories = CategoryManager:new():getCategories()
	return table.concat(errorTable) ..
		   (args.Debug and ( table.concat(categories, '<br />'):gsub('%[%[', '[[:') ) .. '<br />' or
		   table.concat(categories))
end

-- Funzione per il template per {{Bio}}
function p.main(frame)
	-- gli errori generano avvisi, ma non interrompono l'esecuzione,
	-- come avveniva nel vecchio template.
	args = ArgsParser:new():parse(frame:getParent().args)
	-- cerca alcuni parametri se mancanti su Wikidata
	if cfg.wikidata then
		checkWikidata()
	end
	local catTable = CategoryManager:new():getCategories()

	return table.concat(errorTable) ..
		   table.concat(catTable) ..
		   Incipit:new():getIncipit()
end

return p