Aller au contenu

Module:Debate

De Wikidébats, l'encyclopédie des débats et des arguments « pour » et « contre »

La documentation pour ce module peut être créée à Module:Debate/doc

--	Module:Debate (i18n FR/EN) — Optimisé
--	Objectif : rendu du modèle « Débat » (MediaWiki 1.43) fidèle au rendu existant

local p = {}

----------------------------------------------------------------------
--	Accès global au frame + VariablesLua (accélération sans changer le rendu)
----------------------------------------------------------------------

local mw			= mw
local F				= mw.getCurrentFrame()
local V				= (mw.ext and mw.ext.VariablesLua) or nil

--	Locaux (micro-optis)
local tostring		= tostring
local type			= type
local ipairs		= ipairs
local pairs			= pairs
local table_concat	= table.concat
local table_insert	= table.insert
local u_lower		= mw.ustring.lower
local t_trim		= mw.text.trim
local t_gsplit		= mw.text.gsplit
local t_nowiki		= mw.text.nowiki
local t_jsonEncode	= mw.text.jsonEncode
local t_jsonDecode	= mw.text.jsonDecode
local html_create	= mw.html.create

--	wrappers VariablesLua (no-op si extension absente)
local function vset(name, val)
	if V and type(V.vardefine) == "function" then
		pcall(V.vardefine, name, val ~= nil and tostring(val) or "")
	end
end
local function vget(name)
	if V and type(V.var) == "function" then
		local ok, res = pcall(V.var, name)
		if ok then return res end
	end
	return nil
end

----------------------------------------------------------------------
--	I18N : détection de langue + tables de traduction
----------------------------------------------------------------------

local function detectLang(args)
	local lang = args and (args["lang"] or args["language"]) or nil
	if lang and lang ~= "" then
		lang = u_lower(lang)
		if lang:match("^fr") then return "fr" end
		return "en"
	end
	local ok, contentLang = pcall(mw.getContentLanguage)
	if ok and contentLang and type(contentLang.getCode) == "function" then
		if contentLang:getCode() == "fr" then return "fr" end
	end
	return "en"
end

--	Tables i18n
local I18N = {
	files = {
		icon_wikidebats		= { fr = "Icone-Wikidebats.svg",		en = "Icon-Wikidebats.svg" },
		search				= { fr = "Loupe.svg",					en = "Loupe.svg" },
		wikipedia			= { fr = "Wikipedia.svg",				en = "Wikipedia.svg" },
		intro				= { fr = "Presentation-wikidebats.svg",	en = "Presentation-wikidebats.svg" },
		arg_pro				= { fr = "Argument-pour.svg",			en = "Argument-for.svg" },
		arg_con				= { fr = "Argument-contre.svg",			en = "Argument-against.svg" },
		arg_neutral			= { fr = "Ni-pour-ni-contre.svg",		en = "Neither-for-nor-against.svg" },
		biblio				= { fr = "Bibliographie.svg",			en = "Bibliography.svg" },
		webliography		= { fr = "Sitographie.svg",				en = "Webliography.svg" },
		videography			= { fr = "Videographie.svg",			en = "Videography.svg" },
		expand				= { fr = "Expand.svg",					en = "Expand.svg" },
		selftest			= { fr = "Autotest.svg",				en = "Selftest.svg" },
		changes				= { fr = "Debats-modifies.svg",			en = "Debates-changes.svg" },
		feedback			= { fr = "Vos-retours.svg",				en = "Your-feedback.svg" },
		related				= { fr = "Debats-connexes.svg",			en = "Related-debates.svg" },
		further				= { fr = "Pour-aller-plus-loin.svg",	en = "Further-reading.svg" }
	},
	templates = {
		page_desc			= { fr = "Description de page",				en = "Page description" },
		meta_warning		= { fr = "Méta bandeau d'avertissement",	en = "Meta warning banner" },
		banner_any			= { fr = "Bandeau x",						en = "Banner x" },
		banner_stub			= { fr = "Bandeau Ébauche",					en = "Banner Stub" },
		banner_building		= { fr = "Bandeau Débat en construction",	en = "Banner Debate under construction" },
		summary				= { fr = "Sommaire",						en = "Contents" },
		keyword_chip		= { fr = "Mot-clé de nuage",				en = "Tag cloud keyword" },
		debate_faq			= { fr = "FAQ de débat",					en = "Debate FAQ" }
	},
	forms = {
		edit_intro			= { fr = "Pour comprendre le débat",		en = "To understand the debate" },
		edit_wiki_seealso	= { fr = "Voir aussi sur Wikipédia",		en = "See also on Wikipedia" },
		edit_args_pro		= { fr = "Arguments pour",					en = "Arguments for" },
		edit_args_con		= { fr = "Arguments contre",				en = "Arguments against" },
		new_arg_title		= { fr = "Nouveau titre d'argument",		en = "New argument title" },
		new_arg_type_pro	= { fr = "Argument pour",					en = "Argument for" },
		new_arg_type_con	= { fr = "Argument contre",					en = "Argument against" },
		self_eval_home		= { fr = "Autoévaluation (accueil)",		en = "Self-evaluation (home)" },
		edit_keywords		= { fr = "Mots-clés de débat",				en = "Debate keywords" },
		edit_related		= { fr = "Débats connexes",					en = "Related debates" },
		edit_subject		= { fr = "Sujet de débat",					en = "Debate subject" },
		edit_sections		= { fr = "Rubriques de débat",				en = "Debate sections" },
		edit_interlanguage	= { fr = "Interlangue de débat",			en = "Debate interlanguage" },
		biblio_pro			= { fr = "Bibliographie pour",				en = "Bibliography (for)" },
		biblio_con			= { fr = "Bibliographie contre",			en = "Bibliography (against)" },
		biblio_neutral		= { fr = "Bibliographie ni pour ni contre",	en = "Bibliography (neutral)" },
		webliography_pro	= { fr = "Sitographie pour",				en = "Webliography (for)" },
		webliography_con	= { fr = "Sitographie contre",				en = "Webliography (against)" },
		webliography_neutral= { fr = "Sitographie ni pour ni contre",	en = "Webliography (neutral)" },
		videography_pro		= { fr = "Vidéographie pour",				en = "Videography (for)" },
		videography_con		= { fr = "Vidéographie contre",			en = "Videography (against)" },
		videography_neutral	= { fr = "Vidéographie ni pour ni contre",	en = "Videography (neutral)" }
	},
	progress = {
		stub			= { fr = "Ébauche",					en = "Stub" },
		building		= { fr = "Débat en construction",	en = "Debate under construction" },
		constructed		= { fr = "Débat construit",			en = "Constructed debate" }
	},
	categories = {
		debates			= { fr = "Débats",							en = "Debates" },
		progress_missing= { fr = "Débats sans niveau d'avancement",	en = "Debates without progress level" },
		constructed		= { fr = "Débats construits",				en = "Constructed debates" },
		no_intro		= { fr = "Débats sans introduction",		en = "Debates without introduction" },
		no_wikipedia	= { fr = "Débats sans article Wikipédia",	en = "Debates without Wikipedia link" },
		no_pro			= { fr = "Débats sans argument pour",		en = "Debates without pro argument" },
		no_con			= { fr = "Débats sans argument contre",		en = "Debates without con argument" },
		no_keywords		= { fr = "Débats sans mot-clé",				en = "Debates without keywords" },
		no_related		= { fr = "Débats sans débat connexe",		en = "Debates without related debate" },
		no_sections		= { fr = "Débats sans rubrique",			en = "Debates without section" },
		no_topic		= { fr = "Débats sans sujet",				en = "Debates without topic" },
		no_complete_topic= { fr = "Débats sans sujet complet",		en = "Debates without complete topic" }
	},
	text = {
		main_banner_title = {
			fr = 'Cet article vise à exposer tous les arguments « pour » et « contre » du débat « <span style="font-weight: normal;">%s</span> », pour permettre à chacun de se forger une opinion critique et éclairée.',
			en = 'This article aims to present all the arguments “for” and “against” in the debate “<span style="font-weight: normal;">%s</span>”, to help everyone form a critical, informed opinion.'
		},
		main_banner_alt = {
			fr = "Balance de la raison qui pèse les arguments pour et contre",
			en = "Scale of reason weighing arguments for and against"
		},
		learn_more = { fr = "En savoir plus : ", en = "Learn more: " },
		understand_debate = { fr = "Pour comprendre le débat", en = "To understand the debate" },
		edit = { fr = "modifier", en = "edit" },
		edit_intro_tooltip = { fr = "Modifier l'introduction du débat", en = "Edit the debate introduction" },
		no_intro = { fr = "Aucune introduction n'a été entrée pour le moment.", en = "No introduction has been provided yet." },
		wikipedia_seealso = { fr = "Voir aussi : %s sur Wikipédia.", en = "See also: %s on Wikipedia." },
		no_wikipedia = { fr = "Aucun lien vers une page Wikipédia n'a été entré pour le moment.", en = "No link to a Wikipedia page has been provided yet." },
		edit_wikipedia_tooltip = { fr = "Modifier les articles Wikipédia du bandeau", en = "Edit the Wikipedia articles in the banner" },
		title_args_pro = { fr = "Arguments « pour »", en = "Arguments “for”" },
		title_args_con = { fr = "Arguments « contre »", en = "Arguments “against”" },
		edit_args_tooltip_pro = { fr = "Modifier la liste des arguments « pour » ci-dessous", en = "Edit the list of “for” arguments below" },
		edit_args_tooltip_con = { fr = "Modifier la liste des arguments « contre » ci-dessous", en = "Edit the list of “against” arguments below" },
		faq_q_for = { fr = "Quels sont les arguments pour%s ?", en = "What are the arguments for%s?" },
		faq_q_against = { fr = "Quels sont les arguments contre%s ?", en = "What are the arguments against%s?" },
		no_pro_args = { fr = "Aucun argument « pour » n'a été entré pour le moment.", en = "No “for” argument has been added yet." },
		no_con_args = { fr = "Aucun argument « contre » n'a été entré pour le moment.", en = "No “against” argument has been added yet." },
		add_arg_pro = { fr = "Ajouter un argument « pour »", en = "Add a “for” argument" },
		add_arg_con = { fr = "Ajouter un argument « contre »", en = "Add an “against” argument" },
		add_arg_tooltip = { fr = "Ajouter un nouvel argument « %s » au débat : %s", en = "Add a new “%s” argument to the debate: %s" },
		further_reading = { fr = "Pour aller plus loin", en = "Further reading" },
		bibliography = { fr = "Bibliographie", en = "Bibliography" },
		webliography = { fr = "Sitographie", en = "Webliography" },
		videography = { fr = "Vidéographie", en = "Videography" },
		side_for = { fr = "Côté « pour »", en = "“For” side" },
		side_against = { fr = "Côté « contre »", en = "“Against” side" },
		side_neutral = { fr = "Ni « pour » ni « contre »", en = "Neither “for” nor “against”" },
		edit_section = { fr = "Modifier cette rubrique", en = "Edit this section" },
		no_ref = { fr = "Aucune référence n'a été entrée pour le moment.", en = "No references have been added yet." },
		keywords_label = { fr = "'''Mots-clés''' : ", en = "'''Keywords''' : " },
		none = { fr = "''aucun''", en = "''none''" },
		edit_keywords_tooltip = { fr = "Modifier les mots-clés thématiques décrivant ce débat", en = "Edit the thematic keywords describing this debate" },
		related_debates = { fr = "Débats connexes", en = "Related debates" },
		edit_related_tooltip = { fr = "Modifier la liste des débats connexes", en = "Edit the list of related debates" },
		no_related_msg = { fr = "Aucun débat connexe n'a été entré pour le moment.", en = "No related debate has been added yet." },
		self_eval_title = { fr = "Testez-vous", en = "Self-assessment" },
		self_eval_blurb = {
			fr = "'''Êtes-vous plutôt « pour » ou « contre » ? Autoévaluez-vous !'''<br /><br />Vous pouvez parcourir le contenu de ce débat en répondant, pour chaque argument, à deux questions:<ol><li> Cet argument est-il bien fondé ?</li><li> Cet argument est-il important pour vous ?</li></ol>Vous obtiendrez votre pourcentage de « pour » ou de « contre » et une analyse de votre opinion.",
			en = "'''Are you more “for” or “against”? Assess yourself!'''<br /><br />You can browse this debate by answering, for each argument, two questions:<ol><li> Is this argument well-founded?</li><li> Is this argument important to you?</li></ol>You’ll get your “for/against” percentage and an analysis of your stance."
		},
		self_eval_start = { fr = "Commencer", en = "Start" },
		self_eval_tooltip = { fr = "Commencer l'autoévaluation", en = "Start the self-assessment" },
		latest_changes = { fr = "Dernières modifications", en = "Latest changes" },
		show_latest_changes = { fr = "Afficher les dernières modifications apportées dans le débat", en = "Show the latest changes made to the debate" },
		feedback = { fr = "Vos retours", en = "Your feedback" },
		question_faq_wrapper = { fr = '<div class="question-FAQ">%s</div>', en = '<div class="question-FAQ">%s</div>' },
		breadcrumb_root = { fr = "Débats", en = "Debates" },
		shortdesc_plain = { fr = "Arguments pour et contre", en = "Arguments for and against" },
		shortdesc_topic = { fr = "Arguments pour et contre %s", en = "Arguments for and against %s" },
		edit_subject_tooltip = { fr = "Modifier le sujet du débat", en = "Edit the debate subject" },
		edit_sections_tooltip = { fr = "Modifier les rubriques du débat", en = "Edit the debate sections" },
		edit_interlanguage_tooltip = { fr = "Modifier les liens interlangues du débat", en = "Edit the interlanguage links for the debate" },
		arg_map_for = { fr = "POUR", en = "FOR" },
		arg_map_against = { fr = "CONTRE", en = "AGAINST" },
		arg_map_no_for = { fr = "<span>Aucun argument « pour » n'a été entré.</span>", en = "<span>No “for” argument has been added yet.</span>" },
		arg_map_no_against = { fr = "<span>Aucun argument « contre » n'a été entré.</span>", en = "<span>No “against” argument has been added yet.</span>" },
		rename_link_text = { fr = "renommer", en = "rename" },
		rename_special_page = {
			fr = "Special:Renommage/%s",
			en = "Special:MovePage/%s"
		},
		self_eval_ns = { fr = "Autoévaluation", en = "Self-evaluation" },
		self_eval_field_debate = { fr = "débat", en = "debate" },
		self_eval_field_arg_type = { fr = "type-argument", en = "arg-type" },
		self_eval_field_position = { fr = "position", en = "position" },
		arg_new_title_field_base = { fr = "Nouveau titre d'argument", en = "New argument title" },
		principles_page = { fr = "Wikidébats:Principes fondateurs", en = "Wikidebates:Founding principles" },
		principles_label = { fr = "Principes fondateurs", en = "Founding principles" }
	},
	seo = {
		site_name		= { fr = "Wikidébats", en = "Wikidebates" },
		twitter_site	= { fr = "@Wikidébats", en = "@Wikidebates" },
		title_prefix	= {
			fr = "Débat : %s – Wikidébats, l'encyclopédie des arguments « pour » et « contre »",
			en = "Debate: %s – Wikidebates, the encyclopedia of “for and against” arguments"
		},
		title_mode		= { fr = "Débat : %s", en = "Debate: %s" },
		desc_prefix		= {
			fr = "Arguments pour et contre %s : argumentaire pro et anti, défense et critiques, justifications et objections, preuves et réfutations, partisans et opposants. Mots-clés : %s.",
			en = "Arguments for and against %s: pro and con arguments, defenses and critiques, justifications and objections, evidence and refutations, supporters and opponents. Keywords: %s."
		}
	},
	props = {
		debate_name		= { fr = "Nom de débat",			en = "Debate name" },
		debate_number	= { fr = "Numéro de débat",			en = "Debate number" },
		debate_parent	= { fr = "Débat parent",			en = "Parent debate" },
		breadcrumb		= { fr = "Fil d'Ariane",			en = "Breadcrumb" },
		keyword			= { fr = "Mot-clé",					en = "Keyword" },
		arg_map			= { fr = "Carte des arguments",		en = "Argument map" },
		page_params		= { fr = "Paramètres de la page",	en = "Page parameters" },
		debate_subject	= { fr = "Sujet de débat",			en = "Debate subject" },
		subject_title	= { fr = "Sujet et titre de débat",	en = "Subject and debate title" }
	}
}

local function L(lang, section, key)
	local sec = I18N[section]
	if sec then
		local t = sec[key]
		if t then return t[lang] or t.fr or t.en or key end
	end
	if section == "text" then
		local x = I18N.text[key]
		if x then return x[lang] or x.fr or x.en or key end
	end
	return key
end

--	Paramétrage bilingue des modèles (duplique chaque clef fr|en => valeur)
local function biParams(kv)
	local out = {}
	for k, v in pairs(kv or {}) do
		local a, b = k:match("^([^|]+)|([^|]+)$")
		if a and b then
			out[a], out[b] = v, v
		else
			out[k] = v
		end
	end
	return out
end

local function isProgress(lang, val, key)
	if not val or val == "" then return false end
	local p = I18N.progress and I18N.progress[key]
	if not p then return false end
	return val == p[lang] or val == p.fr or val == p.en
end

----------------------------------------------------------------------
--	Helpers
----------------------------------------------------------------------

local function getArgs(frame)
	local parent = frame:getParent()
	local A = {}
	if parent and parent.args then
		for k, v in pairs(parent.args) do
			if type(v) == "string" and v ~= "" then A[k] = v end
		end
	end
	if frame.args then
		for k, v in pairs(frame.args) do
			if type(v) == "string" and v ~= "" then A[k] = v end
		end
	end
	return A
end

local function splitCSV(s, sep)
	if not s or s == "" then return {} end
	sep = sep or ","
	local out = {}
	for part in t_gsplit(s, sep, true) do
		part = t_trim(part)
		if part ~= "" then table_insert(out, part) end
	end
	return out
end

local function expand(frame, title, params)
	local ok, res = pcall(function()
		return frame:expandTemplate{ title = title, args = params or {} }
	end)
	return ok and (res or "") or ""
end

local function hasSMW()
	return type(mw.smw) == "table" and type(mw.smw.set) == "function"
end

local function smwSetSafe(props)
	if not mw.smw or type(mw.smw.set) ~= "function" then
		vset("WD_SMW_STATUS", "no-mw.smw")
		return
	end
	local ok, err = pcall(function() mw.smw.set(props) end)
	if not ok then
		vset("WD_SMW_STATUS", "error: " .. tostring(err))
	else
		vset("WD_SMW_STATUS", "ok")
	end
end

local function smwAskSafe(q)
	if not hasSMW() then return nil end
	local ok, res = pcall(function() return mw.smw.ask(q) end)
	return ok and res or nil
end

local function seoSetSafe(params)
	if type(mw.ext) == "table" and type(mw.ext.seo) == "table" and type(mw.ext.seo.set) == "function" then
		pcall(function() mw.ext.seo.set(params) end)
	end
end

local function makeJsonLD(t)
	return t_jsonEncode(t)
end

--	{{#tag}} utilitaire
local function tag(name, content, attrs)
	local parts = {}
	if attrs then
		for k, v in pairs(attrs) do
			table_insert(parts, string.format("%s=%s", k, v))
		end
	end
	local src
	if #parts > 0 then
		src = string.format("{{#tag:%s|%s|%s}}", name, content or "", table_concat(parts, "|"))
	else
		src = string.format("{{#tag:%s|%s}}", name, content or "")
	end
	return F:preprocess(src)
end

local function pf_escape(v)
	if not v then return "" end
	v = tostring(v)
	v = v:gsub("|", "&#124;"):gsub("=", "&#61;")
	return v
end

local function pf_formlink(opts)
	local parts = { "#formlink:" }
	table_insert(parts, string.format("form=%s", pf_escape(opts.form or "")))
	if opts.target and opts.target ~= "" then
		if tostring(opts.target):find("{{", 1, true) then
			table_insert(parts, "target=" .. opts.target)
		else
			table_insert(parts, string.format("target=%s", pf_escape(opts.target)))
		end
	end
	if opts.link_text and opts.link_text ~= "" then
		table_insert(parts, string.format("link text=%s", pf_escape(opts.link_text)))
	end
	if opts.tooltip and opts.tooltip ~= "" then
		table_insert(parts, string.format("tooltip=%s", pf_escape(opts.tooltip)))
	end
	table_insert(parts, string.format("link type=%s", pf_escape(opts.link_type or "link")))
	if opts.query_string and opts.query_string ~= "" then
		table_insert(parts, "query string=" .. opts.query_string)
	end
	return F:preprocess("{{" .. table_concat(parts, "|") .. "}}")
end

local function pf_queryformlink(opts)
	local parts = { "#queryformlink:" }
	table_insert(parts, string.format("form=%s", pf_escape(opts.form or "")))
	if opts.link_text and opts.link_text ~= "" then
		table_insert(parts, string.format("link text=%s", pf_escape(opts.link_text)))
	end
	if opts.tooltip and opts.tooltip ~= "" then
		table_insert(parts, string.format("tooltip=%s", pf_escape(opts.tooltip)))
	end
	table_insert(parts, string.format("link type=%s", pf_escape(opts.link_type or "post button")))
	if opts.target and opts.target ~= "" then
		if tostring(opts.target):find("{{", 1, true) then
			table_insert(parts, "target=" .. opts.target)
		else
			table_insert(parts, string.format("target=%s", pf_escape(opts.target)))
		end
	end
	if opts.query_string and opts.query_string ~= "" then
		table_insert(parts, "query string=" .. opts.query_string)
	end
	return F:preprocess("{{" .. table_concat(parts, "|") .. "}}")
end

local function joinArticles(items, lang)
	local n = #items
	if n == 0 then return "" end
	if n == 1 then return items[1] end
	if n == 2 then
		return (lang == "en") and (items[1] .. " and " .. items[2]) or (items[1] .. " et " .. items[2])
	end
	local last = items[n]
	local first = {}
	for i = 1, n - 1 do first[i] = items[i] end
	return (lang == "en")
		and (table_concat(first, ", ") .. " and " .. last)
		or  (table_concat(first, ", ") .. " et " .. last)
end

--	Évite qu'un <li> avale le <h2> suivant
local function listBreaker()
	return "\n\n<!--__WD_LIST_BREAK__-->"
end

local function push(t, s)
	t[#t + 1] = s
end

----------------------------------------------------------------------
--	Bloc d’en-tête / variables de page
----------------------------------------------------------------------

local function computePageVars()
	local cached = vget("WD_PageVars_JSON")
	if cached and cached ~= "" then
		local ok, pv = pcall(t_jsonDecode, cached)
		if ok and type(pv) == "table" then
			pv.title = mw.title.getCurrentTitle()
			return pv
		end
	end

	local title		= mw.title.getCurrentTitle()
	local rawTitle	= title.prefixedText:gsub("&#39;", "'")
	local encoded	= mw.uri.encode(rawTitle, "WIKI")
	local uriEncoded= F:callParserFunction('urlencode', rawTitle)
	local pageUrl	= tostring(mw.uri.fullUrl(title.prefixedText))
	local pageId	= tostring(title.id or "")

	local pv = {
		title = title,
		rawTitle = rawTitle,
		encoded = encoded,
		uriEncoded = uriEncoded,
		pageUrl = pageUrl,
		pageId = pageId
	}

	vset("WD_PageVars_JSON", t_jsonEncode({
		rawTitle = rawTitle,
		encoded = encoded,
		uriEncoded = uriEncoded,
		pageUrl = pageUrl,
		pageId = pageId
	}))

	return pv
end

local function emitLegacyVarDefines(pv)
	local function esc(s)
		s = tostring(s or "")
		s = s:gsub("|", "&#124;"):gsub("=", "&#61;")
		return s
	end
	vset("Titre-page", pv.rawTitle)
	vset("Titre-page-encode", pv.encoded)
	vset("URL-page", pv.pageUrl)
	vset("ID-page", pv.pageId)

	local wt =
		'<span style="display:none;">'
		.. '{{#vardefine:Titre-page|' .. esc(pv.rawTitle) .. '}}'
		.. '{{#vardefine:Titre-page-encode|' .. esc(pv.encoded) .. '}}'
		.. '{{#vardefine:URL-page|' .. esc(pv.pageUrl) .. '}}'
		.. '{{#vardefine:ID-page|' .. esc(pv.pageId) .. '}}'
		.. '</span>'
	return F:preprocess(wt)
end

local function defineLegacyVars(_frame, _pv)
	return true
end

----------------------------------------------------------------------
--	Bandeau principal + JSON-LD breadcrumb (i18n)
----------------------------------------------------------------------

local function renderMainBanner(frame, pv, lang)
	local titre = string.format(L(lang, "text", "main_banner_title"), t_nowiki(pv.rawTitle))
	local principlesPage  = L(lang, "text", "principles_page")
	local principlesLabel = L(lang, "text", "principles_label")

	local desc = expand(frame, L(lang, "templates", "page_desc"), biParams({
		["contenu|content"]	= "[[" .. principlesPage .. "|" .. principlesLabel .. "]]",
		["page|page"]		= principlesPage
	}))

	return expand(frame, L(lang, "templates", "meta_warning"), biParams({
		["couleur|color"]	= (lang == "fr") and "vert" or "green",
		["icône|icon"]		= L(lang, "files", "icon_wikidebats"),
		["taille|size"]		= "48px",
		["alt|alt"]			= L(lang, "text", "main_banner_alt"),
		["titre|title"]		= titre,
		["texte|text"]		=
			'<div class="bandeau-section" style="margin: 0.5em 0 0.35em 0;">'
			.. '<table style="background-color:transparent"><tr>'
			.. '<td><div style="text-align:center; margin-right: 0.5em;">[[File: ' .. L(lang, "files", "search") .. ' | 13px | lien= | class=mw-no-invert]]</div></td>'
			.. '<td>' .. L(lang, "text", "learn_more") .. desc .. '</td>'
			.. '</tr></table></div>'
	}))
end

local function renderBreadcrumbJSONLD(pv, lang)
	local rootName = L(lang, "text", "breadcrumb_root")
	local rootUrl = (lang == "fr")
		and "https://fr.wikidebates.org/wiki/Cat%C3%A9gorie:D%C3%A9bats"
		or  "https://en.wikidebates.org/wiki/Category:Debates"
	local data = {
		["@context"] = "http://schema.org",
		["@type"] = "BreadcrumbList",
		itemListElement = {
			{ ["@type"] = "ListItem", position = 1, item = { ["@id"] = rootUrl, name = rootName } },
			{ ["@type"] = "ListItem", position = 2, item = { ["@id"] = pv.pageUrl, name = pv.rawTitle } }
		}
	}
	local json = makeJsonLD(data)
	return tag("htmltag", json, { tagname = "script", type = "application/ld+json", ["class"] = "navigation-not-searchable" })
end

----------------------------------------------------------------------
--	Avancement & avertissements (i18n)
----------------------------------------------------------------------

local function renderAvancement(args, cats, lang)
	local av = args["progress"]
	local out = {}

	local function renderBandeaux(list)
		local html = {}
		for _, x in ipairs(splitCSV(list, ",")) do
			table_insert(html, expand(F, L(lang, "templates", "banner_any"), { [1] = x }))
		end
		return table_concat(html)
	end

	if args["title-warnings"] then
		table_insert(out, renderBandeaux(args["title-warnings"]))
	end

	if isProgress(lang, av, "stub") then
		table_insert(out, expand(F, L(lang, "templates", "banner_stub")))
	elseif isProgress(lang, av, "building") then
		table_insert(out, expand(F, L(lang, "templates", "banner_building")))
	elseif isProgress(lang, av, "constructed") then
		table_insert(cats, "[[Category:" .. L(lang, "categories", "constructed") .. "]]")
	else
		table_insert(cats, "[[Category:" .. L(lang, "categories", "progress_missing") .. "]]")
	end

	if args["debate-warnings"] then
		table_insert(out, renderBandeaux(args["debate-warnings"]))
	end

	return table_concat(out)
end

----------------------------------------------------------------------
--	Carte des arguments (en-tête) — depuis les #var déjà générées
----------------------------------------------------------------------

local function renderArgumentMapTop(lang)
	local pour		= t_trim(F:preprocess("{{#var: Carte-arguments-pour}}") or "")
	local contre	= t_trim(F:preprocess("{{#var: Carte-arguments-contre}}") or "")

	local labelPour		= L(lang, "text", "arg_map_for")
	local labelContre	= L(lang, "text", "arg_map_against")
	local msgPour		= L(lang, "text", "arg_map_no_for")
	local msgContre		= L(lang, "text", "arg_map_no_against")

	local tbl = html_create("table")
		:attr("id", "Argument_map")
		:css("color", "var(--color-emphasized,#101418)")
		:css("background-color", "var(--background-color-neutral-subtle, #f8f9fa)")
		:css("width", "100%")
		:css("margin-top", "1.5em !important")
		:attr("align", "center")

	local tr  = html_create("tr")
	local tdP = html_create("td"):css("width", "50%"):css("vertical-align", "top"):css("padding", "5px 1.5%")
	local tdC = html_create("td"):css("width", "50%"):css("vertical-align", "top"):css("padding", "5px 0")

	tdP:wikitext('<div style="font-size:105%; font-weight:bold;">' .. labelPour .. '</div>')
	tdP:wikitext(pour ~= "" and pour or '<div style="font-size: 95%;" class="navigation-not-searchable">' .. msgPour .. '</div>')

	tdC:wikitext('<div style="font-size:105%; font-weight:bold;">' .. labelContre .. '</div>')
	tdC:wikitext(contre ~= "" and contre or '<div style="font-size: 95%;" class="navigation-not-searchable">' .. msgContre .. '</div>')

	tr:node(tdP):node(tdC)
	tbl:node(tr)
	return tostring(tbl)
end

----------------------------------------------------------------------
--	Sections « Introduction », « Arguments », « Références », etc. (i18n)
----------------------------------------------------------------------

local function renderIntroduction(args, pv, lang)
	local h2 =
		'<h2 id="Introduction" class="section-modifiable">'
		.. '<span style="margin-right: 0.5em;">[[File: ' .. L(lang, "files", "intro") .. ' | 22px | link= | alt=' .. L(lang, "text", "understand_debate") .. ']]</span>'
		.. L(lang, "text", "understand_debate")
		.. '<span class="modifier-section navigation-not-searchable noprint">'
		.. pf_formlink{
			form = L(lang, "forms", "edit_intro"),
			target = pv.encoded,
			link_text = L(lang, "text", "edit"),
			link_type = "link",
			tooltip = L(lang, "text", "edit_intro_tooltip")
		}
		.. '</span></h2>'

	local body
	if args["introduction"] and t_trim(args["introduction"]) ~= "" then
		body = args["introduction"]
	else
		local cat = "[[Category:" .. L(lang, "categories", "no_intro") .. "]]"
		body = '<div class="aucun-contenu navigation-not-searchable" style="margin-bottom: 1em;">' .. L(lang, "text", "no_intro") .. '</div>' .. cat
	end
	--	Corrige multi-retour de gsub (ne renvoyer qu'une valeur)
	local res = (h2 .. body):gsub("%s*$", "")
	return res
end

local function renderWikipediaLinks(args, pv, lang)
	local has = (args["wikipedia-articles"] and t_trim(args["wikipedia-articles"]) ~= "")
	local wrap = html_create("div"):addClass("bandeau-section navigation-not-searchable")
	local tbl  = html_create("table"):css("background-color", "transparent")
	local tr   = html_create("tr")
	local tdIcon = html_create("td"):wikitext('<div style="text-align:center; margin-right: 0.5em;">[[File: ' .. L(lang, "files", "wikipedia") .. ' | 18px | link=]]</div>')
	tr:node(tdIcon)
	local tdMain = html_create("td"):addClass("width-100"):css("line-height", "1.5")

	if has then
		local items = splitCSV(args["wikipedia-articles"], "<ARTICLE>")
		tdMain:wikitext(string.format(L(lang, "text", "wikipedia_seealso"), joinArticles(items, lang)))
	else
		tdMain:addClass("aucun-contenu"):cssText("line-height: 1.5; font-style: italic;"):wikitext(L(lang, "text", "no_wikipedia"))
	end

	local tdBtn = html_create("td")
	tdBtn:wikitext(
		'<span class="modifier-section navigation-not-searchable noprint">'
		.. pf_formlink{
			form = L(lang, "forms", "edit_wiki_seealso"),
			target = pv.encoded,
			link_text = L(lang, "text", "edit"),
			link_type = "link",
			tooltip = L(lang, "text", "edit_wikipedia_tooltip")
		}
		.. '</span>'
	)

	tr:node(tdMain):node(tdBtn)
	tbl:node(tr)
	wrap:node(tbl)

	if not has then
		return tostring(wrap) .. "[[Category:" .. L(lang, "categories", "no_wikipedia") .. "]]"
	end
	return tostring(wrap)
end

local function renderQuestionsFAQ(sujetComplet, sens, lang)
	local suffix = (sujetComplet and sujetComplet ~= "") and (" " .. sujetComplet) or ""
	local isAgainst = (sens == "contre" or sens == "against")
	local q = isAgainst and string.format(L(lang, "text", "faq_q_against"), suffix)
		or string.format(L(lang, "text", "faq_q_for"), suffix)
	return string.format(L(lang, "text", "question_faq_wrapper"), q)
end

local function renderArgsList(args, pv, lang, sens)
	local isAgainst = (sens == "contre" or sens == "against")
	local key = isAgainst and "con-arguments" or "pro-arguments"
	local hIconContre = '[[File: ' .. L(lang, "files", "arg_con") .. ' | 22px | link= | alt=' .. L(lang, "text", "title_args_con") .. ' | class=mw-no-invert]]'
	local hIconPour  = '[[File: ' .. L(lang, "files", "arg_pro") .. ' | 22px | link= | alt=' .. L(lang, "text", "title_args_pro") .. ']]'
	local title = isAgainst and L(lang, "text", "title_args_con") or L(lang, "text", "title_args_pro")

	local btnForm = pf_formlink{
		form = isAgainst and L(lang, "forms", "edit_args_con") or L(lang, "forms", "edit_args_pro"),
		target = pv.encoded,
		link_text = L(lang, "text", "edit"),
		link_type = "link",
		tooltip = isAgainst and L(lang, "text", "edit_args_tooltip_con") or L(lang, "text", "edit_args_tooltip_pro")
	}

	local out = {}
	push(out, listBreaker())

	if isAgainst then
		push(out, '<h2 id="Cons" class="section-modifiable"><span style="margin-right: 0.5em;">' .. hIconContre .. '</span>' .. title .. '<span class="modifier-section navigation-not-searchable noprint">' .. btnForm .. '</span></h2>')
	else
		push(out, '<h2 id="Pros" class="section-modifiable"><span style="margin-right: 0.5em;">' .. hIconPour .. '</span>' .. title .. '<span class="modifier-section navigation-not-searchable noprint">' .. btnForm .. '</span></h2>')
	end

	push(out, renderQuestionsFAQ(args["complete-topic"], sens, lang))

	local ul = html_create("ul"):addClass("liste-arguments")
	if isAgainst then ul:cssText("list-style: none;") end

	if args[key] and t_trim(args[key]) ~= "" then
		ul:wikitext(args[key])
	else
		local cat = isAgainst and ("[[Category:" .. L(lang, "categories", "no_con") .. "]]") or ("[[Category:" .. L(lang, "categories", "no_pro") .. "]]")
		local msg = isAgainst and L(lang, "text", "no_con_args") or L(lang, "text", "no_pro_args")
		ul:wikitext('<div class="aucun-contenu navigation-not-searchable">' .. msg .. '</div>' .. cat)
	end
	push(out, tostring(ul))

	local label = isAgainst and L(lang, "text", "add_arg_con") or L(lang, "text", "add_arg_pro")
	local tooltip = string.format(L(lang, "text", "add_arg_tooltip"), (isAgainst and ((lang == "en") and "against" or "contre")) or ((lang == "en") and "for" or "pour"), pv.rawTitle)
	local baseField = L(lang, "text", "arg_new_title_field_base")
	local qstring = baseField .. "[type]="
		.. pf_escape(isAgainst and L(lang, "forms", "new_arg_type_con") or L(lang, "forms", "new_arg_type_pro"))
		.. "&" .. baseField .. "[ID]={{#var: ID-page}}&_run"

	push(out,
		'<div class="bouton-ajouter navigation-not-searchable noprint">'
		.. pf_queryformlink{
			form = L(lang, "forms", "new_arg_title"),
			link_text = label,
			link_type = "post button",
			tooltip = tooltip,
			query_string = qstring
		}
		.. '</div>'
	)

	return table_concat(out)
end

local function renderRefBlock(pv, lang, titleIcon, titleText, formKey, value)
	local out = {}
	table_insert(out,
		'<div class="titre-references navigation-not-searchable">'
		.. titleIcon .. titleText
		.. '<span class="modifier-rubrique flottant-droite navigation-not-searchable noprint">'
		.. pf_formlink{
			form = L(lang, "forms", formKey),
			target = pv.encoded,
			link_text = L(lang, "text", "edit"),
			link_type = "link",
			tooltip = L(lang, "text", "edit_section")
		}
		.. '</span></div>'
	)
	if value and t_trim(value) ~= "" then
		table_insert(out, "<ul>" .. value .. "</ul>")
	else
		table_insert(out, '<div class="aucune-reference navigation-not-searchable">' .. L(lang, "text", "no_ref") .. '</div>')
	end
	return table_concat(out)
end

local function renderReferences(args, pv, lang)
	local wrap = {}

	table_insert(wrap, '<h2 id="References"><span style="margin-right: 0.5em;">[[File: ' .. L(lang, "files", "further") .. ' | 20px | link= | alt=' .. L(lang, "text", "further_reading") .. ']]</span>' .. L(lang, "text", "further_reading") .. '</h2>')

	do
		local h3 = '<h3 id="Bibliography" class="fr-collapsible-toggle">[[File: ' .. L(lang, "files", "expand") .. ' | 13px | link= | alt= | class=fleche-deroulante]]'
			.. '[[File: ' .. L(lang, "files", "biblio") .. ' | 13px | middle | link= | alt=' .. L(lang, "text", "bibliography") .. ' | class=sub pictogramme-h3]]'
			.. L(lang, "text", "bibliography") .. '</h3>'
		local content = {}
		table_insert(content, renderRefBlock(pv, lang, '[[File: ' .. L(lang, "files", "arg_pro") .. ' | 17px | link= | alt=For | class=pictogramme-h3 mw-no-invert]]', L(lang, "text", "side_for"), "biblio_pro", args["pro-bibliography"]))
		table_insert(content, renderRefBlock(pv, lang, '[[File: ' .. L(lang, "files", "arg_con") .. ' | 17px | link= | alt=Against | class=pictogramme-h3 mw-no-invert]]', L(lang, "text", "side_against"), "biblio_con", args["con-bibliography"]))
		table_insert(content, renderRefBlock(pv, lang, '[[File: ' .. L(lang, "files", "arg_neutral") .. ' | 17px | text-bottom | link= | alt=Neutral | class=pictogramme-h3 mw-no-invert]]', L(lang, "text", "side_neutral"), "biblio_neutral", args["bibliography"]))
		table_insert(wrap, '<div class="fr-collapsible fr-collapsed">' .. h3 .. '<div class="fr-collapsible-content contenu-references">' .. table_concat(content) .. '</div></div>')
	end

	do
		local h3 = '<h3 id="Webliography" class="fr-collapsible-toggle">[[File: ' .. L(lang, "files", "expand") .. ' | 13px | link= | alt= | class=fleche-deroulante]]'
			.. '[[File: ' .. L(lang, "files", "webliography") .. ' | 15px | link= | alt=' .. L(lang, "text", "webliography") .. ' | class=pictogramme-h3]]'
			.. L(lang, "text", "webliography") .. '</h3>'
		local content = {}
		table_insert(content, renderRefBlock(pv, lang, '[[File: ' .. L(lang, "files", "arg_pro") .. ' | 17px | link= | alt=For | class=pictogramme-h3 mw-no-invert]]', L(lang, "text", "side_for"), "webliography_pro", args["pro-webliography"]))
		table_insert(content, renderRefBlock(pv, lang, '[[File: ' .. L(lang, "files", "arg_con") .. ' | 17px | link= | alt=Against | class=pictogramme-h3 mw-no-invert]]', L(lang, "text", "side_against"), "webliography_con", args["con-webliography"]))
		table_insert(content, renderRefBlock(pv, lang, '[[File: ' .. L(lang, "files", "arg_neutral") .. ' | 17px | text-bottom | link= | alt=Neutral | class=pictogramme-h3 mw-no-invert]]', L(lang, "text", "side_neutral"), "webliography_neutral", args["webliography"]))
		table_insert(wrap, '<div class="fr-collapsible fr-collapsed">' .. h3 .. '<div class="fr-collapsible-content contenu-references">' .. table_concat(content) .. '</div></div>')
	end

	do
		local h3 = '<h3 id="Videography" class="fr-collapsible-toggle">[[File: ' .. L(lang, "files", "expand") .. ' | 13px | link= | alt= | class=fleche-deroulante]]'
			.. '[[File: ' .. L(lang, "files", "videography") .. ' | 17px | link= | alt=' .. L(lang, "text", "videography") .. ' | class=pictogramme-h3]]'
			.. L(lang, "text", "videography") .. '</h3>'
		local content = {}
		table_insert(content, renderRefBlock(pv, lang, '[[File: ' .. L(lang, "files", "arg_pro") .. ' | 17px | link= | alt=For | class=pictogramme-h3 mw-no-invert]]', L(lang, "text", "side_for"), "videography_pro", args["pro-videography"]))
		table_insert(content, renderRefBlock(pv, lang, '[[File: ' .. L(lang, "files", "arg_con") .. ' | 17px | link= | alt=Against | class=pictogramme-h3 mw-no-invert]]', L(lang, "text", "side_against"), "videography_con", args["con-videography"]))
		table_insert(content, renderRefBlock(pv, lang, '[[File: ' .. L(lang, "files", "arg_neutral") .. ' | 17px | text-bottom | link= | alt=Neutral | class=pictogramme-h3 mw-no-invert]]', L(lang, "text", "side_neutral"), "videography_neutral", args["videography"]))
		table_insert(wrap, '<div class="fr-collapsible fr-collapsed">' .. h3 .. '<div class="fr-collapsible-content contenu-references">' .. table_concat(content) .. '</div></div>')
	end

	return table_concat(wrap)
end

----------------------------------------------------------------------
--	Champs divers (mots-clés, rubriques, connexes, interlangue) (i18n)
----------------------------------------------------------------------

local function renderKeywords(args, pv, cats, lang)
	local out = {}
	table_insert(out, '<div style="font-size: 95%; margin-top: 1em;">' .. L(lang, "text", "keywords_label"))
	if args["keywords"] and t_trim(args["keywords"]) ~= "" then
		local rendered = {}
		for _, k in ipairs(splitCSV(args["keywords"], ",")) do
			table_insert(rendered, expand(F, L(lang, "templates", "keyword_chip"), { [1] = k }))
		end
		table_insert(out, table_concat(rendered, ", "))
	else
		table_insert(out, L(lang, "text", "none"))
		table_insert(cats, "[[Category:" .. L(lang, "categories", "no_keywords") .. "]]")
	end
	table_insert(out,
		'<span class="modifier-rubrique navigation-not-searchable noprint">'
		.. pf_formlink{
			form = L(lang, "forms", "edit_keywords"),
			target = pv.encoded,
			link_text = L(lang, "text", "edit"),
			link_type = "link",
			tooltip = L(lang, "text", "edit_keywords_tooltip")
		}
		.. '</span><span style="display: none;">.</span></div>'
	)
	return table_concat(out)
end

local function renderConnexes(args, pv, cats, lang)
	local h2 =
		'<h2 id="Related_debates" class="section-modifiable">'
		.. '<span style="margin-right: 0.5em;">[[File: ' .. L(lang, "files", "related") .. ' | 20px | link= | alt=' .. L(lang, "text", "related_debates") .. ']]</span>'
		.. L(lang, "text", "related_debates")
		.. '<span class="modifier-section navigation-not-searchable noprint">'
		.. pf_formlink{
			form = L(lang, "forms", "edit_related"),
			target = pv.encoded,
			link_text = L(lang, "text", "edit"),
			link_type = "link",
			tooltip = L(lang, "text", "edit_related_tooltip")
		}
		.. '</span></h2>'

	if args["related-debates"] and t_trim(args["related-debates"]) ~= "" then
		return h2 .. '<ul class="searchaux">' .. args["related-debates"] .. '</ul>'
	else
		table_insert(cats, "[[Category:" .. L(lang, "categories", "no_related") .. "]]")
		return h2 .. '<div class="aucun-contenu navigation-not-searchable">' .. L(lang, "text", "no_related_msg") .. '</div>'
	end
end

local function renderRubriques(args, cats, lang)
	if args["sections"] and t_trim(args["sections"]) ~= "" then
		local out = {}
		for _, r in ipairs(splitCSV(args["sections"], ",")) do
			table_insert(out, "[[Category: " .. r .. "]]")
			table_insert(out, "[[Rubrique::" .. r .. "| ]]")
		end
		return table_concat(out)
	else
		return "[[Category:" .. L(lang, "categories", "no_sections") .. "]]"
	end
end

----------------------------------------------------------------------
--	SMW + SEO (i18n)
----------------------------------------------------------------------

local function smwProp(lang, key)
	local t = I18N.props[key]
	return (t and (t[lang] or t.fr or t.en)) or key
end

local function setSemanticData(args, pv, lang)
	local props = {}

	--	Pré-évaluer pour créer les {{#var: Carte-arguments-*}}
	if args["pro-arguments"] then F:preprocess(args["pro-arguments"]) end
	if args["con-arguments"] then F:preprocess(args["con-arguments"]) end

	local pourSurvol   = t_trim(F:preprocess("{{#var: Carte-arguments-pour-survol}}") or "")
	local contreSurvol = t_trim(F:preprocess("{{#var: Carte-arguments-contre-survol}}") or "")

	if pourSurvol == ""   then pourSurvol   = L(lang, "text", "arg_map_no_for") end
	if contreSurvol == "" then contreSurvol = L(lang, "text", "arg_map_no_against") end

	props[smwProp(lang,"debate_name")]		= pv.rawTitle
	props[smwProp(lang,"debate_number")]	= pv.pageId
	props[smwProp(lang,"debate_parent")]	= pv.rawTitle
	props[smwProp(lang,"breadcrumb")]		= pv.rawTitle
	props[smwProp(lang,"keyword")]			= splitCSV(args["keywords"] or "", ",")

	props[smwProp(lang,"arg_map")] =
		'<table style="background-color:transparent; width: 100%; margin: 0em 0.5em 0.15em 0;" class="navigation-not-searchable">'
		.. '<tr><th style="text-align:left;">' .. L(lang, "text", "arg_map_for") .. '</th>'
		.. '<th style="text-align:left; padding-left: 1em;">' .. L(lang, "text", "arg_map_against") .. '</th></tr>'
		.. '<tr style="vertical-align:top;"><td><div>' .. pourSurvol .. '</div></td>'
		.. '<td style="padding-left: 1em;"><div>' .. contreSurvol .. '</div></td></tr></table>'

	props[smwProp(lang,"page_params")] = table_concat({ "", "", "", "" }, "<>")

	smwSetSafe(props)
end

----------------------------------------------------------------------
--	Rendu principal (i18n)
----------------------------------------------------------------------

function p.render(frame)
	local args	= getArgs(frame)
	local lang	= detectLang(args)
	local pv	= computePageVars()
	local pieces, cats = {}, {}

	table_insert(pieces, emitLegacyVarDefines(pv))
	defineLegacyVars(frame, pv)

	table_insert(cats, "[[Category: " .. L(lang, "categories", "debates") .. "]]")

	if args["topic"] and t_trim(args["topic"]) ~= "" then
		smwSetSafe({
			[I18N.props.debate_subject[lang] or I18N.props.debate_subject.fr] = args["topic"],
			[I18N.props.subject_title[lang] or I18N.props.subject_title.fr] = args["topic"] .. " : [[" .. pv.rawTitle .. "]]"
		})
	else
		table_insert(cats, "[[Category:" .. L(lang, "categories", "no_topic") .. "]]")
	end

	if args["complete-topic"] and t_trim(args["complete-topic"]) ~= "" then
		table_insert(pieces, F:preprocess("{{SHORTDESC: " .. string.format(L(lang, "text", "shortdesc_topic"), args["complete-topic"]) .. "}}"))
	else
		table_insert(cats, "[[Category:" .. L(lang, "categories", "no_complete_topic") .. "]]")
		table_insert(pieces, F:preprocess("{{SHORTDESC: " .. L(lang, "text", "shortdesc_plain") .. "}}"))
	end

	table_insert(pieces, renderBreadcrumbJSONLD(pv, lang))
	table_insert(pieces, renderMainBanner(F, pv, lang))
	table_insert(pieces, renderAvancement(args, cats, lang))

	table_insert(pieces, '<div class="nomobile searchaux" style="display: none;">{{' .. L(lang, "templates", "summary") .. '|niveau=1|level=1}}</div>')

	--	Précharge les listes pour créer les {{#var: Carte-arguments-*}}
	if args["pro-arguments"] then F:preprocess(args["pro-arguments"]) end
	if args["con-arguments"] then F:preprocess(args["con-arguments"]) end

	--	Carte des arguments (#var)
	table_insert(pieces, renderArgumentMapTop(lang))

	table_insert(pieces, renderKeywords(args, pv, cats, lang))

	if not isProgress(lang, args["progress"], "stub") then
		local autoId = F:preprocess("{{CURRENTTIMESTAMP}}")
		F:preprocess("{{#vardefine: ID-autoévaluation | " .. autoId .. "}}")

		local target = L(lang, "text", "self_eval_ns") .. ":{{#var: Titre-page-encode}} ({{#var: ID-autoévaluation}})"

		--	Par défaut on démarre sur "for"
		local qs = L(lang, "text", "self_eval_ns")
			.. "[" .. L(lang, "text", "self_eval_field_debate") .. "]=" .. pf_escape(pv.rawTitle)
			.. "&" .. L(lang, "text", "self_eval_ns")
			.. "[" .. L(lang, "text", "self_eval_field_arg_type") .. "]=for"
			.. "&" .. L(lang, "text", "self_eval_field_position") .. "]=0"

		local startBtn = pf_formlink{
			form = L(lang, "forms", "self_eval_home"),
			target = target,
			link_text = L(lang, "text", "self_eval_start"),
			link_type = "post button",
			tooltip = L(lang, "text", "self_eval_tooltip"),
			query_string = qs
		}

		local box = {}
		table_insert(box, '<h2 id="Self_evaluation" class="navigation-not-searchable noprint" style="margin-bottom: 0;"><span style="margin-right: 0.5em; position: relative; bottom: 2px;">[[File: ' .. L(lang, "files", "selftest") .. ' | 17px | link= | alt=' .. L(lang, "text", "self_eval_title") .. ']]</span>' .. L(lang, "text", "self_eval_title") .. '</h2>')
		table_insert(box, '<div class="boite-info navigation-not-searchable noprint">' .. L(lang, "text", "self_eval_blurb") .. '<div style="margin-top: 1em">' .. startBtn .. '</div></div>')
		table_insert(pieces, table_concat(box))
	end

	table_insert(pieces, renderIntroduction(args, pv, lang))
	table_insert(pieces, renderWikipediaLinks(args, pv, lang))
	table_insert(pieces, renderArgsList(args, pv, lang, "pour"))
	table_insert(pieces, renderArgsList(args, pv, lang, "contre"))

	if not isProgress(lang, args["progress"], "stub") then
		local vTitrePage   = F:preprocess("{{#var: Titre-page}}")
		local vListePour   = F:preprocess("{{#var: Liste-pour}}")
		local vListeContre = F:preprocess("{{#var: Liste-contre}}")
		local vURL         = F:preprocess("{{#var: URL-page}}")

		table_insert(pieces, expand(F, L(lang, "templates", "debate_faq"), biParams({
			["débat|debate"]					= vTitrePage,
			["sujet|topic"]						= args["complete-topic"] or "",
			["liste-pour|for-list"]				= vListePour,
			["liste-contre|against-list"]		= vListeContre,
			["URL|URL"]							= vURL
		})))
	end

	table_insert(pieces, renderReferences(args, pv, lang))
	table_insert(pieces, renderConnexes(args, pv, cats, lang))

	table_insert(pieces,
		'<h2 id="Latest_changes" class="latest-changes-button navigation-not-searchable noprint">'
		.. '<span style="margin-right: 0.5em;">[[File: ' .. L(lang, "files", "changes") .. ' | 16px | link= | alt=' .. L(lang, "text", "latest_changes") .. ']]</span>'
		.. L(lang, "text", "latest_changes") .. '</h2>'
		.. '<div class="latest-changes navigation-not-searchable noprint"><div class="latest-changes-button mw-ui-button" data-page="'
		.. t_nowiki(pv.rawTitle) .. '">'
		.. L(lang, "text", "show_latest_changes")
		.. '</div><div class="latest-changes-wrapper"></div></div>'
	)

	table_insert(pieces, '<h2 id="Comments" class="navigation-not-searchable noprint"><span style="margin-right: 0.5em;">[[File: ' .. L(lang, "files", "feedback") .. ' | 21px | link= | alt=' .. L(lang, "text", "feedback") .. ']]</span>' .. L(lang, "text", "feedback") .. '</h2>')
	table_insert(pieces, F:preprocess("<comments />"))

	if args["interlanguage"] and t_trim(args["interlanguage"]) ~= "" then
		table_insert(pieces, args["interlanguage"])
	end

	table_insert(pieces, renderRubriques(args, cats, lang))

	--	SMW + SEO : enregistre la carte survol
	setSemanticData(args, pv, lang)

	table_insert(pieces, table_concat(cats))

	local renameLink = string.format(L(lang, "text", "rename_special_page"), pv.encoded)
	table_insert(pieces,
		'<span id="bouton-renommer" class="modifier-rubrique navigation-not-searchable noprint" style="display: none;">[['
		.. renameLink .. '|' .. L(lang, "text", "rename_link_text") .. ']]</span>'
	)

	table_insert(pieces, '<span id="bouton-modifier-sujet" class="modifier-rubrique navigation-not-searchable noprint" style="display: none;">'
		.. pf_formlink{ form = L(lang, "forms", "edit_subject"), target = pv.encoded, link_text = L(lang, "text", "edit"), link_type = "link", tooltip = L(lang, "text", "edit_subject_tooltip") }
		.. '</span>')
	table_insert(pieces, '<span id="bouton-modifier-categories" class="modifier-rubrique navigation-not-searchable noprint" style="display: none;">'
		.. pf_formlink{ form = L(lang, "forms", "edit_sections"), target = pv.encoded, link_text = L(lang, "text", "edit"), link_type = "link", tooltip = L(lang, "text", "edit_sections_tooltip") }
		.. '</span>')
	table_insert(pieces, '<span id="bouton-modifier-interlangue" class="modifier-rubrique navigation-not-searchable noprint" style="display: none;">'
		.. pf_formlink{ form = L(lang, "forms", "edit_interlanguage"), target = pv.encoded, link_text = L(lang, "text", "edit"), link_type = "link", tooltip = L(lang, "text", "edit_interlanguage_tooltip") }
		.. '</span>')

	table_insert(pieces, F:preprocess("__NOCACHE__"))

	return table_concat(pieces, "")
end

return p