Module:ControlArgs

Une page de Wikipédia, l'encyclopédie libre.

 Documentation[voir] [modifier] [historique] [purger]

Module copié de wikisource s:Module:ControlArgs, pour test sur un autre wiki, par User:Rical

This module manage arguments for any module, which allows :

  • Assist the user settings, and even the developers of the module.
  • Read and display settings and messages in several languages.
ATTENTION, LES DONNÉES DE CETTE PAGE DE TEST SONT FAUSSES ET FANTAISISTES ! ! !

#invoke:ControlArgs : mode normal[modifier le code]

{{#invoke:ControlArgs | normal }}

ControlArgs 1.02
Charles Maumené
L'auteur nil nil est mort en ècle , firstname = nil , lastname = nil , title = Charles Maumené , language_cat = nil , occupation_cat = nil , description = officier français - géographe


#invoke:ControlArgs : mode tests[modifier le code]

{{#invoke:ControlArgs | tests }}

ControlArgs 1.02:tests:fr Begin:

p.tests :
Vous devez supprimer cette documentation avant d'enregistrer. Supprimez tous les modes pour revenir en mode normal.

{{Wikidata | anneeDeces = ècle | anneeNaissance = 1857 | description = officier français - géographe | entityid = Q21157618 | id = Q21157618 | label = Charles Maumené | pays = France}}

{{Arguments | anneeDeces = ècle
| anneeNaissance = 1857
| description = officier français - géographe
| id = Q21157618
| initiale = M
| label = Charles Maumené
| mode = tests
| pays = France
| titre = Charles Maumené
}}

ControlArgs 1.02
Charles Maumené
L'auteur nil nil est mort en ècle , firstname = nil , lastname = nil , title = Charles Maumené , language_cat = nil , occupation_cat = nil , description = officier français - géographe
Module avec erreur interne Auteurs-ècle

Tests de cette page


Tests et informations liées à cette page :

Langues : utilisateur : fr, wiki : fr.

*Tests internes


* Tests internes de non régression :
  • Execution and test time : 2024-04-23 09:06:29 UTC , url = https://fr.wikipedia.org/wiki/Module:ControlArgs ,
    start in page = 60 mS , import + 16 mS , form result + 4 mS , tests + 788 mS , duration = 810 mS
    , host = fr.wikipedia.org , authority = fr.wikipedia.org , user = nil , user_language = nil , text = ControlArgs , baseText = ControlArgs , rootText = ControlArgs , subpageText = ControlArgs
    In this wiki: , modules ns(828) = 42243 , 42 patrollers(patroller) = 0 , 18 administrators (Rical ?) (sysop) = 146 , 12 bots (bot) = 89 , checkuser = 7 , bureaucrats (bureaucrat) = 0 , 0 (accountcreator) = 56
ControlArgs 1.02:tests:fr End.

* funct = doc1, [1] = missing_translations_title
<nowiki>{{#invoke:ControlArgs|doc1|missing_translations_title}}</nowiki>

* funct = doc1, [1] = options_from_mode_title
<nowiki>{{#invoke:ControlArgs|doc1|options_from_mode_title}}</nowiki>

* funct = doc1, [1] = used_options_title
<nowiki>{{#invoke:ControlArgs|doc1|used_options_title}}</nowiki>

* funct = doc1, [1] = wikidata_any_page_title, [2] = wikidata ID
<nowiki>{{#invoke:ControlArgs|doc1|wikidata_any_page_title|Q535}}</nowiki>

* funct = doc1, [1] = versions_details_title
<nowiki>{{#invoke:ControlArgs|doc1|versions_details_title}}</nowiki>
-- Module:ControlArgs

local p = {}
local CA = p
_G["CA"] = p

--	Module dependencies. Dependencias del módulo. Dépendances du module.
p.versions = { -- versions space, created here
	vn = { -- initial version descriptor
		versionName = "ControlArgs", versionNumber = "1.02", versionDate = "2015-10-29 12:26",
		selector = " ControlArgs ", -- Asked module and submodules versions
--		versionName = "ControlArgs1", versionNumber = "1.02", versionDate = "2015-10-29 17:39",
--		selector = " ControlArgs1 ", -- Asked module and submodules versions
		-- All existing versions for one module and all its submodules:
		all_versions = " CA,ControlArgs,ControlArgs1 ",
		alternates = "CA,ControlArgs,ControlArgs1", -- alias,normal,version of this module. -- automatic updated by version management
	},
	-- Along the init process, the ControlArgs module manages these submodules and their descriptors:
	alias = "CA", normal = "ControlArgs", version = "ControlArgs1", I18N = "ControlArgs/I18N", -- automatic updated by version management
	inits = {}, -- inits table to collect all init steps descriptors p.inits[i].
	requires = {}, -- requires table to collect all requires[version] descriptors.
	global_modules = {}, -- = _G global_modules table to collect all p.global_modules[alias] modules
	loaded_modules = "", warning = "", after_builder = "", -- some intermediate variables
	d = {}, -- last used module or submodule descriptor
}

local versions = CA.versions
CA.i18n_trc = "-"
CA.ModuleNS = mw.site.namespaces.Module.name .. ":"
CA.module_nil = "xx_yy_zz_tt"

--[[
Along the init process, the software in ControlArgs manage the submodules and the descriptors are saved in the MainModule.
* First, the MainModule require() the MainControlArgs sub-module.
* In all steps of init i, each module m or sub-module s can run a new require() r. m, s, i and r are ordinal numbers and finaly total numbers.
* Each init i is registered in the versions.inits[] table. Each version is registered in the requires[] table.
* Each definition of a module or sub-module contain the normal Module:title, the global variable alias and some or none alternate versions.
* The selector of versions limits the versions to use. The "+" character authorize submodules to extend the versions to use.
--]]

--[[ ControlArgs

-- Todo : debug versions test
-- Todo : versions test for any modules, not only Author3 MathRoman2

-- https://gerrit.wikimedia.org/r/#/c/188503/
-- T85419 User groups should be exposed to Scribunto
-- T85419 gerrit : Expose user information to Lua : Add mw.user, which allows viewing groups, blocks, and some other user info.

-- DEBUG :	versions.warning_report - Table wiki_translations : counts tabs=0, vars=87
-- DEBUG :	versions.warning_report t=start - Table wiki_translations : counts tabs=0, vars=87
-- DEBUG :	arbitrary_access_test	t=start - Table wiki_translations : counts tabs=0, vars=117

------ Todo later:
	-- Todo later: task T00000 Help to convert any module as centralisable
	-- Todo later: migration in Module:Versions and adapt other modules
	-- Todo later: task T00000 Get the name of the module itself from mw.GetModuleName or frame:GetModuleName to require /I18N and manage versions without errors
		-- in Author3
		-- local Versions = require "Module:Versions" -- in Author3
		-- Versions.add_modules( p, MathRoman2, ControlArgs, Versions ) -- in Author3
		-- in MathRoman2
		-- local Versions = require "Module:Versions" -- in MathRoman2
		-- Versions.add_modules( p, ControlArgs1, Versions ) -- in MathRoman2
		-- in ControlArgs
		-- local Versions = require "Module:Versions" -- in ControlArgs
		-- Versions.add_modules( p, Versions ) -- in ControlArgs
		-- in Versions
		-- Versions.build_tree( ) -- in Versions
		-- Versions.all_versions_text( ) -- in Versions
		-- Versions.warning_short( ) -- in Versions
		-- see package.loaded and _G for help

------ Todo later:
local V = require("Module:Versions")
local CA = V.require_version("ControlArgs") -- explicitly manage and require one version, off availables, in versions process.
local CA = V.require_version("MathRoman") -- explicitly manage and require one version, off availables, in versions process.
p.versions = { -- typical versions management
	main, vers, last = { -- typical version descriptor
		name = "ControlArgs", nbr = "1.01", date = "2015-10-13 wikidata date-time ISO 8601",
		selector = " ControlArgs ", -- Asked module and submodules versions
		all_versions = " CA,ControlArgs,ControlArgs1 ", -- All existing versions for one module and all its submodules:
		alternates = "CA,ControlArgs,ControlArgs1", -- alias,normal,version of this module. -- automatic updated by version management
	},
	inits, requires, loaded, global = { like vers and more }
	requires{} collects all the couples{calling, called, execution_time}.
	after the require times, any calling module can ask the build of the tree, and any report.
	The dependency is : ControlArgs, Versions, ... , Main
}

--	T00000 Todo task : Help to convert any module as centralisable
--	T00000 Todo task : Try and help to adapt any module to become central.
-- This module can adapt any other module for international use with I18N tables for argument names, wikitext, categories and errors.
-- see https://fr.wikisource.org/wiki/Module:ControlArgs
-- which adapts https://fr.wikisource.org/wiki/Module:Auteur
-- Ce module ControlArgs permet d'adapter n'importe quel autre à un usage international avec tables I18N pour les noms d'arguments, le wikitexte, les catégories et les messages.

-- == Explore aspects of central modules implementation: arguments translation, user support, submodules, versions ...

-- where to discuss these aspects ?
-- blocking tasks, why : not really blocking but help, automatize and avoids human errors : T53660 edit-sate, TTTTTT module title, T68051 user langage, T85419 users groups
-- block the task : not really blocking :
-- could offer new services to users and admins
-- module title for submodules automatic names and for alternate versions management
-- a local module can replace a local one, or this is forbiden, or this is choose by the central module
-- is a central module shown like a local one, with a special marker
-- local submodules without local main module
-- a central module title is it translated in other languages wiki ?
-- some service or aspects could become a scribunto library or a complement in scribunto extension
-- other aspect : central modules introduces Scribunto and Lua as a fringe part of the mediawiki development.
-- where to discuss ? there ?

Linked bugs :
	T85419 User groups should be exposed to Scribunto
	T49930 Allow accessing data from a Wikidata item not connected to the current page - arbitrary access (tracking)
	T52329 We need a common repository for Scribunto modules and templates
	T85412 The formatPropertyValues needs an option to format data from date-time, angle or geographic coordinates
	T48160 Lua: Allow using labels in mw.wikibase.entity.formatPropertyValues
--	T41610 Scribunto should support global module invocations, blocked by: T85419 T53660 T68051 T22222
	T53660 Detect the edit state to adapt help messages to user errors
	T67507 It should be possible to get entity id from page title
	T59656 Special:ItemDisambiguation ranking
	T68051 Get the user language to help admins to maintain a module or a template (userInfo: String user and password)
	T4085 Add a {{USERLANGUAGE}}/{{USERLANG}} magic word
		*	https://phabricator.wikimedia.org/T4085
		*	Pro arguments in short: {{CONTENTLANGUAGE}} & {{CONTENTLANG}} thus call it {{USERLANGUAGE}} & {{USERLANG}} (as {{USERIFCODE}} is too weird)
		*	Note: On Betawiki (translatewiki.net) there is such a magic word; but they call it {{UILANGCODE}}
		*	Workarounds available:
		*	*	For system administrators, installing a small {{UILANGCODE}} extension.
		*	*	For wiki administrators, using {{int:lang}} after creating all MediaWiki:Lang subpages.
		T4085 Bug duplicate of this bug: 14633, 1136, 16608, 8059, 14098, 46372
	T22222: Get title of module itself, Get the module_name itself, in case of rename, alternative version, or sub-modules names without error.
		-- need 1 : automatize the call of sub modules in sub title, like main_module/i18n for translations
		-- need 2 : help to manage alternate versions of a group of submodules in any titles
	T49137 : Add ability to generate a list of pages based on prefix to Scribunto/Lua. To easy find /I18N sub modules.
	T101280 Render labels in the user language on the client (via Lua or parser function), split parser cache - duplicate of T75460
	T75460 Sane Lua label access in non-content languages
-- It's currently unclear how to get the "correct" target language. Conceptually, it seems like Parser::getTargetLanguage() should return the user's language on multi-lingual wikis. It already does this if $this->mOptions->getInterfaceMessage() is not null.
https://phabricator.wikimedia.org/T101280

Comment Rical on 20150606 11:26 :
In a multilingual module, I put translations of arguments names, categories and error messages in the submodule "module_name/I18N". Then the main_module can change without change translations in any language in any wiki. But without the module_name itself I cannot automatize that for any modules.
The present change could resolve that, giving at the same time "module_name/I18N" and "module_name". The change could be helped with a parameter to select a part of sub titles, which contain "I18N" in my case.

I could also ask a change "Get the module_name itself". But the present change is more general and can be used for a group of sub modules and their datas.

Lua is not granteed to be installed on all third party wikis.

-- Todo debug : "\n* any texte" is forbiden before dropbox. Is it a bug ? To tests with native Dropbox.
	res = res .. CA.dropdown_func(1, "Time test table from wikidata time properties", CA.testable_lister, CA.TimeTest, "CA.TimeTest.claims." .. (CA.TimeName or "Pxxx") )

------------------------------------------------------------------------------
-- Todo pending debug														--
------------------------------------------------------------------------------

-- Todo? test view testable of wikidata time properties, CA.testable_lister CA.TimeTest birth P569 require "Datum"

-- Todo : french for test "Language Definitions:"

-- Todo : versions test for any modules, not only Author3 MathRoman2

-- Todo docum : I18N translations permit to choose at wiki level how errors are grouped in categories (in "usage error" or "internal error" or other groups).

-- Todo debug pour docum : un mode test1 avec le param t = key_test_to_view qui produit seulement un lot de tests pour les documentations utilisateur.

-- Todo debug : Dropbox plus souple a traduire et parametrer par une table de parametres nommés pour le "style".
-- Dropbox : droptions = { 1 = title = "title", 2 = content = "content", function, functargs, alignT, image, alignB, margin_bottom, width, border_radius, border_color, background_color }
-- Dropbox : ControlArgs.drop(frame) = Dropbox with multilingual translations for any template.

-- A faire : pour supprimer CA.get_editstate() attendre le Bug T53660 Detect the edit state.

-- relire ControlArgs/Author : module code in english
-- A faire : Module code must be in english, documentation in the wiki language.

-- A faire : comment eviter qu'un argument de wikidata "perde sans raison" son origine ( src = "wd" ) ?
-- masquer les couleurs en attendant, voir message_color ...

-- a faire ? dans gener_doc, trier les arguments dans l'ordre alphabetique de la langue du wiki.

-- A faire plus tard : testframework : tests groupés avec mediawiki pour des modules centralisés
-- local testframework = require 'Module:TestFramework' return testframework.getTestProvider( {-- Tests go here} )

-- Fait et a faire : séparer clairement les tests a la fin :

---------------------------------------------------------------------------------
-- Documentation ----------------------------------------------------------------
---------------------------------------------------------------------------------
-- A faire docum : base minimale : "CA traduit et verifie les agruments".
-- A faire docum : sub-modules selectors option "-" and option "+".
-- The option "-" transmits the upper selector at any lower level. The option "-" have the priority on others.
-- The option "+" mixes and extends the upper selector with the lower selector.
-- docum : suite a debat dans fr:wikipedia, comment choisir d'utiliser ou non une info de wikidata
-- par "-" pour « donnée dans l'article sinon rien » ou "+" pour « utiliser la valeur WD »
-- The URI object has the following fields, some or all of which may be nil:
-- user: String user, query: A table, as from mw.uri.parseQueryString
-- voir Module:Author3 CA.args_selected, p.selected_autorities, p.authorities_from_args = p.authorities_select()
-- sélecteur = "+" Sélectionner toutes les autorités de wikidata
-- sélecteur = "-" Supprimer toutes les autorités de wikidata. Pioritaire sur '''+'''.
-- sélecteur = "BNF, Worldcat, +4, VIAF, 3, GKD, LCCN" Sélectionner les premières, puis parmi les suivantes 4 de plus en tout, puis au maximum 3 en tout.
--		https://fr.wikipedia.org/wiki/Discussion_Projet:Infobox/Lua#Priorit.C3.A9_des_sources_de_donn.C3.A9es
--		Discussion_Projet:Infobox/Lua#Priorité des sources de données
-- docum : Ce module doit porter le même nom dans les wiki de toutes les langues, le code étant identique.
-- docum : Les sous-modules ../I18N doivent contenir au moins les traductions dans la langue du wiki.
-- docum : ControlArgs example : arguments names, errors and categories

--	Reference et pages tests :
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Test_cases
-- voir aussi https://www.mediawiki.org/wiki/Module:Auteur
-- voir aussi https://www.mediawiki.org/wiki/Module:Unitest_Scribunto
-- voir aussi https://www.mediawiki.org/wiki/Module:Documentation/config
-- voir aussi https://en.wikipedia.org/wiki/Module:ScribuntoUnit
-- voir aussi https://en.wikipedia.org/wiki/Module:UnitTests


---------------------------------------------------------------------------------
							relecture
---------------------------------------------------------------------------------


-- Fait : Si formDocBox est hors de Module ou Modèle alors : erreur et categorie "erreur d'utilisation"

-- Fait : display category without the word "Category".

-- En developement : Bug 66539 - asked - Keep getEntity in cache for all modules and wikidata-templates in a page
-- getEntityObject est très long a executer, 2 à 2.5 seconde, alors que cette entité a probablement déjà été liée pour les liens interwikis ou par interprojet. Peut-on éviter cette double requete ?

-- A faire : obtenir le Bug 51660 - asked - Detect the edit state to adapt help messages to user errors.
-- En attendant le Bug 51660 , on peut le remplacer par l'option manuelle "edit", et l'enlever avant de valider la page.
-- A faire : obtenir le Bug 66051 - asked - Get and use the user language to help to maintain a module or a template
-- La plupart des erreurs sont utiles pendant l'édition de la page, mais génantes ensuite.

-- docum : need="0" not necessary argument
-- need="1" necessary from argument
-- need="2" necessary from argument or module interaction

local testframework = require 'Module:TestFramework'
return testframework.getTestProvider( {
	-- Tests go here
} )

--]]

function CA.versions.loaded_modules() -- List all loaded modules
	local loaded = " "
	local loaded_tab = {}
	local elem = {}
	for title, m in pairs(package.loaded) do -- List package.loaded modules
		local md = string.find(title, CA.ModuleNS)
		local v18 = string.gsub(title, CA.ModuleNS, "") -- v18 = version without Module: and perhaps /I18N
		if md then loaded = loaded .. " " .. v18 .. " " end
		local I18N = string.find(title, "/I18N")
		local vrs = string.gsub(v18, "/I18N", "") -- vrs = version without /I18N
		if md then
			elem = loaded_tab[v18] or {}
			elem.pack = true
			elem.modl = true
			elem.version = vrs
			if I18N then elem.I18N = v18 end -- version with /I18N
			loaded_tab[v18] = elem
		end
	end
	for title, m in pairs(_G) do -- List global _G modules
		local md = string.find(title, CA.ModuleNS)
		local v18 = string.gsub(title, CA.ModuleNS, "") -- v18 = version without Module: and perhaps /I18N
		if md then loaded = loaded .. " " .. v18 .. " " end
		local I18N = string.find(title, "/I18N")
		local vrs = string.gsub(v18, "/I18N", "") -- vrs = version without /I18N
		if md then
			elem = loaded_tab[v18] or {}
			elem.glob = true
			elem.modl = true
			elem.version = vrs
			if I18N then elem.I18N = v18 end -- version with /I18N
			loaded_tab[v18] = elem
		end
	end
	return loaded, loaded_tab
end -- function CA.versions.loaded_modules()

function CA.versions.check_versions(t, calling_module, all_versions, selector, versionNumber, versionName, missings, unknowns, normals, listall, selectall, warnings, loaded_list, excess, aliases, check_versions_tab)
	-- Check all submodules versions.
	local loaded_modules, loaded_tab, modu = CA.versions.loaded_modules()
--	local t = t or "<br><br><b>versions.check_versions</b>: "
	local t = t or "<br>versions.check_versions recieved this ask : "
	t = t .. CA.ta(" selector ", selector) .. CA.ta(" all_versions ", all_versions) .. CA.ta(" selectall ", selectall)
	t = t .. "<br>loaded modules = " .. loaded_modules
	local warn_vrs = ""
	--
	calling_module = calling_module or CA.versions.MainModule -- for modules in the versions system
	local versionNumber = p.versions.vn.versionNumber or "0.0"
	local versionName = p.versions.vn.versionName or "VersName"
	local calling_module_selector, calling_module_all_versions
	if CA.is_version(sub_module) and sub_module.versions.vn.selector then
		calling_module_selector = sub_module.versions.vn.selector
		calling_module_all_versions = sub_module.versions.vn.all_versions
	end
	local selector = selector or calling_module_selector or CA.versions.main_selector or " " -- p.versions.vn.selector or "XXX YYY"
--	local selector = selector or calling_module.versions.vn.selector or CA.versions.main_selector or " " -- p.versions.vn.selector or "XXX YYY"
	local all_versions = all_versions or calling_module_all_versions or p.versions.vn.all_versions or " ,	, "
	local selectall = selectall or selector
	local check_versions_tab = check_versions_tab or {}
	local inits = p.versions.inits
	missings = missings or " " ; unknowns = unknowns or " " ; normals = normals or " " ; listall = listall or " "
	selectall = selectall or " " ; warnings = warnings or " " ; loaded_list = loaded_list or " " ; aliases = aliases or " "
	--
	-- Check coherence and conformities.
	local all_versions_split, max
	if type(all_versions) == "string" then all_versions_split = mw.text.split(all_versions, "*", true) end
	for a, alternates in ipairs(all_versions_split) do
		alternates = mw.text.trim( tostring(alternates) )
		t = t .. CA.ta("\n* a="..a, alternates)
		local alternates_split = mw.text.split(tostring(alternates), ",", true)
		local maxn = table.maxn(alternates_split)
		for v, altern in ipairs(alternates_split) do
			local elem, select, version, alias, normal = {}
			altern = mw.text.trim(altern)
			select = CA.is_in(" " .. altern .. " ", " " .. (selector or "") .. " ")
		--	select = CA.is_in_sp(altern, (selector or "") )
			if v == 1 and select then alias = altern ; normal = altern ; version = altern end
			if v == 2 and select then normal = altern ; version = altern end
			if v > 2 and select then version = altern end
			t = t .. CA.ta("<br>v="..v, altern) .. CA.ta("select", select)
			if (v > 2) or (v == maxn) then
				-- Check coherence and conformities
				if maxn == 1 then alias = altern ; normal = altern ; version = altern end
				if maxn == 2 then normal = altern ; version = altern end
				if maxn > 1	 then version = altern end
				elem = loaded_tab[altern] or {}
				loaded = elem["modl"] and elem["vrs"] and not elem["I18N"]
				aliases = aliases .. " " .. (alias or "")
				if version and not loaded then missings = missings .. " " .. version end
				if select and not version then unknowns = unknowns .. " " .. version end
				if normal and loaded and (version == normal) then normals = normals .. " " .. normal end
				if version and loaded then listall = listall .. " " .. version end
				all_versions_sp = string.gsub(all_versions, ",", " ") -- version without Module:
				if loaded and not CA.is_in_sp(version, all_versions_sp) then excess = excess .. " " .. version .. " " end
				warnings = missings .. unknowns
				t = t .. CA.ta("missings", missings) .. CA.ta("unknowns", unknowns) .. CA.ta("warnings", warnings)
			end
			-- if there is only one word in alternates, stop the loop. See gmatch in Extension:Scribunto/Lua reference manual
			if select and not CA.is_in_sp(altern, selectall) then selectall = selectall .. " " .. altern .. " " end
			-- extend selector with modu.versions
			if elem.version and not elem.I18N then
				loaded_list = elem.version .. " " .. loaded_list
				local modu_vrs = CA.ModuleNS .. elem.version
				if package.loaded and package.loaded[modu_vrs] then sub_module = package.loaded[modu_vrs] else sub_module = nil end
				-- Extend extensible selector in versions management sub-modules
				if CA.is_version(sub_module) and sub_module.versions.vn.selector and CA.is_in_sp("+", selector) and not CA.is_in_sp(altern, selectall)
				then selectall = selectall .. " " .. elem.version .. " " end
			--	and not CA.is_in("/I18N", sub_module.versions.vn.selector)
			--	then selectall = selectall .. " (" .. elem.version .. " > " .. sub_module.versions.vn.selector .. ") " end
			--	then selectall = selectall .. " <b>+" .. elem.version .. "</b> " end
				--
				-- Selector options "+" and "-" permit to select or mix the calling selector and/or the sub selector.
				local calling_selector = " " .. (calling_module.versions.vn.selector or " ") .. " "
			--	local selectall = selector .. " " .. calling_selector
				-- The option "-" transmits the upper selector at any lower level. The option "-" have the priority on others.
				if CA.is_in("-", calling_selector) then selector = calling_selector
				-- The option "+" mixes and extends the upper selector with the lower selector.
				elseif CA.is_in_sp("+", calling_selector) then selector = calling_selector .. " :: " .. selector
				-- If no option, uses the lower selector. It can select unpreviewed versions. Lowest priority case.
				else selector = selector end
				--
				-- Cycle inside each of submodules
				if CA.is_version(sub_module) and sub_module.versions.vn.selector and CA.is_in_sp("+", selector) and not CA.is_in_sp(altern, selectall)
				then
					warn_vrs, xx, missings, unknowns, normals, listall, selectall, warnings, loaded_list, excess, aliases, check_versions_tab =
					CA.versions.check_versions("", sub_module, nil, nil, versionNumber, versionName,
								  missings, unknowns, normals, listall, selectall, warnings, loaded_list, excess, aliases, check_versions_tab)
				end
			end -- and not CA.is_in(elem.version, selectall)
			--
			if altern == alternates then break end
		end
	end
	warn_vrs = CA.str_vars("all_versions_tests", warnings, normals, listall) or "*"
	t = t .. "<br>" .. warn_vrs -- .. ", " .. check["missings"] .. ", " .. check["unknowns"] .. ", " .. check["excess"]
	CA.check = t
--	selectall = (selectall or "")
	local v, d = CA.versions, d or CA.versions.vn or CA.versions.d or {} -- descriptor of submodule
	d.missings = missings ; d.unknowns = unknowns ; d.normals = normals ; d.listall = listall ; d.selectall = selectall ;
	d.warnings = warnings ; d.loaded_list = loaded_list ; d.excess = excess ; d.aliases = aliases ;
	check_versions_tab = table.insert( check_versions_tab, d )
	return warn_vrs, t, missings, unknowns, normals, listall, selectall, warnings, loaded_list, excess, aliases, check_versions_tab
end -- function CA.versions.check_versions(t, calling_module, all_versions, selector, versionNumber, versionName,
--						missings, unknowns, normals, listall, selectall, warnings, loaded_list, excess, aliases, check_versions_tab)

function CA.versions.require_versions(calling_module, all_versions, selector, versionNumber, versionName) -- Require all submodules versions
	local v, d = CA.versions, d or CA.versions.vn or CA.versions.d or {} -- descriptor of submodule
	local calling_module = calling_module or CA.versions.MainModule -- for modules in the versions system
	local selector = selector or CA.versions.main_selector
	--
	if CA.is_in("MathRoman", selector) then selector = selector .. " + MathRoman333 " end -- DEBUG
	--
	-- Selector options "+" and "-" permit to select or mix the calling selector and/or the sub selector.
	local calling_selector = " " .. (calling_module.versions.vn.selector or " ") .. " "
	local selectall = (selector or " s ") .. " " .. calling_selector
	-- The option "-" transmits the upper selector at any lower level. The option "-" have the priority on others.
	if CA.is_in(" - ", calling_selector) then selector = calling_selector
	-- The option "+" mixes and extends the upper selector with the lower selector.
	elseif CA.is_in(" + ", calling_selector) then selector = calling_selector .. " :: " .. selector
	-- If no option, uses the lower selector. It can select unpreviewed versions. Lowest priority case.
	else selector = selector end
	--
	local all_versions = all_versions or CA.versions.main_all_versions
	local versionNumber = all_versions or CA.versions.vn.versionNumber or "0.0"
	local versionName = all_versions or CA.versions.vn.versionName or "MainModule"
--	local all_versions_reverse = v.reverse_all_versions(d.main_all_versions) -- Reverse all the sort of submodules
	-- First, list all missing modules versions to require them.
	local warn_vrs, xx, missings, unknowns, normals, listall, selectall, warnings, loaded_list, excess, aliases, check_versions_tab =
	CA.versions.check_versions("- ", calling_module, all_versions, selector, versionNumber, versionName)
	local missings_split = mw.text.split(tostring(missings), " ", true)
	local maxn = table.maxn(missings_split) -- DEBUG : + 9 -- add some more search in case some submodules add more subversions
	for i, miss in ipairs(missings_split) do
		local miss_title = CA.ModuleNS .. miss
		local submodule = CA.require_xpcall( miss ) -- require() must not fail and block the page.
		if submodule then -- require() is OK, use the submodule
			local dd = CA.record_module(submodule, nil, nil, miss)
--			warn_vrs, xx, missings, unknowns, normals, listall, selectall, warnings, loaded_list
--			= CA.versions.require_versions(calling_module, all_versions, selector, versionNumber, versionName) -- Require all submodules versions
		end
	end
	local t = "<br>" .. warn_vrs -- .. ", " .. check["missings"] .. ", " .. check["unknowns"] .. ", " .. check["excess"]
	CA.check = t
	selectall = (selectall or " _ ")
	CA.check_versions_tab = check_versions_tab
	return warn_vrs, t, missings, unknowns, normals, listall, selectall, warnings, loaded_list, excess
end -- function CA.versions.require_versions(calling_module, all_versions, selector)

function CA.versions.check_versions_test(t) -- Check all submodules versions   Versions warning report
	-- Guide to understand the versions management:
	local t = t or "\n* <b>check_versions_test</b> :"
	t = t .. "<br><b>Selector</b>: The main module selects which modules to use and in which versions."
	t = t .. "\n* The <b>ControlArgs</b> module tries to require modules when init, and later in test checks them and displays warnings."
	t = t .. "\n* Errors can happens anywhere in this process and the versions management helps users to maintain the coherence."
	t = t .. '\n* Arguments <b>selectversions</b> and <b>allversions</b> can replace and try them in only one page.'
	t = t .. "\n* <b>Selector</b>: submodules can add their own submodules versions to the main selector."
	t = t .. "\n* <b>Loaded</b> modules : ControlArgs lists all really loaded modules and their /I18N submodules."
	t = t .. "\n* All versions are defined in the MainModule. For each module: "
	t = t .. "\n** An <b>alias</b>, used in the code, like CA for ControlArgs."
	t = t .. "\n** A <b>normal</b> module name."
	t = t .. "\n** One or more available <b>versions</b>."
	t = t .. '\n* Example of <b>allversions</b>: AT,Author,Author3 * MathRoman * CA,ControlArgs.'
	t = t .. "\n* Management indicators for each module: "
	t = t .. "\n** <b>Missing</b> = selected but not loaded."
	t = t .. "\n** <b>Unknown</b> = selected but not in all versions of one module."
	t = t .. "\n** <b>Normal</b> = selected and normal and loaded."
	t = t .. "\n** <b>Excess</b> = loaded but in none version. Out of versions management."
	t = t .. "\n** <b>SelectAll</b> = all really loaded modules, even selected by submodules."
	local loaded, loaded_tab = CA.versions.loaded_modules()
	t = t .. "\n* <b>Really Loaded modules</b>: " .. loaded
	local function check_versions_test1(t, all_versions, selector, versionNumber, versionName)
		-- Test for (Author3 * MathRoman * ControlArgs) is not enough.
		-- DEBUG todo : Implement test fo any versions names, inside the all_versions, select with a parameter which choose : (nil:all normal, 0:odd-in-all normal, 1:even-in-all normal, 2:unknow=normal_Tijk)
		local warn_vrs, xx, missings, unknowns, normals, listall, selectall, warnings, loaded_list, excess, aliases, check_versions_tab =
		CA.versions.check_versions("- ", nil, all_versions, selector, versionNumber, versionName)
		local indicators = CA.str_vars( "all_versions_check", missings, unknowns, normals, excess, selectall)
		return t .. CA.Tr() .. CA.Td(all_versions) .. CA.Td(selectall) .. CA.Td(loaded_list) .. CA.Td("<b>" .. versionNumber .. "</b> " .. indicators)
	end
	-- Main module all_versions
	local main_version = (CA.versions.main_versionName or "MainModule-T") .. " " .. (CA.versions.main_versionNumber or "0.0-T")
	t = t .. CA.Th() .. CA.Tc(" all_versions from " .. main_version) .. CA.Tc("extended selector") .. CA.Tc("loaded modules") .. CA.Tc("management indicators")
	t = check_versions_test1(t, CA.versions.main_all_versions, CA.versions.main_selector, CA.versions.main_versionNumber or "0.0-T", CA.versions.main_versionName or "MainModule-T")
	-- several selector and all_versions tests
	t = t .. CA.Tr() .. CA.Tc("all_versions tests of " .. main_version) .. CA.Tc("extended selector") .. CA.Tc("loaded modules") .. CA.Tc("management indicators")
	t = check_versions_test1(t, " AT,Author3 * MathRoman * CA,ControlArgs ", "Author MathRoman ControlArgs", "3.00", "Author")
	t = check_versions_test1(t, " AT,Author3 * MathRoman2 * CA,ControlArgs ", "Author MathRoman2 ControlArgs - ", "3.00 -", "Author")
	t = check_versions_test1(t, " AT,Author3 * MathRoman2 * CA,ControlArgs ", "Author MathRoman2 ControlArgs + ", "3.00", "Author")
	t = check_versions_test1(t, " AT,Author3 * MathRoman2 * CA,ControlArgs ", "Author MathRoman2 ControlArgs + ", "3.00 +", "Author")
	t = check_versions_test1(t, " AT,Author3 * MathRoman * CA,ControlArgs ", "Author3 MathRoman2 ControlArgs", "3.00", "Author")
	t = check_versions_test1(t, " AT,Author,Author3 * MathRoman,MathRoman2 * CA,ControlArgs ", "Author3 MathRoman2 ControlArgs", "3.00", "Author")
	t = check_versions_test1(t, " AT,Author,Author,Author3 * MathRoman,MathRoman,MathRoman2 * CA,ControlArgs,ControlArgs1 ", "Author3 MathRoman2 ControlArgs", "3.00", "Author")
	t = check_versions_test1(t, " AT,Author4,Author3 * MathRoman,MathRoman4,MathRoman2 * CA,ControlArgs,ControlArgs4 ", "Author3 MathRoman2 ControlArgs1 - ", "3.00 -", "Author")
	t = check_versions_test1(t, " AT,Author4,Author3 * MathRoman,MathRoman4,MathRoman2 * CA,ControlArgs,ControlArgs4 ", "Author3 MathRoman2 ControlArgs1 ", "3.00+", "Author")
	t = check_versions_test1(t, " AT,Author4,Author3 * MathRoman,MathRoman4,MathRoman2 * CA,ControlArgs,ControlArgs4 ", "Author3 MathRoman2 ControlArgs1 + ", "3.00 +", "Author")
	t = t .. CA.Te()
	return t
end -- function CA.versions.check_versions_test(d)

function CA.i18n_trac(id, t, count) -- track for i18n DEBUG
--	CA.i18n_trac("base_func", "end", 1) -- example of use
	CA.i18n_trc = CA.i18n_trc .. "<br><b>" .. (id or "") .. "</b> t=" .. (t or "")
	if count then
		CA.i18n_trc = CA.i18n_trc .. " - " .. CA.table_count("i18n")
		CA.i18n_trc = CA.i18n_trc .. " - " .. CA.table_count("wiki_translations")
		CA.i18n_trc = CA.i18n_trc .. " - " .. CA.table_count("user_translations")
	end
	return
end

function CA.is_in(word, text) -- The word is it in the text, even inside another word?
	local f = string.find(text, word, 1, true)
	if f then return true else return false end
end

function CA.is_in_sp(word, text) -- The word is it in the text, beetwen spaces, not inside another word?
	local f = string.find(" " .. text .. " ", " " .. word .. " ", 1, true)
--	local f = string.find(text, "%s" .. word .. "%s")
	if f then return true else return false end
end

function CA.is_version(module) -- A module is it in version management system?
	return type(module) == "table" and module.versions and module.versions.vn
end

function CA.versions.msri(m,s,r,i)
	local v, d = CA.versions, d or CA.versions.d or {} -- descriptor of submodule
	m = m or d.n_modul or 0 ; s = s or d.n_submod or 0 ; r = r or d.n_require or 0 ; i = i or d.n_init or 0
	return " :m"..m.."s"..s.."r"..r.."i"..i..": "
end -- CA.versions.msri(m,s,r,i)

--	local already_required = package.loaded[d.version] or v.global_modules.MainModule.module_version
--	if d.version and not CA.module_loaded(d.version) then -- restore submodule from global_modules in _G or require
function CA.module_loaded(version)
	local loaded = false
	version = string.gsub(version, CA.ModuleNS, "") -- version without Module:
	version = CA.ModuleNS .. version -- if not CA.is_in(CA.ModuleNS, version) then version = CA.ModuleNS .. version end
	for title, vers in pairs(package.loaded) do -- all
	--	if type(vers) == "table" and vers.module_version and vers.module_version == version then loaded = true end
--		title = string.gsub(title, ": ", ":") -- DEBUG loaded "Module: MathRoman"
		if type(vers) == "table" and CA.is_in(version, title) then loaded = true end
	end
	return loaded
--	return CA.is_in(version, CA.versions.package_loaded) -- verify if a module was already_required, see package.loaded
end

function CA.package_loaded_read() -- Read and register the state of package.loaded to keep scribunto packages
-- Read the updated package.loaded, and add the MainModule which contains it
	local v, d = CA.versions, d or CA.versions.d or {} -- descriptor of submodule
	v.package_loaded = " "
	for version, modu in pairs(package.loaded) do
		v.package_loaded = v.package_loaded .. version .. " "
	end
	return v.package_loaded
end

function CA.versions.MainModuleInitState(when)
	local v, d, t = CA.versions, d or CA.versions.d or {}, "" -- descriptor of submodule
	local inits_maxn = table.maxn(v.inits)
	t = t .. "<br><b>Track " .. when .. "</b> : "  .. CA.ta("inits", inits_maxn) .. CA.versions.msri(m,s,r,i)
	if v.inits and v.inits[inits_maxn] then
		t = t  .. CA.ta("version", v.inits[inits_maxn].version)
		t = t  .. CA.ta("time", v.inits[inits_maxn].time)
	end
	v.MainModuleInitStateSteps = (v.MainModuleInitStateSteps or "") .. t
	return t
end

function versions.version_warning_normal_list(d) -- From alternatives forms version, title, alias, warning, normal
	local v, d = CA.versions, d or v.d or {} -- descriptor of submodule
	--	example of d.alternatives = "ControlArgs,CA,ControlArgs1,ControlArgs3"
	if type(d.alternates) == "string" then d.alternates = mw.text.trim(d.alternates) end
	if not d.alternates then d.alternates = "" ; return d	end
	local altern_tab = mw.text.split(d.alternates, ",", true)
	local version, alias, normal
	for v, altern in ipairs(altern_tab) do
		altern = mw.text.trim(altern)
		if v == 1 and CA.is_in(altern, d.selector) then alias = altern ; normal = altern ; version = altern end
		if v == 2 and CA.is_in(altern, d.selector) then normal = altern ; version = altern end
		if v > 2 and CA.is_in(altern, d.selector) then version = altern end
	end
	if version then
		d.version = string.gsub(version, CA.ModuleNS, "") -- version without Module:
		d.title = CA.ModuleNS .. d.version
		d.normal = normal
		d.normal_I18N = CA.ModuleNS .. normal .. "/I18N"
		d.normal_I18N = string.gsub(d.normal_I18N, "/I18N/I18N", "/I18N")
		d.version_I18N = CA.ModuleNS .. version .. "/I18N"
		d.version_I18N = string.gsub(d.version_I18N, "/I18N/I18N", "/I18N")
		d.alias = alias
		if d.normal and not CA.is_in(d.normal, d.norm or " ") then d.norm = (d.norm or " ") .. " " .. d.normal .. " " end
		if d.version and not CA.is_in(d.version, d.list or " ") then d.list = (d.list or " ") .. " " .. d.version .. " " end
		if d.normal_I18N and package.loaded[d.normal_I18N] and not CA.is_in(d.normal_I18N, d.list or " ") then d.list = (d.list or " ") .. " " .. d.normal_I18N .. " " end
		if d.version_I18N and package.loaded[d.version_I18N] and not CA.is_in(d.version_I18N, d.list or " ") then d.list = (d.list or " ") .. " " .. d.version_I18N .. " " end
	--	d.missing = d.missing .. " " .. d.version .. " " -- managed elsewhere
	--	d.excess = d.excess .. " " .. d.version .. " " -- managed elsewhere
	end
	return d
end -- function versions.version_warning_normal_list(d)

-- Search also init of submodules in recursive submodules. examples :
-- ControlArgs : step 1 MainControlArgs
-- Author > ControlArgs step 2 direct
-- MathRoman > ControlArgs : step 2 direct
-- Author > MathRoman > ControlArgs : step 2 recursive
-- http://www.lua.org/manual/5.1/manual.html#pdf-require
-- looking into the package.loaded table to determine whether modname is already loaded.
-- To find a loader, require is guided by the package.loaders array.
-- If the loader returns no value and has not assigned any value to package.loaded[modname], then require assigns true to this entry.
-- https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Error_handling
--	function CA.versions.warning_report(t, warning, all_versions_text, tree_text)

function CA.require_xpcall( version ) -- require() must not fail and block the page.
	if type(version) == "string" then
		version = string.gsub( version, CA.ModuleNS, "" )
		version = CA.ModuleNS .. version -- example Module:ControlArgs/I18N
	end
	local success, module = pcall( require, version ) -- pcall or xpcall can run any function without blocking page.
	if success then return module else return nil end
end

function versions.reverse_all_versions(all_versions) -- Reverse all the sort of submodules
	local modules_reverse = {}
	local all_versions_split, max
	if type(all_versions) == "string"
		then all_versions_split = mw.text.split(all_versions, "*", true)
		else all_versions_split = all_versions end
	if type(all_versions_split) == "table"
		then max = table.maxn(all_versions_split)
		else return "" end
	if type(all_versions_split) == "table" then
		for i, alternatives in ipairs(all_versions_split) do
			modules_reverse[i] = mw.text.trim(all_versions_split[max+1-i])
		end
	end
	all_versions = table.concat(modules_reverse, " * ")
	return all_versions
end -- function versions.reverse_all_versions(all_versions)

-- Modifier ou ajouter des langues et champs de traductions d'une table ou d'un module
function CA.add_i18n_mix(t, module_name, module_tab)
--function CA.add_i18n(i18n, t)
	-- p.add_i18n(p.i18n) -- fusionner les traductions d'une table i18n
	-- p.add_i18n("ControlArgs/I18N") -- fusionner les traductions d'un module I18N
	t = t or ""
	local an, ln, i18n = 0, 0
	if type(module_tab) == "table" then -- and type(module_tab.i18n) == "table" then
		i18n = module_tab -- .i18n
		an, ln = 0, 0 -- module_tab.
		for lng, trans in pairs(i18n) do -- Pour toutes les langues à importer
			if CA.i18n[lng] then -- la langue existe déja, y ajouter ou y remplacer les champs importés
				t = t .. CA.str_vars(", '''Langue : %1''' ", lng)
				ln = ln + 1
				if type(trans) == "table" then --if th language to add is really in a table
					for argn, val in pairs(trans) do -- Pour tous les champs importés
						CA.i18n[lng][argn] = val -- ajouter ou remplacer un champs et sa traduction
						t = t .. CA.str_vars(", %1 ", argn)
						an = an + 1
					end
				end
			else -- add the table and all is fileds if it is not already in CA.i18n
				if mw.language.isKnownLanguageTag(lng) then
					CA.i18n[lng] = i18n[lng]
				end
			end
		end
	end
	return t, CA.i18n, ln, an
end -- function CA.add_i18n_mix(t, module_name, module_tab)

function CA.add_i18n_track(id, module_name, module_tab)
	if type(module_name) ~= "string" then return "" end
	-- Erreur Lua dans Module:Author3 à la ligne 3725 : stack overflow. dans la fonction « add_i18n_track » in loop
--	if type(module_name) ~= "string" then module_name = "missing module_name error" end
	module_name = module_name or ""
	module_name = string.gsub(module_name, CA.ModuleNS, "")
	local t = "" -- .. "add " .. id .. ": "
	local st, vr, fn, tb, mix, v_t
	if type(module_tab) == "table" then
		st, vr, fn, tb = CA.testable_lister(module_tab, module_name)
		if tb < 1 then tb = 1 end
		v_t = math.floor( vr / tb )
		t = t .. CA.ta(module_name, tostring(vr) .. "/" .. tostring(tb) .. "=" .. tostring(v_t) ) .. " v/t "
		st, vr, fn, tb = CA.testable_lister(p.i18n, "i18n")
		if tb < 1 then tb = 1 end
		v_t = math.floor( vr / tb )
		t = t .. CA.ta("i18n", tostring(vr) .. "/" .. tostring(tb) .. "=" .. tostring(v_t) ) .. "v/t "
		st, vr, fn, tb = CA.testable_lister(CA.wiki_translations, "wiki_translations")
		if tb < 1 then tb = 1 end
		v_t = math.floor( vr / tb )
		t = t .. CA.ta("wiki_translations", tostring(vr) .. "/" .. tostring(tb) .. "=" .. tostring(v_t) ) .. ". "
	else
		t = t .. " table is nil."
	end
	return t
end -- function CA.add_i18n_track(t, module_name, module_tab)

-- Modifier ou ajouter des langues et champs de traductions d'une table i18n ou d'un module module/I18N
-- Mix or add languages and translation values from one i18n table or one module/I18N
function CA.add_i18n(id, module_name, module_tab)
--	CA.add_i18n(t, sub_i18n) -- normal usage
--	t, X18n, ln, an = CA.add_i18n(t, normal .. "/I18N")
	if CA.is_in(CA.module_nil, module_name) then return " * - " end
	local i18n, X_18n, I18N_mod, t_i18n
	local I18N_title = CA.ModuleNS .. module_name -- .. "/I18N"
--	CA.track_i18n_t("add_i18n(" .. tostring(i18n) .. ")")
	t = id or "add_i18n : "
	if type(module_name) == "string" and type(module_tab) == "table" then -- if we already have the module itself.
		module_name = module_name
		i18n = module_tab
	elseif type(module_name) == "string" and module_name ~= CA.module_nil then -- if we have only the module name, we must require it.
		-- Verify that translations are available. Verifier que les traductions sont disponibles.
		I18N_mod = CA.require_xpcall(I18N_title) -- integrer le module I18N
		if type(I18N_mod) == "table" and type(I18N_mod.i18n) == "table" then
			-- If the require() module really contains an i18n table
			i18n = I18N_mod.i18n
		end
	end
	if type(i18n) == "table" then
		tx, X_18n, ln, an = CA.add_i18n_mix(id, module_name, i18n)
		t = CA.add_i18n_track(id, module_name, i18n)
		if type(t) ~= "string" then return " add_i18n/add_i18n_track " end
	--	if type(tx) == "string" then t = t .. tx else
	--	t = t .. " (alias " .. tostring( tx ) .. " ? " .. alias .. ") " end
	else
		t = "add_i18n without i18n table."
	end
	if type(t) ~= "string" then return " add_i18n/module_name " end
	if id == "alias" then t_i18n = "" else t_i18n = "/I18N" end
	t = "<br>+ " .. id .. ": <b>" .. module_name .. t_i18n .. "</b> " .. t
	if type(t) ~= "string" then return " add_i18n/return " end
	return t, CA.i18n, ln or 0, an or 0, i18n
--	t, X18n, ln, an = CA.add_i18n(t, normal .. "/I18N")
end -- function CA.add_i18n(t, module_name, module_tab)

CA.track_i18n_vers = "\n* CA.versions.init_i18n: "
function CA.versions.init_i18n(d, t) -- Cumulate all translations in CA.i18n, from i18n tables and modules.
	local v, d = CA.versions, d or CA.versions.vn or CA.versions.d or {} -- descriptor of submodule
	local submodule = nil
	t = t or "CA.versions.init_i18n, count of translations"
	local tx = "<br><b>" .. t .. "</b>: "
	--
	local selector = v.main_selector -- or " Author3 MathRoman2 ConstrolArgs " -- DEBUG
	CA.init_wiki_user_lang()
	d.all_versions_reverse = v.reverse_all_versions(v.main_all_versions) -- Reverse all the sort of submodules
	CA.i18n_trc = CA.i18n_trc .. CA.ta("init_i18n", d.version) .. CA.table_count("i18n") .. CA.ta("main_all_versions", v.main_all_versions) .. CA.ta("reverse", d.all_versions_reverse) -- CA.i18n_trc ..
	local sub_i18n = CA.i18n -- CA.require_i18n(alias, CA)
	local vars_ante, vars_post, vars_diff, X18n = 0, 0, 0, nil
	--
	-- Save first CA.i18n in case of remake init_i18n after a remake init_i18n from an argument selectversions or allversions:
	if not CA_i18n_memory then _G["CA_i18n_memory"] = mw.clone(CA.i18n) end
	-- Restore first CA.i18n in case of remake init_i18n after remake init_i18n from an argument selectversions or allversions:
	if CA_i18n_memory then CA.i18n = CA_i18n_memory end
	--
	-- Add, mix and track all other translations of other modules and /I18N submodules
	local all_versions_split = mw.text.split(d.all_versions_reverse, "*", true)
	local t1, x, ln, an, i18n
	local st, vr, fn, tb
	local alias, normal, version
	for i, alternatives in ipairs(all_versions_split) do
		CA.track_i18n_vers = CA.track_i18n_vers .. CA.ta("Module:", alternatives)
		t = t .. "<br><br>* <b>versions.init_i18n " .. i .. " </b>: " .. CA.ta("alternatives", alternatives)
		local altern_tab = mw.text.split(alternatives, ",", true)
		alias = nil ; normal = nil ; version = nil
		for v, altern in ipairs(altern_tab) do
			altern = mw.text.trim(altern)
			if v == 1 then alias = altern end
			if v == 2 and CA.is_in(altern, selector) then normal = altern end
			if v >= 2 and CA.is_in(altern, selector) then version = altern end
		end
		t = t .. " v/t=variables/tables."
		--
		if alias and _G[alias] and _G[alias].i18n then -- alias p.i18n
	--	--	table.insert(sub_i18n, alias) -- register the MainControlArgs descriptor, cloning it to distinct each step
			t1, x, ln, an, i18n = CA.add_i18n("alias", alias, _G[alias].i18n)
			t = t .. t1
			CA.track_i18n_vers = CA.track_i18n_vers .. CA.ta("alias", alias)
		end
		--
		if normal and normal ~= CA.module_nil then -- Module:Normal/I18N
			normal = string.gsub(normal, CA.ModuleNS, "")
			normal = string.gsub(normal, "/I18N", "")
			t1, x, ln, an, i18n = CA.add_i18n("normal", normal)
			t = t .. t1
			CA.track_i18n_vers = CA.track_i18n_vers .. CA.ta("normal", normal)
			t1, x, ln, an, i18n = CA.add_i18n("normal/I18N", normal .. "/I18N")
			t = t .. t1
			CA.track_i18n_vers = CA.track_i18n_vers .. CA.ta("normal/I18N", normal .. "/I18N")
		end
		--
		if version and version ~= CA.module_nil then -- Module:Version/I18N
			version = string.gsub(version, CA.ModuleNS, "")
			version = string.gsub(version, "/I18N", "")
			t1, x, ln, an, i18n = CA.add_i18n("version", version) -- return t, CA.i18n, ln or 0, an or 0
			t = t .. t1
			CA.track_i18n_vers = CA.track_i18n_vers .. CA.ta("version", version)
			t1, x, ln, an, i18n = CA.add_i18n("version/I18N", version .. "/I18N")
			t = t .. t1
			CA.track_i18n_vers = CA.track_i18n_vers .. CA.ta("version/I18N", version .. "/I18N")
		end
		st, vr, fn, tb = CA.testable_lister(CA.wiki_translations, "wiki_translations")
		tx = tx .. " * " .. alternatives .. ", <b>N = " .. vr .. "</b> "
		CA.track_i18n_vers = CA.track_i18n_vers .. CA.ta("Module:", (alias or "-") .. ", / " .. (normal or "-") .. " / " .. (version or "-") )
	end
	--	If ConstrolArgs is running alone, display it's name and version.
	local st, vr, fn, tb = CA.table_count(CA.requires)
	if tb ~= 0 then
--		versionName = "ControlArgs1", versionNumber = "1.01", versionDate = "2015-09-09",
		CA.versions.main_versionName = CA.versions.main_versionName or CA.versions.vn.versionName or "ConstrolArgs_i"
		CA.versions.main_versionNumber = CA.versions.main_versionNumber or CA.versions.vn.versionNumber or "0.1_i"
		CA.versions.main_versionDate = CA.versions.main_versionDate or CA.versions.vn.main_versionDate or "2015-09-09"
	end
	-- Save first CA in case of another version of ControlArgs after a remake init_i18n from an argument selectversions or allversions:
	if CA_memory and CA_memory ~= CA then _G["CA_memory"] = CA end
	-- For further cases after remake init_i18n from an argument selectversions or allversions:
	_G["CA_memory"] = CA
	_G["CA_i18n_memory"] = CA.i18n
	--
--	CA.init_user_lang()
	t = "<br><br>AT=" .. CA.str_vars("entityid_txt") .. ", MR=" .. CA.str_vars("err_roman_error") .. ", CA=" .. CA.str_vars("cat_usage_error") .. t
	return tx, sub_i18n
--- return t, sub_i18n
end -- function CA.versions.init_i18n(d, t)

--[[	Module dependencies. Dependencias del módulo. Dépendances du module.
	versions.selector = " Author3 MathRoman ControlArgs1 " -- List of versions of submodules to require from one module.
	versions.all_versions = "Author,AT,Author3 * MathRoman,MathRoman,MathRoman2 * ControlArgs,CA,ControlArgs1" -- List of all existing versions of one module and all its submodules
	modules_builder() requires submodules following versions.selector and versions.all_versions.
	modules_builder() lists also the branches of the tree of submodules.
--]]

function CA.record_module(module, d, alias, glob_1, glob_2, glob_3)
	-- Record a module and its descriptor in reference tables.
	local v, d = CA.versions, d or CA.versions.d or {} -- descriptor of the module
	if type(module) ~= "table" then return end -- other global record
	d = {} -- descriptor of a module.
	-- Record the key datas of the module.
	if CA.is_version(module) and module.versions.vn.version then
		d.version = module.versions.vn.version
		d.I18N = module.versions.vn.I18N
		d.selector = module.versions.vn.selector
		d.all_versions = module.versions.vn.all_versions
		alias = alias or module.versions.vn.alias
	else
		return d
	end
	d.version = string.gsub(d.version, CA.ModuleNS, "") -- version without Module:
	d.title = CA.ModuleNS .. d.version
	alias = alias or d.module_version
--	alias = module.module_alias or alias or d.module_version
	if type(alias) == "string" then d.alias = mw.text.trim(alias) end
	--
	-- Record the module in reference tables for later management uses.
	v.requires[d.title] = mw.clone(d) -- register the version module descriptor
	table.insert(v.inits, mw.clone(d) ) -- register the MainControlArgs descriptor, cloning it to distinct each step
	if d.alias then v.global_modules[d.alias] = module end -- register the module descriptor by its variable name
	--
	-- Record the module for direct acces like: alias = require(version).
	if d.alias then _G[d.alias] = module end -- register the module descriptor by its variable name
	if d.version then _G[d.version] = module end -- Insert the submodule in the _G global space.
	if d.title then _G[d.title] = module end -- Insert the submodule in the _G global space.
	--
	-- other global records, examples: MainModule, MainControlArgs, Module:ControlArgs/I18N, or ControlArgs/I18N.
	if type(glob_1) == "string" and module then _G[glob_1] = module end -- other global record
	if type(glob_2) == "string" and module then _G[glob_2] = module end -- other global record
	if type(glob_3) == "string" and module then _G[glob_3] = module end -- other global record
	v.d = d
	return d
end -- function CA.record_module(module, d, alias, glob_1, glob_2, glob_3)

function CA.versions.modules_builder_raz(calling_module) -- raz excess _G
--	local v, d = CA.versions, CA.versions.d or {} -- descriptor of submodule
	local v, d = CA.versions, d or CA.versions.d or CA.versions.vn or {} -- descriptor of submodule
	local all_versions
	-- Along the init process : First step, the MainModule execute : local CA = require (CA.ModuleNS .. p.CA_version)
	-- Report and warning lists : d.missing, d.warning, d.excess, d.norm, d.list.
	d.N_submodules = 0
	if type(d.all_versions) == "string" then -- Cut and trim
		d.all_versions_split = mw.text.split(d.all_versions, "*", true)
		d.N_submodules = table.maxn(d.all_versions_split)
	end
--	local all_versions = table.concat(d.all_versions, " ** ")
	for i = 1, d.N_submodules do
		d.alternates = d.all_versions[i]
		d.alternates = mw.text.trim(d.alternates)
		d = v.version_warning_normal_list(d) -- From alternatives forms version, title, alias, warning, normal
		if d.title then -- if submodule realy required
			_G[d.alias] = nil
			_G[d.title] = nil
			_G[d.version] = nil
		end
	end
	d.missings = "" ; d.unknowns = "" ; d.normals = "" ; d.listall = "" ; d.selectall = "" ;
	d.warnings = "" ; d.loaded_list = "" ; d.excess = "" ;	d.aliases = "" ;
	v.d = d
	return d
end -- function CA.versions.modules_builder_raz(calling_module)

--	modules_builder() requires submodules following selector and all_versions.
--	modules_builder() lists also the branches of the tree of submodules if modules_builder() present in all ones.
function CA.modules_builder(calling_module, d)
--	local v, d = CA.versions, CA.versions.d or CA.versions.vn or {} -- descriptor of submodule
	local v, d = CA.versions , d -- or CA.versions.d or CA.versions.vn or {} -- descriptor of submodule
	local t = ""
	-- Along the init process : First step, the MainModule execute : local CA = require (CA.ModuleNS .. p.CA_version)
	-- Then MainControlArgs execute versions.require_builder(Main_init, calling_module) ... and register the MainControlArgs.
	-- The MainControlArgs only builds and manages sub modules, but the managing tables must stay in the MainModule.
	--
	-- Along the init process : Second step, the MainModule execute : CA.modules_builder(p, versions.used, versions.all_versions)
	-- Then MainControlArgs execute versions.modules_builder(calling_module, selector, all)
	-- Then MainControlArgs execute versions.require_builder(Main_init, calling_module) ... and register the MainModule.
	--
	if not calling_module then calling_module = CA.versions.MainModule end -- for modules in the versions system
	if type(calling_module) == "table" and calling_module.versions then -- for modules in the versions system
	--	v, d = CA.versions, CA.versions.vn or {} -- descriptor of submodule
	--	v, d = CA.versions --, d or CA.versions.d or CA.versions.vn or {} -- descriptor of submodule
		if not GlobalVersions then -- Only for the first and main module MainModule.
			GlobalVersions = {} -- create the versions space in the _G global space.
			GlobalVersions = CA.versions -- The ControlArgs.versions memory begins to share the same versions table with others modules in the versions system.
			CA.versions.MainModule = calling_module
		--	CA.versions.main_versionName = calling_module.versions.vn.versionName or "MainModule"
		--	CA.versions.main_versionNumber = calling_module.versions.vn.versionNumber or "0.0"
		--	CA.versions.main_selector = calling_module.versions.vn.selector .. " + " -- DEBUG
		--	CA.versions.main_all_versions = calling_module.versions.vn.all_versions
			if calling_module and calling_module.versions and type(calling_module.versions.vn) == "table" then
				local vn = calling_module.versions.vn
				if type(vn.versionName) == "string" then CA.versions.main_versionName = vn.versionName or "MainModule_b" end
				if type(vn.versionNumber) == "string" then CA.versions.main_versionNumber = vn.versionNumber or "0.0_b" end
				if type(vn.versionDate) == "string" then CA.versions.main_versionDate = vn.versionDate or "2015-09-09" end
				if type(vn.selector) == "string" then CA.versions.main_selector = vn.selector or " + " end
				if type(vn.all_versions) == "string" then CA.versions.main_all_versions = vn.all_versions or " " end
			end -- for modules in the versions system
		--	CA.versions.main_versionName = CA.versions.vn.versionName or "ConstrolArgs"
		--	CA.versions.main_versionNumber = CA.versions.vn.versionNumber or "0.0"
			if calling_module.versions and not d then -- for modules in the versions system
				local dd = calling_module.versions.vn
				dd = CA.record_module(calling_module, dd, nil, "MainModule")
			--	return dd
			end
			-- Require all submodules versions
			local warn_vrs, t, missings, unknowns, normals, listall, selectall, warnings, loaded_list
			= CA.versions.require_versions(calling_module, CA.versions.main_selector, CA.versions.main_all_versions)
			CA.versions.main_warnings = warnings
		--	t, x = CA.versions.init_i18n(d, "modules_builder ") -- Cumulate all translations in CA.i18n, from i18n tables and modules.
		--	CA.add_i18n_track_txt = t
		else
			-- Other intermediate module could try to init here because they could also be main modules.
			-- Do nothing. Other intermediate modules are built following the Main module selector and all_versions.
		end
	end
	--	What to do when the user or the template ask a allversions or a selectversions ?
	--	1 : Build the submodules before import all arguments and do nothing.
	--	2 : Build the submodules only after import all arguments.
	--	CA.versions.selector = args_final.selectversions
	--	CA.versions.all_versions = args_final.allversions
	--	The versions mecanism can also use this argument
	--	If the arguments exist they have the priority.
	return
end -- function CA.modules_builder(calling_module)

function CA.ta(txt, val, sep) -- Formats an argument and its value in a documentation. The text is "nil" if the value is nil.
	if not val then val = "nil" end
	if not sep then sep = "=" end
	return " , " .. tostring(txt) .. " " .. sep .. " <b> " .. tostring(val) .. " </b> "
end

function CA.tam(txt, val, sep) -- Met en forme un argument, ou le masque s'il est nul.
	if not val then return "" end
	return p.ta(txt, val, sep)
end

--	The MainModule uses the CA.modules_builder(p) to select and build all submodules.
--	CA.modules_builder(1) -- is also called by intermediate submodules able to be main modules.

if not CA then
--	versions.warning = versions.warning .. " no_CA_in_ControlArgs"
--	CA = require (CA.ModuleNS .. p.CA_version)
end

function CA.all_versions_text(all_versions) -- DEBUG
	all_versions = all_versions or CA.versions.all_versions
	if type(all_versions) == "string" then return all_versions end
--	if type(all_versions) == "string" then all_versions = { all_versions } end
	if type(all_versions) == "table" then return table.concat(all_versions, " >> ")
	else return "" end
end

function CA.track_i18n_t(t)
	CA.track_i18n_tt = (CA.track_i18n_tt or "track_i18n_tt ") .. (t or "track_i18n_tt") .. ":" .. CA.str_vars("err_J_before_end") .. " - "
	return CA.track_i18n_tt -- .. " " .. CA.i18n_trc, see also track_i18n_vers
end

--	function CA.guide_text(d)	return CA.versions.warning_short(d) end
--	function CA.warning_text(d) return CA.versions.warning_short(d) end
function CA.versions.warning_short(d, missing, warning, excess)
	local v, d, t = CA.versions, d or CA.versions.d, ""
	missing = missing or d.missing or v.missing or ""
	warning = warning or d.warning or v.warning or ""
	excess = excess or d.excess or v.excess or ""
	if mw.text.trim(missing .. warning .. excess) ~= "" then
		t = CA.str_vars("Modules missing: %1 warning: %2 excess: %3", missing, warning, excess)
		t = CA.error_color(t)
	elseif v.vn then -- v.vn.versionNumber = "2.00", v.vn.versionName = "MR", -- example
		t = CA.discreet_color(CA.versions.main_versionName .. " " .. CA.versions.main_versionNumber .. " ")
	else
		t = CA.discreet_color(v.version .. " ")
	end
--	t = t .. CA.track_i18n_vers
	return t
end
--	t = t .. CA.str_vars("modules versions missing: %1 warning: %2", missing, warning)
--	t = t .. "Modules missing: " .. (missing or "") .. " warning: " .. (warning or "") .. " warning: " .. (warning or "")
--	t = t .. CA.str_vars("Modules missing: %1 warning: %2 excess: %3", missing, warning, excess)
--	t = t .. CA.track_i18n_t("warning_short")

--	function CA.warning_versions_text() return CA.versions.warning_small() end
--	function CA.warning_text() return CA.versions.warning_small() end
function CA.versions.warning_small(d)
	local v, d, t = CA.versions, d or CA.versions.d, ""
	t = t .. CA.versions.warning_short(d)
	t = CA.str_vars("<small><small>" .. t .. "</small></small>")
	return t
end

function CA.versions.warning_report(t)
	local v, d = CA.versions, CA.versions.d
	local t = "<br>Versions warning report for <b>" .. v.main_versionName .. " " .. v.main_versionNumber .. " " .. v.main_versionDate .. "</b> : "
	t = t .. "<br>Main module selector: <b>" .. (v.main_selector or "main_selector") .. "</b>"
	t = t .. "<br>Main module all versions: <b>" .. (v.main_all_versions or "main_all_versions") .. "</b>"
	CA.i18n_trac("versions.warning_report", "start", 1)
	local loaded_modules, loaded_tab, modu = v.loaded_modules()
	CA.loaded_modules_track = "v.warning_report : " .. loaded_modules
	t = t .. "<br>* Used modules: "
	t = t .. "<br>* " .. v.main_versionName .. " : "
	t = t .. CA.ta("versionName", v.main_versionName )
	t = t .. CA.ta("versionNumber", v.main_versionNumber )
	t = t .. CA.ta("versionDate", v.main_versionDate )
	for title, modu in pairs(CA.check_versions_tab or _G) do -- check_versions_tab
		if type(modu) == "table" and title ~= "CA_memory" and modu.versions and modu.versions.vn then --
			local vn = modu.versions.vn
		--	versionName = "ControlArgs1", versionNumber = "1.01", versionDate = "2015-09-09",
			t = t .. "<br>* " .. title .. " : "
			t = t .. CA.ta("versionName", vn.versionName )
			t = t .. CA.ta("versionNumber", vn.versionNumber )
			t = t .. CA.ta("versionDate", vn.versionDate )
		end
	end
	t = t .. "<br>"
	t = t .. "<br>* Received options : "
	t = t .. "<br>" .. CA.ta("CA.args_final.mode", CA.args_final.mode)
	t = t .. "<br>" .. CA.ta("CA.mode_options", CA.mode_options)
	t = t .. "<br>" .. CA.ta("CA.invoke_options", CA.invoke_options)
	t = t .. "<br>" .. CA.ta("CA.args_final.c", CA.args_final.c)
	t = t .. "<br>" .. CA.versions.warning_versions_details()
	return t
end -- function CA.versions.warning_report(t)

function CA.versions.warning_versions_details(t) -- List all required descriptors of submodules
	local v, d = CA.versions, d or CA.versions.d or {} -- descriptor of submodule
--	local t = "<br>verify_modules_builder</b>:"
	local t = t or "warning_versions_details:"
	--
	t = t .. "<br><br><b>require()</b>: "
	for vers, desc in pairs(CA.versions.requires) do t = t .. CA.ta(desc.alias, vers) end
	--
	t = t .. "<br><br><b>CA.package_loaded_read</b>: " .. CA.package_loaded_read()
	--
	t = t .. "<br><br><b>package.loaded</b>: "
	for pack, modu in pairs(package.loaded) do t = t .. CA.ta(pack, type(modu) ) end -- modu.module_version
	--
	t = t .. "<br><br>* Used modules : " .. CA.ta("check_versions_tab", CA.check_versions_tab ) .. CA.ta("_G", _G )
	t = t .. "<br>* " .. CA.versions.main_versionName .. " : "
	t = t .. CA.ta("versionName", CA.versions.main_versionName )
	t = t .. CA.ta("versionNumber", CA.versions.main_versionNumber )
	t = t .. CA.ta("versionDate", CA.versions.main_versionDate )
--	for title, modu in pairs(_G) do -- check_versions_tab
	for title, modu in pairs(CA.check_versions_tab or _G) do -- check_versions_tab
		if type(modu) == "table" and title ~= "CA_memory" and modu.versions and modu.versions.vn then --
			local vn = modu.versions.vn
		--	versionName = "ControlArgs1", versionNumber = "1.01", versionDate = "2015-09-09",
			t = t .. "<br>* " .. title .. " : "
			t = t .. CA.ta("versionName", vn.versionName )
			t = t .. CA.ta("versionNumber", vn.versionNumber )
			t = t .. CA.ta("versionDate", vn.versionDate )
		end
	end
	--
	t = t .. "<br><br><b>Global _G</b>: "
--	for pack, modu in pairs(_G) do t = t .. CA.ta(pack, type(modu) ) end -- modu.module_version
	for title, modu in pairs(_G) do if type(modu) == "table" then t = t .. CA.ta(title, type(modu) ) end end -- modu.module_version
	--
	t = t .. "<br><br><b>Descriptors</b>: "
	for vers, desc in pairs(v.requires) do -- all
		t = t .. CA.str_vars("<br><b>* desc</b>", desc)
		if desc then
		--	t = t .. CA.ta("vers", vers)
			t = t .. CA.ta("title", desc.title)
			t = t .. CA.ta("version", desc.version)
			t = t .. CA.ta("name", desc.name)
			t = t .. CA.ta("alias", desc.alias)
			t = t .. CA.ta("selector", desc.vn or desc.selector)
			t = t .. CA.ta("missings", desc.missings)
			t = t .. CA.ta("unknowns", desc.unknowns)
			t = t .. CA.ta("normals", desc.normals)
			t = t .. CA.ta("listall", desc.listall)
			t = t .. CA.ta("selectall", desc.selectall)
			t = t .. CA.ta("warnings", desc.warnings)
			t = t .. CA.ta("loaded_list", desc.loaded_list)
			t = t .. CA.ta("excess", desc.excess)
			t = t .. CA.ta("aliases", desc.aliases)
		end
	end
	t = t .. "<br><br><b>Tracks</b>: "
	t = t .. (v.MainModuleInitStateSteps or "")
	return t
end -- function CA.versions.warning_versions_details(t)
--[[
-- List all used submodules versions
function p.all_versions_tests() -- DEBUG ?
	local warn_vrs = ""
	warn_vrs = warn_vrs .. "<br><b><b>Résumé</b></b>"
	warn_vrs = warn_vrs .. "<br>modules_builder recieved this ask : " .. CA.ta(" selector ", versions.vn.selector or versions.selector) .. CA.ta(" all_versions ",	table.concat(versions.all_versions, " >> ") ) .. CA.ta(" selectall ", versions.selectall) .. "."
	warn_vrs = warn_vrs .. "<br>" .. CA.str_vars("all_versions_tests", versions.warning, versions.list, versions.normal) --, versions.unused_versions)
	warn_vrs = warn_vrs .. "<br>" .. versions.tree
	warn_vrs = warn_vrs .. CA.dropdown_func(1, "all_versions table", CA.testable_lister, versions.all_versions, "all_versions", { levelmaxi = 3, } )
	warn_vrs = warn_vrs .. CA.dropdown_func(1, "global_modules table", CA.testable_lister, versions.global_modules, "global_modules", { levelmaxi = 2, } )
	warn_vrs = warn_vrs .. CA.dropdown_func(1, "requires table", CA.testable_lister, versions.requires, "requires", { levelmaxi = 3, } )
	warn_vrs = warn_vrs .. CA.dropdown_func(1, "inits table", CA.testable_lister, versions.inits, "inits", { levelmaxi = 3, exclude1 = "module", } )
	-- { levelmaxi = 2, max_n = 2, exclude1 = "hou" }
	return warn_vrs
end -- function p.all_versions_tests()
--]]
-- p.treeVersions_debug = versions.warning .. "<br><small><small>" .. p.all_versions_text() .. "</small></small><br>" .. versions.tree

-- CA.nowyear = tonumber(os.date("%Y") ) -- local now_date = os.date("%Y-%m-%d %H:%M:%S")
-- CA.nowyear = tonumber(os.date("%Y-%m-%d") ) -- local now_date = os.date("%Y-%m-%d %H:%M:%S")
-- p.nowyear = nowyear

CA.constants = CA.constants or {}
-- Similar words search : diff on length
-- Cerrar de palabras puscadas: diff en longitud
-- Recherche de mots proches: diff sur longueur
CA.constants.near_word_search_diff_coef = 0.30
CA.constants.near_word_search_diff_const = 0.82

------------------------------------------------------------
-- Tables des traductions des paramètres et messages dans les langues : en, es, fr.
-- Tables of translations of settings and messages in the languages : en, es, fr.
-- Mesas traducciones parámetros y mensajes en los idiomas : en, es, fr.
--
-- i18n translations tables to extend and update in calling modules
------------------------------------------------------------

CA.i18n = {} -- translations tables known in the module

CA.i18n.en = {
	-- ControlArgs main texts, errors and categories
	[1]							= "1",
	["1"]						= "1",
	mode						= "mode",
	c							= 'c',
	debug						= 'debug',
	options						= 'options',
	category					= 'Category',
	date_months_names			= "January, February, March, April, May, June, July, August, September, October, November, December",
	userlang					= 'userlang',
	wikilang					= 'wikilang',
	user_wiki_lang				= "Languages: user: %1, wiki: %2.",
	allversions					= "allversions",
	selectversions				= "selectversions",
	DropBox_option_label_Unwrap = "Unwrap/Wrap",

	-- Names and descriptions of arguments
	needed_to_verify			= "(required, to be checked)",
	list_needed_arguments		= "List of needed arguments:",
	list_all_other_arguments	= "List of all other arguments:",
	label						= 'label',
	label_descr					= "Automatic Wikidata argument.",
	entityid					= 'id',
	entityid_descr				= "Identifier of Wikidata data, such as <code>Q535</code> for Victor Hugo.",
	entityid2					= 'entityid',
	entityid2_descr				= "Identifier of Wikidata data, such as <code>Q535</code> for Victor Hugo.",
	sitelink					= 'sitelink',
	sitelink_descr				= "Automatic Wikidata argument.",
	lastname					= 'name',
	lastname_descr				= "Family name. Please specify to correct the sort key.",
	firstname					= 'firstname',
	firstname_descr				= "First name. Please specify to correct the sort key.",
	firstname2_descr			= "First name. Please specify to correct the sort key.",
	initiale					= 'initial',
	initiale_descr				= "Initial to correct the category of authors.",
	title						= 'title',
	title_descr					= "Page title, automatic.",
	birthyear					= 'birthyear',
	birthyear_descr				= "Year of birth.",
	deathyear					= 'deathyear',
	deathyear_descr				= "Year of death.",
	cat_name_number				= "Year %1",
	debug						= "debug",
	language					= 'language',
	country						= 'country',
	language_cat				= 'Speaking %1',
	occupation_cat				= '%1',
	authors_deathyear_cat		= "Authors-%1",
	description					= 'description',
	description_descr			= "Author's description, to clarify whether the automatic description does not fit.",
	flag_of_image				= 'Flag_of_England.svg',

	-- Alegaciones de varios valores verificados
	region						= 'region',
	region_values				= 'other,china,india,century',
	rights						= 'rights',
	rights_descr				= "Necessary copyrights type: 70,50,mpf,non.",
	rights_values				= '70,50,mpf,none',
	args_values_err				= "Abnormal value of the argument '''%1 = %2''' including (%3) ",

	date_to_part_format			= "dd yyyy mmmm",
	date_to_part_call_err		= "Internal Error: Abnormal calling arguments in date '''%1'''.",
	date_to_part_call_cat		= "Module with internal error",
	date_to_part_err_not_found	= "Internal Error: No part found in date '''%1'''.",

	-- Miscellaneous messages and errors
	all_versions_tests			= "Versions warning: <b>%1</b>, normal: <b>%2</b>, listall: <b>%3</b>.",
	all_versions_check			= "Versions: missings: <b>%1</b>, unknowns: <b>%2</b>, normals: <b>%3</b>, excess: <b>%4</b>, all selected: <b>%5</b>.",
	err_nearest_argument		= "Error: Do you need the known argument '''%1''' ?",
--	max_nearest_argument		= 3,
	max_nearest_argument_msg	= "A longer name argument accepts more letter errors.",
	err_value_re_defined		= "Error: The value of the argument '''%1''' is already defined. Choose only one value of a single synonymous.",

	cat_test_OK					= "Test category generation OK",
	cat_usage_error				= "Module with usage error",
	cat_internal_error			= "Module with internal error",
	err_module_with_error		= "Module with error",
	err_error_list_header		= "User support about parameters:",
	err_unknown_argument		= "Error: parameter '''%1''' is unknown in this template. Check the name or flag this gap.",
	err_need_value				= "Error: This argument is required but absent : '''%1'''. Should define it.",
	err_none_value				= "Error: No argument has been defined.",
	err_too_unnamed_arguments	= "Error: This unnamed argument is too many: '''%1''' = '''%2'''.",
	err_internal_notice_wsid	= "Internal Error: Notify the developer that the internal argument '''%1''' is unknown in the records.",
	err_without_translation		= "Known argument, but not translated: '''%1'''.",
	err_without_translation_N	= "There are %1 arguments untranslated.",
	err_is_defined				= "The argument %1:'''%2''' is defined.",
	err_is_undefined			= "The argument %1:'''%2''' is not defined.",
	err_module_with_error		= "Module with error",
	err_wikidata_wikibase		= "Error: Wikibase is not available.",
	err_wikidata_getEntity		= "Error: getEntity Wikidata is not available.",
	err_wikidata_getEntityObject= "Error: Element Wikidata '''%1''' is not found.",
	err_wikidata_property		= "Error: Wikidata property '''%1''' is not found.",
	err_wikidata_error			= "Error Wikidata: '''%1''' ",
--	err_wikidata_cat			= "Error Wikidata",
	err_wikidata_cat			= "Module with internal error",	 -- cat_internal_error
	structured_data_txt			= 'Datas from Wikidata',
	table_listlimit_levelmaxi	= "Level limit levelmaxi = '''%1''' ",
	table_listlimit_max_n		= "Length limit max_n = '''%1''' ",
	err_module_miss_i18n_txt	= "Internal Error: The text '''%1''' lack of translation in '''%2''' language, and/or others.",
	err_module_miss_i18n_count	= "Internal Error: There are '''%1''' missings in '''%2''' translations.",
	err_module_miss_i18n_none	= "OK, none missing translations.",
	err_module_miss_i18n		= "Internal Error: Module missing i18n translation for the argument '''%1''' ",
--	err_module_miss_i18n_cat	= "Module missing i18n translation",
	err_module_miss_i18n_cat	= "Module with internal error",	 -- cat_internal_error
	err_module_miss_i18n_mini	= "Error: I18n table has only '''1%''' translations.",
	i18n_list_all_texts			= "This list show all the texts, but cannot replace the original declarations.",
--	err_no_args_known			= "Error: Module without known arguments table.",
--	err_no_args_source			= "Error: Module without source arguments table.",
	languages_nbr_and_list		= "\n* There are %1 tables of translations in these languages : %2 <br>",
	err_no_wiki_translations	= "Error: Module without translated arguments table.",
	err_no_args_wikidata		= "Error: Module without wikidata arguments table.",
	err_lang_table				= "Error: The '''%1''' language or its table is incorrect.",
--	err_lang_table_cat			= "Module with erroneous language arguments",
	err_lang_table_cat			= "Module with internal error",	 -- cat_internal_error
	err_lang_not_exist			= "Error: The language '''%1''' is not one of wikipedias.",
	err_lang_not_translated		= "Error: The language '''%1''' is not translated.",
	err_argv_args_src			= "Internal Error: in p.argv, arguments table is missing.",
	err_argv_args_name			= "Internal Error: in p.argv, abnormal argument '''%1'''.",
	err_argv_args_miss			= "Internal Error: in p.argv, missing argument '''%1'''.",
	err_generDoc1_paramName		= "Internal Error: in generDoc1, bad argument '''%1'''.",
	sources_of_datas			= "Informations from: /Wikidata, /template or module, /other, /message, /error",
	list_a_translated_language	= "\n* %1 translations in language %2 : %3 : %4",
	table_counts				= "Table '''%1''' : counts tabs='''%2''', vars='''%3''', funcs='''%4'''.",
	table_dont_exists			= "The table '''%1''' does not exist.",

	err_delete_docbox			= "You must remove this documentation before recording.\nRemove all modes to return to normal mode.",
	err_assist_user_param		= "User support for checking the settings:",
--	err_module_error			= "Module with error",
	err_module_with_error		= "Module with error",
	msg_auto_val_warning		= "Verify the automatic argument: %1 = '''%2'''.",
	msg_auto_val_unknown		= "Internal Error: Unknown automatic argument: %1 = '''%2'''.",
	--
	err_no_known_arguments		= "Internal Error: Module without known arguments table.",
--	cat_no_known_arguments		= "Module without known arguments table.",
	cat_no_known_arguments		= "Module with internal error",	 -- cat_internal_error
	err_no_source_arguments		= "Internal Error: Module without source arguments table.",
--	cat_no_source_arguments		= "Module without source arguments table.",
	cat_no_source_arguments		= "Module with internal error",	 -- cat_internal_error

	-- Titles of tests
	page_tests_h3_title			= "Tests of this page",
	page_tests_title			= "Testing and information related to this page:",
	internal_tests_h3_title		= "Internal tests",
	internal_tests_title		= "Internal regression tests:",
	dropdown_missing_title		= "Missing title for this dropbox",
	used_options_title			= "Options and their uses:",
	options_from_mode_title		= "Test options from modes",
	options_from_args_title		= "Test options from arguments",
	spaces_page_names_title		= "Module, namespaces, and page names:",
	tables_count_title			= "Counts of contents in tables",
	list_all_args_title			= "List of all arguments",
	wikidata_any_page_title		= "Wikidata for any page:",
	wikidata_details_test_title = "Tests and imported datas from wikidata",
	wikidata_arbitrary_access_title = "Test of Wikidata arbitrary access",
	wikidata_arbitrary_access_text	= "Wikidata for another Title: ",
	actual_versions_title		= "Actual versions",
	versions_details_title		= "Detailed versions",
	versions_manage_test_title	= "Versions management test",
	date_to_part_test_title		= "Tests of date to day, month, year or era",
	cat_add_test_title			= "Test the creation of categories in some languages",
	levenshtein_test_title		= "Test the distances between words '''[https://en.wikipedia.org/wiki/Levenshtein_distance Levenshtein]''':",
	table_args_source_title		= "Table of received arguments, args_source:",
	table_args_unknown_title	= "Table of unknown arguments, args_unknown:",
	table_args_known_title		= "Table of known arguments, args_known:",
	missing_translations_title	= "Translations missing in i18n tables:",
	similar_args_test_title		= "Test close words similar_args:",
	testable_lister_title		= "List a table, test without limits:",
	testable_limit_title		= "List a table, test with limits:",
	combined_translations_title = "Combined i18n translations tables:",
	dummy_languages_title		= "Language Definitions:",
	time_format_test_title		= "Tests of coding and conversion of dates",

} -- p.i18n.en

p.i18n.es = {
	[1]							= "1",
	["1"]						= "1",
	mode						= "modo",
	c							= 'c',
	debug						= 'debug',
	options						= 'opciones',
	category					= 'Categoría',
	date_months_names			= "Enero, Febrero, Marzo, Abril, Mayo, Junio​​, Julio, Agosto, Septiembre, Octubre, Noviembre, Diciembre",
	userlang					= 'userlang',
	wikilang					= 'wikilang',
	user_wiki_lang				= "Idiomas: usuario: %1, wiki: %2.",
	allversions					= "allversions",
	selectversions				= "selectversions",
	DropBox_option_label_Unwrap = "Desenvolver/Envolver",

	-- Nombres y descripciones de los argumentos
	needed_to_verify			= "(obligatorio, se debe comprobar)",
	list_needed_arguments		= "Lista de argumentos necesarios:",
	list_all_other_arguments	= "Lista de todos los otros argumentos:",
	label						= 'label',
	label_descr					= "Wikidata automática argumento.",
	entityid					= 'id',
	entityid_descr				= "Nombre de los datos Wikidata, como <code>Q535</code> para Victor Hugo.",
	entityid2					= 'entityid',
	entityid2_descr				= "Nombre de los datos Wikidata, como <code>Q535</code> para Victor Hugo.",
	sitelink					= 'sitelink',
	sitelink_descr				= "Wikidata automática argumento.",
	lastname					= 'nombre',
	lastname_descr				= "Nombre. Por favor, especifique para corregir la clave de ordenación.",
	firstname					= 'apellido',
	firstname_descr				= "Primero. Proponer para corregir la autómata.",
	firstname2_descr			= "Primero. Proponer para corregir la autómata.",
	initiale					= 'inicial',
	initiale_descr				= "Inicial para corregir la categoría de los autores.",
	title						= 'titulo',
	title_descr					= "Título de la página, automático.",
	birthyear					= 'nacimiento',
	birthyear_descr				= "Año de nacimiento.",
	deathyear					= 'muerte',
	deathyear_descr				= "Año de la muerte.",
	cat_name_number				= "Año %1",
	debug						= "debug",
	language					= 'lenguaje',
	country						= 'país',
	language_cat				= 'Hablando %1',
	occupation_cat				= '%1',
	authors_deathyear_cat		= "Autores-%1",
	description					= 'descripcion',
	description_descr			= "Descripción del autor, para aclarar si la descripción automática no encaja.",
	flag_of_image				= 'Flag_of_Spain.svg',

	-- Alegaciones de varios valores verificados
	region						= 'región',
	region_values				= "otro,china,india,siglo",
	rights						= 'derechos',
	rights_descr				= "Tipo de derechos de autor necesario: 70,50,mpf,non.",
	rights_values				= '70,50,mpf,no',
	args_values_err				= "Valor anormal del argumento '''%1 = %2''' a partir de: (%3) ",

	date_to_part_format			= "dd mmmm yyyy",
	date_to_part_call_err		= "Error interno: argumentos de llamadas anómala en fecha '''%1'''.",
	date_to_part_call_cat		= "Módulo con error interno",
	date_to_part_err_not_found	= "Error interno: No se encuentra en fecha '''%1'''.",

	-- Diversos mensajes y errores
	all_versions_tests			= "Versiones aviso: <b>%1</b>, normal: <b>%2</b>, lista: <b>%3</b>.",
	all_versions_check			= "Versiones: falta: <b>%1</b>, incógnitas: <b>%2</b>, normales: <b>%3</b>, en exceso: <b>%4</b>, selecciona todo: <b>%5</b>.",
	err_nearest_argument		= "Error : ¿Es usted conocido argumento '''%1''' ?",
--	max_nearest_argument		= 3,
	max_nearest_argument_msg	= "Un argumento nombre más largo acepta más letras errores.",
	err_value_re_defined		= "Error: El valor del argumento '''%1''' ya está definido. Elija sólo un valor de un solo sinónimo",

	cat_test_OK					= "Prueba generación categoría en OK",
	cat_usage_error				= "Módulo con error del uso",
	cat_internal_error			= "Módulo con error interno",
	err_module_with_error		= "Módulo con error",
	err_too_unnamed_arguments	= "Error: Demasiado argumento sin nombre: '''%1''' = '''%2'''.",
	languages_nbr_and_list		= "\n* Hay %1 mesas de traducciones en estas idiomas: %2 <br>",
	structured_data_txt			= 'Datos de Wikidata',
	err_unknown_argument		= "Error: El parámetro '''%1''' es desconocido en este modelo. Compruebe el nombre o marca esta brecha.",
	err_wikidata_getEntityObject= "Error: Elemento <b>%1</b> de Wikidata no se encuentra.",

	args_values_err				= "Valor anormal del argumento '''%1 = %2''' a partir de: (%3) ",
	language_cat				= 'Habla %1',
	occupation_cat				= '%1',
	sources_of_datas			= "Informacións de: /Wikidata, /modelo o módulo, /otros, /mensaje, /error",
	list_a_translated_language	= "\n* %1 traducciones en idioma %2 : %3 : %4",
	err_module_miss_i18n_none	= "OK, ninguno traducciones faltan.",
	err_module_miss_i18n		= "Error interno: falta de traducción para el argumento '''%1''' ",
--	err_module_miss_i18n_cat	= "Módulo falta de traducción i18n",
	err_module_miss_i18n_cat	= "Módulo con error interno", -- cat_internal_error
	table_counts				= "Tabla <b>%1</b> : cuentas tabs=<b>%2</b>, vars=<b>%3</b>, funcs=<b>%4</b>.",
	table_dont_exists			= "La tabla '''%1''' no existe.",

	err_delete_docbox			= "Debe quitar esta documentación antes de grabar.\nRetire todos los modos para volver al modo normal.",
	err_assist_user_param		= "Soporte al usuario para comprobar la configuración:",
	i18n_list_all_texts			= "Esta lista muestra todos los textos, pero no puede sustituir a las declaraciones originales.",
--	err_module_error			= "Módulo con error.",
	msg_auto_val_warning		= "Verifique el argumento automático: %1 = <b>%2</b>.",
	msg_auto_val_unknown		= "Error interno: Argumento desconocido automático: %1 = <b>%2</b>.",
	--
	err_no_known_arguments		= "Error interno: Módulo sin tabla de argumentos conocidos.",
	cat_no_known_arguments		= "Módulo con error interno", -- cat_internal_error
	err_no_source_arguments		= "Error interno: Módulo sin tabla de argumentos fuentes.",
	cat_no_source_arguments		= "Módulo con error interno", -- cat_internal_error

	-- Titres des pruebas
	page_tests_h3_title			= "Pruebas de esta página",
	page_tests_title			= "Pruebas y información relacionada con esta página:",
	internal_tests_h3_title		= "Pruebas internas",
	internal_tests_title		= "Pruebas de no regresión internas:",
	dropdown_missing_title		= "Falta el título para este dropbox",
	used_options_title			= "Opciones y sus usos:",
	options_from_mode_title		= "Prueba de opciones de modos",
	options_from_args_title		= "Prueba de opciones de argumentos",
	spaces_page_names_title		= "Módulo, namespaces, y nombres de páginas:",
	tables_count_title			= "Condes des contenidos en tablas",
	list_all_args_title			= "Lista de todos los argumentos",
	wikidata_any_page_title		= "Wikidata para cualquier página:",
	wikidata_details_test_title = "Pruebas y datos importados de wikidata",
	wikidata_arbitrary_access_title = "Prueba de acceso independiente Wikidata",
	wikidata_arbitrary_access_text	= "Wikidata por otro título: ",
	actual_versions_title		= "Versiones efectivas",
	versions_details_title		= "Versiones detalladas",
	versions_manage_test_title	= "Prueba de la gestión de versiones",
	date_to_part_test_title		= "Pruebas de fecha a día, mes, año o época",
	cat_add_test_title			= "Pruebe la creación de categorías en algunos idiomas",
	levenshtein_test_title		= "Prueba de las distancias entre las palabras '''[https://es.wikipedia.org/wiki/Distancia_de_Levenshtein Levenshtein]''':",
	table_args_source_title		= "Tabla de argumentos recibido, args_source:",
	table_args_unknown_title	= "Tabla de argumentos desconocidos, args_unknown:",
	table_args_known_title		= "Tabla de argumentos conocidos, args_known:",
	missing_translations_title	= "Traducciones que faltan en las tablas:",
	similar_args_test_title		= "Prueba de palabras cercanos similar_args:",
	testable_lister_title		= "Enumerar una tabla, prueba sin límites:",
	testable_limit_title		= "Enumerar una tabla, prueba con límites:",
	combined_translations_title = "Tablas traducciones i18n combinadas:",
	dummy_languages_title		= "Definiciones de idiomas:",
	time_format_test_title		= "Pruebas de codificación y conversión fechas",

} -- p.i18n.es

p.i18n.fr = {
	-- Principaux textes, erreurs et catégories de ControlArgs
	[1]							= "1",
	["1"]						= "1",
	mode						= "mode",
	c							= 'c',
	debug						= 'debug',
	options						= 'options',
	category					= 'Catégorie',
	date_months_names			= "Janvier, Février, Mars, Avril, Mai, Juin, Juillet, Août, Septembre, Octobre, Novembre, Décembre",
	userlang					= 'userlang',
	wikilang					= 'wikilang',
	user_wiki_lang				= "Langues : utilisateur : %1, wiki : %2.",
	allversions					= "allversions",
	selectversions				= "selectversions",
	DropBox_option_label_Unwrap = "Dérouler/Enrouler",

	-- Noms et descriptions des arguments
	needed_to_verify			= "(obligatoire, à vérifier)",
	list_needed_arguments		= "Liste des arguments nécessaires :",
	list_all_other_arguments	= "Liste de tous les autres arguments :",
	label						= 'label',
	label_descr					= "Argument automatique de Wikidata.",
	entityid					= 'id',
	entityid_descr				= "Identifiant des données de wikidata, comme <code>Q535</code> pour Victor Hugo.",
	entityid2					= 'entityid',
	entityid2_descr				= "Identifiant des données de wikidata, comme <code>Q535</code> pour Victor Hugo.",
	sitelink					= 'sitelink',
	sitelink_descr				= "Argument automatique de Wikidata.",
	lastname					= 'nom',
	lastname_descr				= "Nom. A préciser pour corriger la clé de tri.",
	firstname					= 'prénom',
	firstname_descr				= "Prénom. A préciser pour corriger la clé de tri.",
	firstname2					= 'prenom',
	firstname2_descr			= "Prénom. A préciser pour corriger la clé de tri.",
	initiale					= 'initiale',
	initiale_descr				= "Initiale pour corriger les catégories d'ateurs.",
	title						= 'titre',
	title_descr					= "Titre de la page, automatique.",
	birthyear					= 'anneeNaissance',
	birthyear_descr				= "Année de naissance",
	deathyear					= 'anneeDeces',
	deathyear_descr				= "Année de décès",
	rights						= 'droits',
	rights_descr				= "Type de droits d'auteur nécessaire parmi : 70,50,mpf,non.",
	language					= 'langue',
	language_cat				= 'Parle %1',
	occupation_cat				= '%1',
	authors_deathyear_cat		= "Auteurs-%1",
	cat_name_number				= "Année %1",
	debug						= "debug",
	country						= 'pays',
	description					= 'description',
	description_descr			= "Description de l'auteur, à préciser si la description automatique ne convient pas",
	flag_of_image				= 'Flag_of_France.svg',

	-- Arguments à valeurs multiples vérifiées
	region						= 'région',
	region_values				= "autre,chine,inde,siècle",
	rights						= 'droits',
	rights_values				= '70,50,mpf,non',
	args_values_err				= "Valeur anormale de l'argument '''%1 = %2''' parmi : (%3) ",

	date_to_part_format			= "dd yyyy mmmm",
	date_to_part_call_err		= "Erreur interne : Argument anormal de définition de date '''%1'''.",
	date_to_part_call_cat		= "Module avec erreur interne",
	date_to_part_err_not_found	= "Erreur interne : partie non définie de date '''%1'''.",

	-- Messages et erreurs divers
	all_versions_tests			= "Versions avertissements : <b>%1</b>, normaux : <b>%2</b>, liste : <b>%3</b>.",
	all_versions_check			= "Versions: manquantes: <b>%1</b>, inconnues: <b>%2</b>, normales: <b>%3</b>, en excès: <b>%4</b>, toutes selectionées : <b>%5</b>.",
	err_nearest_argument		= "Erreur : Voulez vous l'argument connu <b>%1</b> ?",
--	max_nearest_argument		= 3,
	max_nearest_argument_msg	= "Un nom d'argument plus long accepte plus d'erreurs de lettres.",
	err_value_re_defined		= "Erreur : La valeur de l'argument '''%1''' est déjà définie. XXX Choisir une seule valeur d'un seul synonyme.",
	cat_test_OK					= "Test de génération de catégorie OK",
	cat_usage_error				= "Module avec erreur d'utilisation",
	cat_internal_error			= "Module avec erreur interne",
	err_error_list_header		= "Assistance sur les paramètres de ce modèle :",
	err_unknown_argument		= "Erreur : Le paramètre '''%1''' est inconnu dans ce modèle. Vérifier ce nom ou signaler ce manque.",
	err_need_value				= "Erreur : Vous devez définir cet argument nécessaire mais absent : '''%1'''.",
	err_none_value				= "Erreur : Aucun argument n'a été défini.",
	err_too_unnamed_arguments	= "Erreur : Ce paramètre non nommé est en trop : '''%1''' = '''%2'''.",
	err_internal_notice_wsid	= "Erreur interne : Signaler au développeur que l'argument interne '''%1''' est inconnu dans les notices.",
	err_without_translation		= "Argument connu, mais non traduit : '''%1'''.",
	err_without_translation_N	= "Il y a %1 arguments non traduits.",
	err_is_defined				= "L'argument %1:'''%2''' est défini.",
	err_is_undefined			= "L'argument %1:'''%2''' n'est pas défini.",
	err_module_with_error		= "Module avec erreur",
	err_wikidata_wikibase		= "Erreur : Wikibase n'est pas disponible.",
	err_wikidata_getEntity		= "Erreur : getEntity Wikidata n'est pas disponible.",
	err_wikidata_getEntityObject= "Erreur : L'élément '''%1''' de Wikidata n'est pas trouvé.",
	err_wikidata_property		= "Erreur : La propriété '''%1''' de Wikidata n'est pas trouvée.",
	err_wikidata_error			= "Erreur Wikidata : '''%1''' ",
	err_wikidata_cat			= "Erreur Wikidata",
	structured_data_txt			= 'Données de Wikidata.',
	table_listlimit_levelmaxi	= "Limite de niveau levelmaxi = '''%1''' ",
	table_listlimit_max_n		= "Limite de longueur max_n = '''%1''' ",
	err_module_miss_i18n_txt	= "Erreur interne : Le texte '''%1''' manque de traduction en langue '''%2''', et/ou d'autres.",
	err_module_miss_i18n_count	= "Erreur interne : Il y a '''%1''' manques parmi '''%2''' traductions.",
	err_module_miss_i18n_none	= "OK, aucune traduction manquante.",
	err_module_miss_i18n		= "Erreur interne : manque de traduction pour l'argument '''%1''' ",
--	err_module_miss_i18n_cat	= "Module manquant de traduction i18n",
	err_module_miss_i18n_cat	= "Module avec erreur interne", -- cat_internal_error
	err_module_miss_i18n_mini	= "Erreur interne : La table i18n n'a que '''%1''' traductions.",
--	err_no_args_known			= "Erreur interne : Module sans table d'arguments connus.",
--	err_no_args_source			= "Erreur interne : Module sans table d'arguments sources.",
	languages_nbr_and_list		= "\n* Il y a %1 tables de traductions dans ces langues : %2 <br>",
	err_no_wiki_translations	= "Erreur interne : Module sans table d'arguments traduits.",
	err_no_args_wikidata		= "Erreur interne : Module sans table d'arguments wikidata.",
	err_lang_table				= "Erreur interne : La langue '''%1''' ou sa table est erronée.",
--	err_lang_table_cat			= "Module avec langue d'arguments erronée",
	err_lang_table_cat			= "Module avec erreur interne", -- cat_internal_error
	err_lang_not_exist			= "Erreur : La langue '''%1''' n'est pas une des langues des wikipedias.",
	err_lang_not_translated		= "Erreur : La langue '''%1''' n'est pas traduite.",
	err_argv_args_src			= "Erreur interne : en p.argv, table d'arguments absente.",
	err_argv_args_name			= "Erreur interne : en p.argv, argument anormal '''%1'''.",
	err_argv_args_miss			= "Erreur interne : en p.argv, argument absent '''%1'''.",
	err_generDoc1_paramName		= "Erreur interne : en generDoc1, mauvais argument '''%1'''.",
	sources_of_datas			= "Informations de : /Wikidata, /modèle ou module, /autres, /message, /erreur",
	list_a_translated_language	= "\n* %1 traductions en langage %2 : %3 : %4",
	table_counts				= "Table '''%1''' : comptes tabs='''%2''', vars='''%3''', funcs='''%4'''.",
	table_dont_exists			= "La table '''%1''' n'existe pas.",

	err_delete_docbox			= "Vous devez supprimer cette documentation avant d'enregistrer.\nSupprimez tous les modes pour revenir en mode normal.",
	err_assist_user_param		= "Support aux utilisateurs pour vérifier les paramètres :",
	i18n_list_all_texts			= "Cette liste montre tous les textes, mais ne peut remplacer les déclarations originales.",
--	err_module_error			= "Module avec erreur.",
	msg_auto_val_warning		= "Vérifiez l'argument automatique : %1 = <b>%2</b>.",
	msg_auto_val_unknown		= "Erreur interne: Argument automatique inconnu : %1 = <b>%2</b>.",
	--
	err_no_known_arguments		= "Erreur interne : Module sans table d'arguments connus.",
--	cat_no_known_arguments		= "Module sans table d'arguments connus.",
	cat_no_known_arguments		= "Module avec erreur interne", -- cat_internal_error
	err_no_source_arguments		= "Erreur interne : Module sans table d'arguments sources.",
--	cat_no_source_arguments		= "Module sans table d'arguments sources.",
	cat_no_source_arguments		= "Module avec erreur interne", -- cat_internal_error

	-- Titres des tests
	page_tests_h3_title			= "Tests de cette page",
	page_tests_title			= "Tests et informations liées à cette page :",
	internal_tests_h3_title		= "Tests internes",
	internal_tests_title		= "Tests internes de non régression :",
	dropdown_missing_title		= "Titre manquant pour cette boite déroulante",
	used_options_title			= "Options et leurs utilisations :",
	options_from_mode_title		= "Test des options de modes",
	options_from_args_title		= "Test des options des arguments",
	spaces_page_names_title		= "Module, namespaces, et noms de pages :",
	tables_count_title			= "Comptages des contenus de tables",
	list_all_args_title			= "Liste de tous les arguments",
	wikidata_any_page_title		= "Wikidata pour une autre page :",
	wikidata_details_test_title = "Tests et données importées de wikidata",
	wikidata_arbitrary_access_title = "Test de Wikidata pour une autre page",
	wikidata_arbitrary_access_text	= "Wikidata pour une autre page: ",
	actual_versions_title		= "Versions effectives",
	versions_details_title		= "Versions détaillées",
	versions_manage_test_title	= "Test de la gestion des versions",
	date_to_part_test_title		= "Tests de date vers jour, mois, année ou ère",
	cat_add_test_title			= "Tester la création des catégories dans quelques langues",
	levenshtein_test_title		= "Test des distances entre mots de '''[https://fr.wikipedia.org/wiki/Distance_de_Levenshtein Levenshtein]''':",
	table_args_source_title		= "Table des arguments reçus, args_source :",
	table_args_unknown_title	= "Table des arguments inconnus, args_unknown :",
	table_args_known_title		= "Table des arguments connus, args_known :",
	missing_translations_title	= "Manques de traductions en tables i18n :",
	similar_args_test_title		= "Test des mots proches similar_args :",
	testable_lister_title		= "Lister une table, test sans limites :",
	testable_limit_title		= "Lister une table, test avec limites :",
	combined_translations_title = "Tables de traductions i18n combinées :",
	dummy_languages_title		= "Définitions de langues :",
	time_format_test_title		= "Tests de codage et conversions de dates",

} -- p.i18n.fr

------------------------------------------------------------
-- p.i18n tables end
------------------------------------------------------------

------------------------------------------------------------
-- Arguments table, to change in calling modules
------------------------------------------------------------

p.args_known_default = {

	-- Arguments in order without names, with their keyword for use as other arguments.
	-- Arguments dans l'ordre, non nommés, avec leur keyword pour traitement comme les autres arguments.

	[1] =			{need = 0, list = 1, syn = 2,
		keyword = "mode"},

	-- Special arguments to modify the fonctions and outputs of this module.
	-- Arguments speciaux pour modifier le fonctionnement et les sorties de ce module.

	mode =			{typ = "prg", need = 0, list = 1,
		keyword = "mode"},

	c =				{typ = "dat", need = 0, list = 1,
		keyword = "c"},

	options =		{typ = "dat", need = 0, list = 1,
		keyword = "options"},

	-- The userlang argument permits at an administrator in his own langage (errors, messages, catégories, tests) to help a wiki in any language.
	-- El userlang argumento permisos en administrador en su propia langage (errores, mensajes, categorías, pruebas) para ayudar a un wiki en cualquier idioma.
	-- L'argument userlang permet à un administrateur dans son propre langage (erreurs, messages, catégories, tests) d'aider un wiki dans ne importe quelle langue.
	userlang =		{typ = "dat", need = 0, list = 1,
		keyword = "userlang"},

	-- The wikilang argument permits to verify in one unique wiki that a module can well adapt itself to any wiki language.
	-- Los argumento wikilang permisos para verificar en una wiki único que un módulo puede así adaptarse a cualquier idioma de wiki.
	-- L'argument wikilang permet de vérifier dans un wiki unique qu'un module peut s'adapter à n'importe quelle langue de wiki.
	wikilang =		{typ = "dat", need = 0, list = 1,
		keyword = "wikilang"},

	allversions =	{typ = "dat", need = 0, list = 1,
		keyword = "allversions"},

	selectversions = {typ = "dat", need = 0, list = 1,
		keyword = "selectversions"},

	debug =			{typ = "opt", need = 0, list = 1,
		keyword = "debug"},

	category =		{typ = "ctr", need = 0, list = 3,
		keyword = "category"},

	-- All arguments have a keyword identical to the registration name, except synonyms.
	-- Tous les arguments ont un keyword identique au nom d'enregistrement, sauf les synonymes.

	label =			{typ = "dat", need = 0, list = 2,
		keyword = "label" , prop = "label"},

	country =		{typ = "dat", need = 0, list = 3,
		keyword = "country", prop = "P27", },

	sitelink =		{typ = "dat", need = 0, list = 2,
		keyword = "sitelink" , prop = "sitelink"},

	entityid =		{typ = "dat", need = 2, list = 2,
		keyword = "entityid" , prop = "entityid"},

	entityid2 =		{typ = "dat", need = 2, list = 2, syn = 2,
		keyword = "entityid" , prop = "entityid"},

	lastname =		{typ = "dat", need = 0, list = 2,
		keyword = "lastname"},

	lastname2 =		{typ = "dat", need = 0, list = 2, syn = 2,
		keyword = "lastname"},

	firstname =		{typ = "dat", need = 0, list = 2,
		keyword = "firstname"},

	firstname2 =	{typ = "dat", need = 0, list = 2, syn = 2,
		keyword = "firstname"},

	initiale =		{typ = "dat", need = 2, list = 2,
		keyword = "initiale"},

	title =			{typ = "dat", need = 2, list = 2,
		keyword = "title"},

	birthyear =		{typ = "dat", need = 0, list = 2,
		keyword = "birthyear", prop = "P569", format = "year"},

	deathyear =		{typ = "dat", need = 0, list = 2,
		keyword = "deathyear", prop = "P570", format = "year"},

	description =	{typ = "dat", need = 0, list = 2,
		keyword = "description", prop = "description"},

} -- p.args_known_default

function CA.list_all_args(t, args_known) -- list_all_args_title = List of all arguments
	local t = t or "<br>* <b>List_all_args :</b> "
	if type(args_known) ~= "table" then args_known = CA.args_known end
	local descr, description = "", ""
	local args = mw.clone(args_known)
	local arglst = {}
	for key, elem in pairs(args) do
		elem.key = tostring(key)
		descr = key .. "_descr" -- key for description of an argument
		elem.description = CA.user_translations[descr] or "**missing translation**"
		elem.user_lang_key = CA.user_translations[elem.key] or "**missing translation**"
		elem.user_lang_keyword = CA.user_translations[elem.keyword] or "**missing translation**"
		elem.lev_arg_txt = ""
		local lst_lev = {}
		local lev_min = 99
		for arg_lev, elem_lev in pairs(args_known) do
			elem_lev.levenshtein = CA.levenshtein( CA.wiki_translations[arg_lev], CA.wiki_translations[key] ) or 99
			if elem_lev.levenshtein < lev_min then
				lev_min = elem_lev.levenshtein
				elem.levenshtein = elem_lev.levenshtein
				elem.lev_min = lev_min
				elem.arg_lev = arg_lev
				elem.lev_lang = CA.wiki_translations[arg_lev] or arg_lev
			end
			elem.arg_lev = arg_lev
			elem.lev_arg_txt = CA.ta("lev", tostring(elem.lev_lang) .. ":" .. tostring(elem.lev_min) .. ":" .. elem.arg_lev )
		--	CA.err_add("err_too_unnamed_arguments", key_N, val_src .. " LLL lev=" .. tostring(arglst[1].levenshtein) )
		--	p.cat_add("cat_usage_error")
		end
		table.insert(arglst, elem)
	end -- insert in the arguments their own key
	table.sort(arglst, function (a, b) return (a.user_lang_key < b.user_lang_key) end )
	local gr_syn, gr_need, gr_other = {}, {}, {}
	for i, elem in ipairs(arglst) do -- group arguments in some groups
		if elem.need == 1 or elem.need == 2 then table.insert(gr_need, elem)
		else table.insert(gr_other, elem) end
	end
	local needed = CA.small_caps_style(CA.str_vars("needed_to_verify"))
	local function list_group( group, needed )
		needed = needed or ""
		local t = ""
		for key, elem in pairs(group) do
			if elem.syn
			then t = t .. "<br>* <b>" .. tostring(elem.user_lang_key) .. "</b> => <b>" .. elem.user_lang_keyword .. "</b> : " .. needed .. " " .. tostring(elem.description) .. elem.lev_arg_txt
			else t = t .. "<br>* <b>" .. tostring(elem.user_lang_key) .. "</b> : " .. needed .. " " .. tostring(elem.description) .. elem.lev_arg_txt end
		end
		return t
	end
	t = t .. "<br><br>* <b>" .. CA.str_vars("list_needed_arguments") .. "</b> " .. list_group( gr_need, needed )
	t = t .. "<br><br>* <b>" .. CA.str_vars("list_all_other_arguments") .. "</b> " .. list_group( gr_other )
	return t
end -- function CA.list_all_args(t, args_known) -- list_all_args_title

-- relire ControlArgs/Auteur : module code in english

------------------------------------------------------------
-- Main tables of arguments. Principales tablas de argumentos. Principales tables d'arguments.
------------------------------------------------------------

p.args_source_example = { "Hugo", "Victor", "arg3", c = ' docdef docview docsrc erron ', nom = 'Voltaire', nomm = 'Voltaire', anneenaissance = '1987', BNF = '123456789' }

CA.wiki_lang = tostring(mw.language.getContentLanguage().code) -- "fr" -- mw.language.getContentLanguage().code -- language of usual wiki for args and categories
CA.wiki_translations = {} -- = p.i18n[p.wiki_lang] -- translations in the wiki language of identifiers, errors and messages
CA.user_lang = tostring(mw.language.getContentLanguage().code) -- "fr" -- user language for errors and messages
CA.user_translations = {} -- translations in the user language of identifiers, errors and messages
p.msgs_list = CA.user_translations -- = p.i18n[p.user_lang] -- translations in the user language of identifiers, errors and messages
--	https://www.mediawiki.org/wiki/Extension:UniversalLanguageSelector
--	Flexible and easy way to select a language from a large set of languages.

p.args_known = nil -- Table of the definitions of all known arguments at module level.
p.args_wikidata = nil -- Table of present arguments values from wikidata
p.args_source = nil -- Table of source arguments from calling template, based on argument names in wiki language
p.args_unknown = nil -- unknown arguments are source arguments without known arguments.
p.args_import = nil -- Table of values of all imported arguments, including wikidata, based on international english keys
p.args_final = nil -- Table of values of arguments after interactions between them
-- p.max_nearest_argument = 3 -- Limit of differences of arguments proposed to the user
CA.args_wikidata_import = nil -- Table of the first complete import from wikidata
CA.args_wikidata_selected = nil -- Table of imported arguments from wikidata after any selection

p.categories_list = {} -- Table to collect all categories to generate in wikitext
CA.catView = "" -- = ":" to document a category rather than truly categorize it
p.category_space = mw.site.namespaces.Category.name -- translated Category namespace

function p.gener_categories(args_final) -- Produire les catégories sans les activer
	if type(args_final) ~= "table" then args_final = CA.args_final end
	if args_final.deathyear then
		CA.cat_add("authors_deathyear_cat", args_final.deathyear)
	end
	return
end -- function p.gener_categories(args_final)

------------------------------------------------------------
-- Miscelanous functions. Fonctions utilitaires.
------------------------------------------------------------

-- A faire ? to detect create mode : mw.title.getContent(): Returns the (unparsed) content of the page, or nil if there is no page. The page will be recorded as a transclusion.
-- mw.title.getCurrentTitle() Returns the title object for the current page.
function p.get_edit_state(t) -- iterator to use all elements of a table, one by one
	-- for noticeN in p.table_iterator(notices_properties) do
	-- modele : function list_iter (t)
	if type(t) ~= "table" then return {}, 0 ,0 end
	local i = 0
	local n = table.getn(t)
	return function ()
		i = i + 1
		n = table.getn(t)
		if t[i] ~= nil then return t[i], i ,n end
	end
end -- function p.get_edit_state(t)

function p.table_iterator(t) -- iterator to use all elements of a table, one by one
	-- for noticeN in p.table_iterator(notices_properties) do
	-- modele : function list_iter (t)
	if type(t) ~= "table" then return {}, 0 ,0 end
	local i = 0
	local n = table.getn(t)
	return function ()
		i = i + 1
		n = table.getn(t)
		if t[i] ~= nil then return t[i], i ,n end
	end
end -- function p.table_iterator(t)

function p.spaces_page_names_test(t)
	if type(t) ~= "string" then t = nil end
	local t = t or "\n* '''spaces_page_names_test''' :"
	local mwtitle = mw.title.getCurrentTitle()
	t = t .. p.ta("mwtitle", mwtitle)
	local nsText = mwtitle.nsText
	t = t .. p.ta("nsText", nsText)
	local baseText = mwtitle.baseText -- namespace for the page
	t = t .. p.ta("baseText", baseText)
	local url = tostring(mwtitle:canonicalUrl( ))
	t = t .. p.ta("url", url)
	--
	t = t .. "\n* Module namespace : "
	local ns = mw.site.namespaces
	if ns and ns[828] then
		t = t .. p.ta("id828", ns[828].id)
		t = t .. p.ta("canonicalName828", ns[828].canonicalName)
		t = t .. p.ta("name828", ns[828].name)
		t = t .. p.ta("displayName828", ns[828].displayName)
	end
	t = t .. "\n* All namespaces : "
	local ns = ""
	for ins = 0, 2000 do
	--	if mw.site.contentNamespaces[ins] then
		if mw.site.namespaces[ins] then
			ns = mw.site.namespaces[ins].canonicalName
			t = t .. CA.ta(ns, ins)
		end
	end
	return t
end -- function p.spaces_page_names_test()

-- Some usual fonctional styles for this module and those using it.
-- Quelques styles fonctionnels banals dans ce module et ses modules appelants.
function p.error_color(t)
	return '<span style="color:red;" >' .. tostring(t) .. '</span>'
end

function p.message_color(t)
	return '<span style="color:blue;" >' .. tostring(t) .. '</span>'
end

function p.wikidata_color(t)
	return '<span style="color:green;" >' .. tostring(t) .. '</span>'
end

function p.invoke_color(t)
	return '<span style="color:#804020;" >' .. tostring(t) .. '</span>'
end

function p.other_color(t)
	return '<span style="color:black;" >' .. tostring(t) .. '</span>'
end

function p.discreet_color(t)
	return '<span style="color:#B0B0B0;" >' .. tostring(t) .. '</span>'
end

function CA.small_caps_style(t) -- Display a text in small-caps style.
	return '<span style="font-variant: small-caps">' .. t .. '</span>'
end

function p.isDef(x) -- x is defined or no. x est défini ou non
	return (type(x) == "string") and (x ~= "") and (x ~= " ") and (x ~= "nil")
end

function CA.tab_fields(tab) -- List the fields of a table
	if type(tab) ~= "table" then return tostring(tab) end
	local t = ""
	for key, val in pairs(tab) do
		t = t .. key .. ", "
	end
	t = string.sub( t, 1, -3 )
	return t
end -- function CA.tab_fields(tab)

function CA.tab_pairs(tab) -- List a pairs table: Lister une table de pairs :"
	local t = ""
	for key, val in pairs(tab) do -- Pour tous les mots
		t = t .. tostring(key) .. " = '''" .. tostring(val) .. "''', "
	end
	t = string.sub( t, 1, -3 )
	return t
end -- function CA.tab_pairs(tab)

-- Build a table, with headers for columns. Example:
-- ! style="text-align:left;"| Item
-- {| class="wikitable" style="text-align: center; color: green;"
-- {| border="1" style="border-collapse:collapse"
-- {| class="wikitable sortable" border="1" mw-collapsible mw-collapsed"
-- |} end of table
function CA.Th(t) return '\n{| class="wikitable alternative center" | ' .. tostring(t or " ") end -- Table header
function CA.Tc(t) return '\n! scope="col" | ' .. tostring(t or " ") end -- Table row
function CA.Tr(t) return "\n|- " .. tostring(t or " ") end -- Table row
function CA.Td(t) return "\n| " .. tostring(t or " ") end -- Table data
function CA.Te(t) return "\n|}" end -- Table end

function p.string_vars(translations, ref, v1, v2, v3, v4, v5)
	-- replace %1 to %5 by v1 to v5 in the user translation of ref, else in ref
	ref = tostring(ref)
	local wt = " stv "
	if translations and translations[ref] then
		wt = tostring(translations[ref]) -- Utiliser ref comme reference du texte
	elseif ref then
		wt = tostring(ref) -- Sinon utiliser ref comme le texte lui-même.
	else
		wt = tostring("noref") -- Sinon utiliser ref comme le texte lui-même.
	end
	v1 = v1 or " 1"
	v2 = v2 or " 2"
	v3 = v3 or " 3"
	v4 = v4 or " 4"
	v5 = v5 or " 5"
	if v1 then wt = string.gsub(wt, "%%1", tostring(v1) ) end
	if v2 then wt = string.gsub(wt, "%%2", tostring(v2) ) end
	if v3 then wt = string.gsub(wt, "%%3", tostring(v3) ) end
	if v4 then wt = string.gsub(wt, "%%4", tostring(v4) ) end
	if v5 then wt = string.gsub(wt, "%%5", tostring(v5) ) end
	return tostring(wt)
end -- function p.string_vars(translations, ref, v1, v2, v3, v4, v5)

function CA.str_vars(ref, v1, v2, v3, v4, v5)
	-- replace %1 to %5 by v1 to v5 in the user translation of ref, else in ref
	return p.string_vars(CA.user_translations, ref, v1, v2, v3, v4, v5)
end -- function CA.str_vars(ref, v1, v2, v3, v4, v5)

function CA.wiki_vars(ref, v1, v2, v3, v4, v5)
	-- replace %1 to %5 by v1 to v5 in the wiki translation of ref, else in ref
	return p.string_vars(CA.wiki_translations, ref, v1, v2, v3, v4, v5)
end -- function CA.wiki_vars(ref, v1, v2, v3, v4, v5)

function p.str_STR_N(wt_ref, v1, v2, v3)
	-- replace STR_1 to STR_3 by v1 to v3 in the translation of wt_ref, else in wt_ref
	wt_ref = tostring(wt_ref)
	local wt = ""
	if CA.wiki_translations and CA.wiki_translations[wt_ref]
		then wt = tostring(CA.wiki_translations[wt_ref]) -- Utiliser wt_ref comme la reference de texte traduit.
		else wt = tostring(wt_ref) end -- Sinon utiliser wt_ref comme le texte lui-même.
	if v1 then wt = string.gsub(wt, "STR_1", tostring(v1) ) end -- REF_1
	if v2 then wt = string.gsub(wt, "STR_2", tostring(v2) ) end
	if v3 then wt = string.gsub(wt, "STR_3", tostring(v3) ) end
	return wt
end -- function p.str_STR_N(wt_ref, v1, v2, v3)

-- To do debug : Dropbox plus souple a traduire et parametrer par "style" une table de parametres nommés.

function CA.DropBox(title, content, boxstyle) -- alignT, image, alignB, margin_bottom, width, border_radius, border_color, background_color)
	-- Form a drop box. Formar un cuadro desplegable. Former une boite déroulante.
	local s = { -- options for style and other
		title = CA.str_vars(title or "dropdown_missing_title"),
		content = content,
		image = nil,
		alignT = alignT or "left", -- align
		alignB = alignB or "left", -- align
		margin_all = margin_all or "0px", -- margin
		margin_bottom = margin_bottom or "1em", -- margin-bottom
		width = width or "99%", -- width
		border_radius = border_radius or "0", -- border-radius
		text_color = text_color or "black", -- color
		background_color = background_color or "#FFFFFF", -- background-color
		border_color = border_color or "#AAAAAA", -- border-color
		height = height or "1.6em", -- height
		label = label or CA.str_vars("DropBox_option_label_Unwrap"),
	}
	if type(boxstyle) == "table" then
		for key, val in pairs(boxstyle) do
			if type(val) == "string" then s[key] = val end
		end
	end
	if type(title) ~= "string" then title = "dropdown_missing_title" end
	s.title = CA.str_vars(title or "dropdown_missing_title")
	if type(content) ~= "string" then content = "" end
	s.content = CA.str_vars(content)
--	local txt = "{{Boîte déroulante/début|titre=" .. title .. "|alignT=" .. alignT .. "}}" .. content .. "{{Boîte déroulante/fin}}"
--	image = image or "Nuvola_apps_bookcase_2.svg" -- image
	if type(s.image) == "string" then
		s.image = ''
		.. '<div class="NavPic" style=" background-color:' .. s.background_color .. '; " >'
			.. '[[File:' .. s.image .. '|22px]]'
		.. '</div>'
	else
		s.image = ''
	end
	local res = ''
	.. '<div align="'.. s.alignB ..'" >'
		.. '<div class="NavFrame" style="clear:both; margin-bottom:'.. s.margin_bottom ..'; width:'.. s.width ..'; border-style:solid; border-radius:'.. s.border_radius ..'; border-color:'.. s.border_color ..'; background-color:'.. s.background_color ..'; " title="'.. s.label ..'" >'
				.. s.image
			.. '<div class="NavHead" align="'.. s.alignT ..'" style=" height:'.. s.height ..'; background-color:'.. s.background_color ..'; color:'.. s.text_color ..'; " >'
				.. s.title
			.. '</div>'
			.. '<div class="NavContent" align="'.. s.alignB ..'" style="margin:'.. s.margin_all ..'; background-color:'.. s.background_color ..'; display:block; " >'
				.. s.content
			.. '</div>'
		.. '</div>'
	.. '</div>'
	return res
end -- function CA.DropBox(title, content, boxstyle)

function CA.dropdown_func(if_view, title, content, ...) -- alignT, image, alignB, margin_bottom, width, border_radius, border_color, background_color)
	-- CA.dropdown_func(1, "Comptages des contenus de tables", CA.tables_counts)
	-- CA.dropdown_func(1, "Détails des arguments", CA.testable_lister, CA.args_source, "CA.args_source")
	if (if_view == false) or (if_view == 0) or (not if_view) then return "" end
	local t = ""
	local func = content
	local args = { ... } -- optional arguments
	local boxstyle = { }
	if type(content) == "string" then
		t = t .. content
	elseif type(func) == "function" then
		-- Get the style options for the box, then mask this style for functions giving the content.
		for i, tab in ipairs(args) do --
			if type(tab) == "table" and tab.boxstyle == "boxstyle" then
				boxstyle = mw.clone( args[i] )
				args[i] = nil
			end
		end
		t = t .. ( func( ... ) or "" ) -- doc1_doc
	else
		t = ""
	end
	-- Search if the last arg is a table of args, then return it for DropBox style options.
--	local maxn = table.maxn(args)
--	if type(args[maxn]) == "table" then args = args[maxn] end
	--
	return CA.DropBox(title, t, boxstyle)
end -- function CA.dropdown_func(if_view, title, content, ...)

-- Extract a part of date following a pre-defined format which starts with separator
function p.date_split(date, format)
	-- local dd, mmmm, yyyy, era = p.date_split(a.birthdate, " dd mmmm yyyy")
	local dd, mmmm, yyyy, era
	local format_split = mw.text.split(format, "%s")
	local split = mw.text.split(date, "%s")
	for i, date_part in ipairs(split) do
		if format_split[i] == "dd" then dd = tonumber(date_part) end
		if format_split[i] == "mmmm" then mmmm = date_part end
		if format_split[i] == "yyyy" then yyyy = tonumber(date_part) end
		if format_split[i] == "era" then era = date_part end
	end
	return dd, mmmm, yyyy, era
end -- function p.date_split(date, format)

-- Extract a part of date following a pre-defined format which starts with separator
function CA.date_to_part(date, part)
	-- local t, err = p.date_to_part(a.birthyear, " dd mmmm yyyy", "yyyy")
	part = part or "yyyy"
	local dd, mmmm, yyyy, era
	local found = false
	--	local err = "date_to_part_err_not_found"
	if not found then
		dd, mmmm, yyyy, era = p.date_split(date, "dd mmmm yyyy")
		if dd and mmmm and yyyy and not era then
			found = true
		end
	end
	if not found then
		dd, mmmm, yyyy, era = p.date_split(date, "mmmm dd yyyy")
		if dd and mmmm and yyyy and not era then
			found = true
		end
	end
	if not found then
		dd, mmmm, yyyy, era = p.date_split(date, "yyyy era")
		if not dd and not mmmm and yyyy then
			if era == "BCE" then yyyy = - yyyy end
			found = true
		end
	end
	if not found then
		dd, mmmm, yyyy, era = p.date_split(date, "mmmm yyyy")
		if not dd and mmmm and yyyy and not era then
			--	date_months_names				= "January, February, March, April, May, June, July, August, September, October, November, December",
			--	date_months_names				= "Enero, Febrero, Marzo, Abril, Mayo, Junio​​, Julio, Agosto, Septiembre, Octubre, Noviembre, Diciembre",
			--	date_months_names				= "Janvier, Février, Mars, Avril, Mai, Juin, Juillet, Août, Septembre, Octobre, Novembre, Décembre",
			found = true
		end
	end
	if not found then
		dd, mmmm, yyyy, era = p.date_split(date, "yyyy")
		if not dd and not mmmm and yyyy and not era then
			found = true
		end
	end
	-- + cccc = roman number ?
	if found then
		if part == "yyyy" then return yyyy or "" end
		if part == "mmmm" then return mmmm or "" end
		if part == "dd" then return dd or "" end
		if part == "era" then return era or "" end
	end
	return "" -- tostring(dd)..tostring(mmmm)..tostring(yyyy)..tostring(era)
--	A faire : Titus Livius (Q2039), format de dates : P569 = date of birth = 59 BCE, P570 = date of death = 17
--	Socrate (470-399 av. J.-C.).
end -- function CA.date_to_part(date, part)

-- relire ControlArgs/Auteur : module code in english

------------------------------------------------------------
-- Manage translations of arguments and messages
-- Gestione traducciones de argumentos y mensajes
-- Gérer les traductions des arguments et messages
------------------------------------------------------------

function p.trans(key) -- give the wiki translation of indentified texts, or arguments indentifier, or errors messages
	local t = CA.wiki_translations[key]
	if t == nil then t="" end
	return t
end

function p.get_IETF_fr()
	-- Chargement de la base de donnée des langues avec gestion d'erreur.
	-- voir, see : https://fr.wikipedia.org/wiki/Module:Langue
	local dataLangue
	local success, resultat = pcall (mw.loadData, 'Module:Langue/Data' )
	if success then
		dataLangue = resultat
	else
		-- Base de donnée à minima en cas de bug sur le Module:Langue/Data
		dataLangue = {
			en = { code = 'en', nom = 'anglais' },
			fr = { code = 'fr', nom = 'français' },
			de = { code = 'de', nom = 'allemand' },
			es = { code = 'es', nom = 'espagnol' },
			it = { code = 'it', nom = 'italien'	 },
			la = { code = 'la', nom = 'latin'	 },
--			['rtl script'] = { Arab = true }, -- debug : risque d'anomalie, anomaly risc
		}
		dataLangue.anglais = dataLangue.en
		dataLangue['français'] = dataLangue.fr
		dataLangue.francais = dataLangue.fr
		dataLangue.allemand = dataLangue.de
		dataLangue.espagnol = dataLangue.es
		dataLangue.italien = dataLangue.it
	end
	return dataLangue
end -- function p.get_IETF_fr()

function p.dummy_languages() -- Test dummy languages
	-- https://fr.wikipedia.org/wiki/%C3%89tiquette_d%27identification_de_langues_IETF
	-- https://fr.wikipedia.org/wiki/Module:Langue/Data
	--	pour convertir en code de langue IETF les noms français de langues
	-- https://www.mediawiki.org/wiki/Manual:$wgDummyLanguageCodes
	--	List of language codes that have been renamed to new (correct) codes, or don't correspond to an actual interface language.
	--	array( 'als' => 'gsw', 'bat-smg' => 'sgs', 'be-x-old' => 'be-tarask', 'bh' => 'bho'...
	local dummy_i18n = {
		en = "Module:ControlArgs/I18N", -- english
		es = "Modul:ControlArgs/I18N", -- spanish
		fr = "Module:ControlArgs/I18N", -- french
		Fr = "Module:ControlArgs/I18N", -- error test
		als = "Modulen:ControlArgs/I18N", -- alemanish
		gsw = "Mod-gsw:ControlArgs/I18N", -- unknown
		["bat-smg"] = "Mod-bat-smg:ControlArgs/I18N", -- alemanish
		sgs = "Mod-sgs:ControlArgs/I18N", -- unknown
	}
	local t = '\n* dummy_languages : ' ..  p.ta("isTag", "isKnownLanguageTag(xx)") .. p.ta("isLang", "isSupportedLanguage(xx)") .. p.ta("isBuilt", "isValidBuiltInCode(xx)")
	t = t .. CA.Th() .. CA.Tc("lang") .. CA.Tc("isTag") .. CA.Tc("isLang") .. CA.Tc("isBuilt")
	t = t .. CA.Tc("langname") .. CA.Tc("native")
	for lang, modname in pairs(dummy_i18n) do -- Pour tous les parametres connus
		local isTag = mw.language.isKnownLanguageTag(lang)
		local isLang = mw.language.isSupportedLanguage(lang)
		local isBuilt = mw.language.isValidBuiltInCode(lang)
		local langname = mw.language.fetchLanguageName(lang, "en")
		local native = mw.language.fetchLanguageName(lang)
		local space_name = tostring(mw.site.namespaces.Module.name)
		t = t .. CA.Tr() .. CA.Td(lang) .. CA.Td(isTag) .. CA.Td(isLang) .. CA.Td(isBuilt)
		t = t .. CA.Td(langname) .. CA.Td(native)
	end
	t = t .. CA.Te()
	return t
end -- function p.dummy_languages()

-- Verifier la cohérence des tables de traductions
function p.verif_i18n(i18n)
	if type(i18n) ~= "table" then i18n = p.i18n end
	if type(i18n) ~= "table" then return " verif_i18n MISSING. " end
	local nerr, trad, t, err = 0, "", "", ""
	local nbr, list = 0, ""
	--
	CA.i18n_trac("verif_i18n", "begin", 1)
	-- List any gaps of translations in i18n tables.
	-- Liste cualquier las lagunas traducciones en tablas i18n.
	-- Lister tous les manques de traductions dans les tables i18n.
	local prec_tab, suiv_tab, max, nk = nil, nil, 0, 0
	local lang, prec_lang, suiv_lang, cats = "", "", "", ""
	local all_txts = {}
	for lang, texts in pairs(i18n) do -- Pour toutes les langues et tous les textes à traduire
		suiv_lang = lang -- table suivante de traduction d'une langue
		suiv_tab = texts -- table suivante de traduction d'une langue
		list = list .. lang .. ", "
		nbr = nbr + 1
		if type(texts) == "table" then -- pour chaque couple de langues à comparer
			for k, v in pairs(texts) do -- Lister tous les textes traduits au moins dans une langue
				all_txts[k] = "x"
			end
			if prec_tab ~= nil and suiv_tab ~= nil then -- pour chaque couple de langues à comparer
				t = t .. "\n* Comparer les langues : '''" .. prec_lang .. "/" .. suiv_lang .. "''', "
				nk = 0
				for k, x in pairs(all_txts) do -- Pour tous les textes à traduire
					trad = suiv_tab[k]
					nk = nk + 1
					if trad == nil then -- and k ~= nil then -- args vers un argument nommé
						err = CA.err_add("err_module_miss_i18n_txt", k, suiv_lang)
						t = t .. err .. " " -- .. "<br>"
						nerr = nerr + 1
					end
				end
				if nk > max then max = nk end
			end
		end
		prec_lang = suiv_lang
		prec_tab = suiv_tab
	end
	if nerr > 0 then
		err = CA.err_add("err_module_miss_i18n_count", nerr, max)
		t = t .. "<br>" .. err .. " "
		cats = cats .. p.cat_add("err_module_miss_i18n_cat")
--		cats = cats .. p.cat_add("cat_internal_error")
	else
		err = CA.msg_add("err_module_miss_i18n_none")
		t = t .. "<br>" .. err .. " "
	end
	-- tester vr le nombre total de variables de traductions
	local st, vr, fn, tb = p.testable_lister(i18n, "i18n")
	if vr < 33 then
		err = CA.err_add("err_module_miss_i18n_mini", vr)
		t = t .. "<br>" .. err .. " "
		cats = cats .. p.cat_add("err_module_miss_i18n_cat")
--		cats = cats .. p.cat_add("cat_internal_error")
	end
	--
	-- List all translated languages
	t = t .. "\n* Translated languages: Langues traduites : "
	local an, ln, Nadd, Nchange, Ntotal = 0, 0, 0, 0, 0
	for lng, argmts in pairs(i18n) do -- Pour toutes les langues à importer
	--	if p.i18n[lng] then -- la langue existe déja, y ajouter ou y remplacer les champs importés
		if i18n[lng] then -- la langue existe déja, y ajouter ou y remplacer les champs importés
			t = t .. CA.str_vars(", '''Langue : %1''' ", lng)
			ln = ln + 1
			if type(texts) == "table" then -- pour chaque couple de langues à comparer
				for argn, val in pairs(argmts) do -- For all imported fields
					if p.i18n[lng][argn] then Nchange = Nchange + 1
					else Nadd = Nadd + 1 end
					-- Add or replace a field and its translation. Ajouter ou remplacer un champs et sa traduction
					p.i18n[lng][argn] = val --	t = t .. CA.str_vars(", %1 ", argn)
					an = an + 1
				end
			end
		else -- ajouter la table et tous ses champs si elle n'est pas encore dans p.i18n
			if mw.language.isKnownLanguageTag(lng) then
				p.i18n[lng] = i18n[lng]
			end
		end
	end
	local lst_lng = ""
	for lang, argmts in pairs(i18n) do -- Pour toutes les langues à importer
		Ntotal = 0
		if type(texts) == "table" then -- pour chaque couple de langues à comparer
			for argn, val in pairs(p.i18n[lang]) do -- For all existing fields
				Ntotal = Ntotal + 1
			end
			local langname = mw.language.fetchLanguageName(lang, "en")
			local native = mw.language.fetchLanguageName(lang)
			lst_lng = lst_lng .. CA.str_vars("list_a_translated_language", Ntotal, lang, langname, native)
			-- list_a_translated_language	= "\n* %1 translations in language %2 : %3 : %4",
		end
	end
	CA.Lang_list = lst_lng
	CA.i18n_trac("verif_i18n", "end", 1)
	--
	t = t .. CA.str_vars("languages_nbr_and_list", nbr, list) .. lst_lng
	return t, cats, lst_lng
end -- function p.verif_i18n(i18n)

CA.trac_lang_t = "" -- "track_lang: "
function CA.trac_lang(where, args_tab) -- res = res .. CA.trac_lang("CA.init_args:", args_source) -- DEBUG
	CA.trac_lang_t = CA.trac_lang_t .. " <b>" .. where .. "</b>"
	CA.trac_lang_t = CA.trac_lang_t .. CA.ta(".c", args_tab.c)
	CA.trac_lang_t = CA.trac_lang_t .. CA.ta(".userlang", args_tab.userlang) .. CA.ta(".wikilang", args_tab.wikilang)
	.. ":u." .. CA.user_lang .. "=" .. (CA.user_translations["title"] or "x")
	.. ":w." .. CA.wiki_lang .. "=" .. (CA.wiki_translations["title"] or "x") .. " "
--	.. ": u:" .. CA.user_lang .. ".title=" .. CA.user_translations["title"]
--	.. ": w:" .. CA.wiki_lang .. ".title=" .. CA.wiki_translations["title"]
end

-- en : initialize or change the user and the wiki languages and their tables
-- es : inicializar o cambiar el idiomas del usuario y del wiki y su tablas
-- fr : initialiser ou modifier la langue de l'utilisateur et du wiki et leurs tables
function CA.init_wiki_user_lang(wiki_lang, user_lang)
	-- en : initialize or change the user and the wiki languages and their tables
	CA.init_wiki_lang(wiki_lang or CA.wiki_lang)
	CA.init_user_lang(user_lang or CA.user_lang)
	if CA.user_lang == nil then
		CA.user_lang = CA.wiki_lang
		CA.user_translations = CA.i18n[CA.user_lang]
	end
end -- function CA.init_wiki_user_lang()

function CA.init_wiki_lang(wiki_lang)
--	t = "\n* init_wiki_lang : asked wiki_lang = " .. tostring(wiki_lang)
	-- wiki_lang ok if i18n ok, else wiki language
	if type(wiki_lang) ~= "string" then wiki_lang = nil end
	if not p.i18n[wiki_lang] then wiki_lang = nil end
	wiki_lang = wiki_lang or tostring(mw.language.getContentLanguage().code)
	if mw.language.isSupportedLanguage(wiki_lang) then
		-- Activate the wiki language for categories.
		-- Activer la langue du wiki pour les pages réelles de catégories.
		CA.wiki_lang = wiki_lang
		CA.wiki_translations = p.i18n[CA.wiki_lang]
	end
	CA.user_wiki_lang = CA.str_vars("user_wiki_lang", CA.user_lang, CA.wiki_lang)
	return -- t .. "<br>"
end -- function p.init_wiki_lang(wiki_lang)

function CA.init_user_lang(user_lang)
--	t = "\n* init_user_lang : asked user_lang = " .. tostring(user_lang)
	-- user_lang ok if i18n ok, else wiki language
	if type(user_lang) ~= "string" then user_lang = nil end
	if not p.i18n[user_lang] then user_lang = nil end
	user_lang = user_lang or CA.wiki_lang or tostring(mw.language.getContentLanguage().code)
	if user_lang then
		-- Activate the user language for errors and messages.
		-- Activer la langue de l'utilisateur pour les erreurs et messages l'affichage des catégories.
		CA.user_lang = user_lang
		CA.user_translations = p.i18n[CA.user_lang]
	else
		-- Error if the language is not a wikipedia language
		-- Erreur si la langue n'est pas une des langues de wikipedia
		CA.err_add("err_lang_not_exist", " ", user_lang)
--		t = t .. CA.err_add("err_lang_not_exist", " ", user_lang)
--		t = t .. p.cat_add("err_module_with_error")
	end
	-- DEBUG wait for : T68051 Get the user language to help admins to maintain a module or a template (userInfo: String user and password)
	local mwtitle = mw.title.getCurrentTitle()
--	local url = tostring(mwtitle:canonicalUrl( ))
	local mwuri = mw.uri.canonicalUrl(mwtitle.prefixedText, { host, authority, user, password, } )
--	CA.trac_lang_t = CA.trac_lang_t .. CA.ta("host", tostring(mwuri.host)) .. CA.ta("authority", tostring(mwuri.authority))
--	.. CA.ta("user", tostring(mwuri.user)) .. CA.ta("language", tostring(mwuri.language))
	CA.user_wiki_lang = CA.str_vars("user_wiki_lang", CA.user_lang, CA.wiki_lang)
	return -- t .. "<br>"
end -- function p.init_user_lang(user_lang)

function p.init_spaces()
--	local frame = mw.getCurrentFrame()
--	local title = mw.title.getCurrentTitle()
--	p.current_space = frame:preprocess("{{ns:0}}") -- Module namespace from system
--	p.current_space = title.nsText -- Module namespace from system
	p.current_space = mw.site.namespaces.name -- present namespace from system
--	p.module_space = frame:preprocess("{{ns:Module}}") -- Module namespace from system
	p.module_space = tostring(mw.site.namespaces.Module.name)
--	p.template_space = frame:preprocess("{{ns:Template}}") -- Template namespace from system
	p.template_space = tostring(mw.site.namespaces.Template.name)
--	p.author_space = frame:preprocess("{{ns:Author}}") -- Category namespace from system
--	p.author_space = tostring(mw.site.namespaces.Author.name)
--	p.category_space = frame:preprocess("{{ns:Category}}") -- Category namespace from system
	p.category_space = tostring(mw.site.namespaces.Category.name)
end -- function p.init_spaces(frame)

------------------------------------------------------------
-- Manage errors and messages. Administrar errores y mensages. Gérer les erreurs et messages.
------------------------------------------------------------

CA.errors_list = {} -- Table to collect errors and messages
CA.erron = true -- Activated or not errors. Errores activado o no. Erreurs activées ou non.

-- Add a message, error or category to le lists.
function CA.error_add(typ, ref, v1, v2, v3, v4, v5)
	--	CA.err_add("err_value_re_defined", k, key, xyz)
	if not p.erron then return "" end
	local msg = { ["typ"] = typ, ["ref"] = ref, ["v1"] = v1, ["v2"] = v2, ["v3"] = v3, ["v4"] = v4, ["v5"] = v5 }
	local str = CA.str_vars(msg.ref, msg.v1, msg.v2, msg.v3, msg.v4, msg.v5)
	local do_it = true
	for k, reg in ipairs(p.errors_list) do -- If the new error was previously registered, do not add it.
		local reg_txt = CA.str_vars(reg.ref, reg.v1, reg.v2, reg.v3, reg.v4, reg.v5)
		if str == reg_txt then do_it = false end
	end
	if do_it then table.insert(p.errors_list, msg) end
	local res = ""
	if msg.typ == "msg" then
		res = '<br>⦁ ' .. p.message_color(str)
	end
	if msg.typ == "err" then
		res = '<br>⦁ ' .. p.error_color(str)
	end
	return res
end -- function CA.error_add(typ, ref, v1, v2, v3, v4, v5)

function CA.err_add(ref, v1, v2, v3, v4, v5)
	return CA.error_add("err", ref, v1, v2, v3, v4, v5)
end

function CA.msg_add(ref, v1, v2, v3, v4, v5)
	return CA.error_add("msg", ref, v1, v2, v3, v4, v5)
end

function p.errors_lister(title, v1, v2, v3)
	local res, msgref = "", ""
--	res = res .. p.testable_lister(p.errors_list, "p.errors_list")
	local mwtitle = mw.title.getCurrentTitle()
	local page = tostring(mwtitle.nsText) .. ":" .. tostring(mwtitle.text)
	if type(title) ~= "string" then title = "err_error_list_header" end
	res = res .. '\n*' .. CA.str_vars(title, page, v1, v2, v3) -- .. ' - ' .. page
	local n = 0
	for k, msg in ipairs(p.errors_list) do
		msgref = CA.str_vars(msg.ref, msg.v1, msg.v2, msg.v3, msg.v4, msg.v5) -- texte traduit ou direct
		if msg.typ == "msg" then
			res = res .. '<br>⦁ ' .. p.message_color(msgref)
		end
	end
	for k, msg in ipairs(p.errors_list) do
		msgref = CA.str_vars(msg.ref, msg.v1, msg.v2, msg.v3, msg.v4, msg.v5) -- texte traduit ou direct
		if msg.typ == "err" then
			res = res .. '<br>⦁ ' .. p.error_color(msgref)
			n = n + 1
		end
	end
	if n > 0 then
		p.cat_add("err_module_with_error")
	end
	return res
end -- function p.errors_lister(title, v1, v2, v3)

------------------------------------------------------------
-- Manage categories. Administrar categorías. Gérer les catégories.
------------------------------------------------------------

function p.catGen(ref, v1, v2, v3, v4, v5)
	-- add a category to the categories_list
	table.insert(p.categories_list, { ["typ"] = "cat", ["ref"] = ref, ["v1"] = v1, ["v2"] = v2, ["v3"] = v3, ["v4"] = v4, ["v5"] = v5 })
end

-- Record in categories_list and genrate the wikitext of a category
function p.cat_add(ref, v1, v2, v3, v4, v5)
	-- add a category to the categories_list
	local cat = { ["typ"] = "cat", ["ref"] = ref, ["v1"] = v1, ["v2"] = v2, ["v3"] = v3, ["v4"] = v4, ["v5"] = v5 }
	local str = CA.str_vars(cat.ref, cat.v1, cat.v2, cat.v3, cat.v4, cat.v5)
	local do_it = true
	for k, reg in ipairs(p.categories_list) do -- If the new category was previously registered, do not add it.
		local reg_txt = CA.str_vars(reg.ref, reg.v1, reg.v2, reg.v3, reg.v4, reg.v5)
		if str == reg_txt then do_it = false end
	end
	if do_it then table.insert(p.categories_list, cat) end
	--
	local c = CA.catView or ""
--	if CA.catView == true then c = ":" else c = "" end
	local wiki_catspace = CA.wiki_translations.category or mw.site.namespaces.Category.name
	local user_catspace = CA.user_translations.category or mw.site.namespaces.Category.name
	local user_cat = p.string_vars(CA.user_translations, cat.ref, cat.v1, cat.v2, cat.v3, cat.v4, cat.v5)
	local wiki_cat = p.string_vars(CA.wiki_translations, cat.ref, cat.v1, cat.v2, cat.v3, cat.v4, cat.v5)
	--
	user_cat = (user_cat or "")
	local user_verif = string.match( user_cat, "([%w_])") -- verif content with all alphanumeric characters.
	if not user_verif then user_verif = ":" .. user_cat .. ":" end
--	user_cat = user_verif or ("<" .. user_cat .. ">")
	--
	user_catspace = "" -- without "Category" word
	local res = " [[" .. c .. wiki_catspace .. ":" .. wiki_cat ..  "|" .. user_catspace .. " " .. user_cat .. "]] "
	return res
end -- function p.cat_add(ref, v1, v2, v3, v4, v5)

-- Generate categories from plural values in only one argument
function p.catGroup(groupCat, groupList) -- generate some categories
	-- catGroup("Country %1", "France,Italie") -> [[Category:Country France]] [[Category:Country Italie]]
	if type(groupCat) ~= "string" then groupCat = "%1" end
	if type(groupList) ~= "string" then return "" end
	local cats = ""
	local t = ""
	t = t .. CA.ta("groupList", groupList)
	local splitxt = mw.text.split(groupList, ",", true)
--	t = t .. " splitxt = " .. table.concat(splitxt, "+")
	for str in mw.text.gsplit(groupList, ",", true) do
	--	t = t .. " , s=" .. p.cat_add(groupCat, str)
		cats = cats .. p.cat_add(groupCat, str)
	end
	return cats, t
end -- function p.catGroup(groupCat, groupList)

function CA.options_to_catView() -- Init or restaure CA.catView from options
	if CA.option(":") or CA.option("catview") then CA.catView = ":" else CA.catView = "" end
	return CA.catView
end -- function CA.options_to_catView()

-- generate the wikitext of the list of categories to only display them
function p.categories_lister(c)
	local res = "" -- "\n* Catégories : "
	local wiki_cat = ""
	local user_cat = ""
	CA.options_to_catView()
	-- c can replace catView to enforce the documentation or the categorisation
	-- c peut remplacer catView pour forcer la documentation ou la catégorisation
	c = c or CA.catView or ""
--	local wiki_catspace = mw.site.namespaces.Category.name
	local user_catspace = CA.user_translations.category
	local wiki_catspace = CA.wiki_translations.category
	for k, cat in ipairs(p.categories_list) do
		user_cat = p.string_vars(CA.user_translations, cat.ref, cat.v1, cat.v2, cat.v3, cat.v4, cat.v5)
		wiki_cat = p.string_vars(CA.wiki_translations, cat.ref, cat.v1, cat.v2, cat.v3, cat.v4, cat.v5)
		user_catspace = "" -- without "Category" word

		user_cat = (user_cat or "") -- prize
		if user_cat == " " then user_cat = "" end
		if user_cat == "  " then user_cat = "" end
		if user_cat == "	" then user_cat = "" end
	--	local user_verif = string.match( user_cat, "([%w_])") -- verif content with all alphanumeric characters.
	--	user_verif = mw.ustring.gsub( user_cat, "(%s)", "") -- verif content with all alphanumeric characters.
		local user_verif = mw.ustring.gsub( user_cat, "%s", "") -- verif content with all alphanumeric characters.
		if not user_verif then user_verif = "1" .. user_cat .. "2" end
	--	user_cat = user_verif or ("<" .. user_cat .. ">")

		res = res .. " [[" .. c .. wiki_catspace .. ":" .. wiki_cat ..	"|" .. user_catspace .. user_cat .. "]] "
	end
	-- Category namespaces = 14 Category
	-- https://en.wikipedia.org/wiki/IETF_language_tag
	-- https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
	return res
end -- function p.categories_lister(c)

-- Initialize the categories list. Initialise la liste des categories.
function p.categories_init(catName, catText) -- initialize the category list
	local frame = mw.getCurrentFrame()
	-- p.category_space = frame:preprocess("{{ns:Category}}") -- Category namespace from system, translated
	-- "Category" namespace translated to local language :
	p.category_space = tostring(mw.site.namespaces.Category.name)
	p.categories_list = {} -- init the collect of categories
	return
end

------------------------------------------------------------
-- Manage options. Administrar opciones. Gérer les options.
------------------------------------------------------------

--	Options de maitrise du fonctionnement de ce module

p.invoke_options = "" -- Options normales venant du modèle. Aucune par defaut. Normal options from the template.
	--	In the template : options = "params docview docmin docmax docdef docnotice docafter docline docsrc"

p.mode_options = "" -- Options de debug du module par edit. Aucune par defaut.
	-- p.mode_options = "unitest debug noerr erron params docview nobox nocat docin docmax docnotice docdef docline docsrc notices "
	-- p.mode_options change only by editing this module code or the calling module. Empty default value.
	-- p.mode_options change n'est modifié que par ce module ou celui qui l'appele. Valeur par defaut vide.

p.used_options = {} -- table to collect tested options then list them at end of tests

function CA.option(key, opt)
	-- if option("nocat") then cat = "" end -- utilisation exemple simple
	-- Si le mot key est parmi les mots options, repondre true
	-- Chercher dans invoke_options, mode_options ou opt, voir ci-dessous
	-- p.options = " : docdata docmin docdef docmax docline docview docsrc docafter docnotice " -- for documentation
	-- p.options = " erron noerr nobox nocat " -- without normal result
	-- p.options = " debug tests en es fr " -- for debug or enforce language
	-- Veiller à toujours séparer les mots par des espaces
	-- Les identifiants de langues permettent de forcer certaines langues.
	-- Les erreurs n'apparaissent que dans les espaces de noms Modèle ou Module, en attendant la réalisation du Bug 51660.
	if type(key) ~= "string" then key = false end
	if type(opt) ~= "string" then opt = false end
	local available_options = " " .. (p.invoke_options or "") .. " "
	if opt -- opt param can replace p.mode_options
		then available_options = " " .. opt .. " " .. available_options .. " "
		else available_options = " " .. p.mode_options .. " " .. available_options .. " "
	end -- options du modèle et de debug sinon
	-- Chercher le mot clef exact, non inclus dans un autre.
	-- Search the exact key, not included in another.
	local key2 = " " .. (key or "				  ") .. " "
	-- Le mot cle est-il parmi les options definies ?
	-- The searched keyword is it among the options words?
	local n = string.find(available_options, key2)
	local ifyes = n and (n > 0)
	if not ifyes then ifyes = false else ifyes = true end
	-- collect options tested along the execution of the module
	if ifyes then p.used_options[key] = "a" else p.used_options[key] = "x" end
	return ifyes, available_options -- ( yes ~= nil )
end -- function p.option(key, opt)

function p.init_options(opt)
	-- p.invoke_options = init_options(args.options)
	-- p.invoke_options = init_options("fr params docview docmin docmax docdef docnotice docafter docline docsrc")
	--
	if (type(opt) == "string") then p.invoke_options = opt end --  and (new ~= "")
	--
	-- Early effects on options which modify other ones.
	-- Effets précosses pour des options qui agissent sur d'autres options.
	-- p.options = " : docdata docmin docdef docmax docline docview docafter docnotice docsrc" -- for documentation
	-- p.options = " erron noerr nobox nocat " -- without normal result
	-- p.options = " debug tests en es fr " -- for debug or enforce language
	CA.options_to_catView()
	if p.option("noerr") then CA.erron = false end
	if p.option("erron") then CA.erron = true end
	if p.option("docolor") then CA.docolor = true end
	-- If an option is a language, enable this language for the user.
	-- Si una opción es un lenguaje, activar esta lengua para usuario.
	-- Si une option est une langue, activer cette langue pour l'utilisateur.
	local _lang
	for lang, tab in pairs(p.i18n) do
		if p.option(lang) then
	--		CA.init_user_lang(lang, CA.wiki_lang) -- CA.user_lang, CA.wiki_lang
	--		_lang = lang
		end
	end
--	if _lang then p.option(_lang) end
--	CA.init_user_lang(nil) -- , CA.args_final.wikilang)
	return opt
end -- function p.init_options(opt)

function p.used_options_list(t, used_options)
	local t = "List of modes:" or t
	for md, opt in pairs(CA.options_for_modes) do t = t .. "<br>- " .. CA.ta(md, opt) end
	-- List of used options after collect them
	used_options = used_options or CA.used_options
	t = t or ""
	t = t .. "\n* " .. CA.user_wiki_lang
	t = t .. "\n* Actual mode used: <b>" .. CA.mode_name .. "</b>"
	t = t .. "\n* Options read the last time in these tests, ones <b>activated</b> are in bold: "
	t = t .. "\n* Options lues pour la dernière fois pendant ces tests, celles <b>activées</b> sont en gras : "
	t = t .. "<br>"
	local opt = ""
	for key, x in pairs(p.used_options) do
		if x == "a" then opt = opt .. ", <b>" .. tostring(key) .. "</b> "
		else opt = opt .. ", " .. tostring(key) .. " " end
	end
	t = t .. opt
--	t = t .. "<br>* " .. CA.get_editstate()
--	t = t .. "\n* import_arguments_track : " .. CA.import_arguments_track
	return t, opt
end -- function p.used_options_list(t, used_options)

function p.options_from_args_test(t)
	local mode_options_memo = p.mode_options -- save
	local invoke_options_memo = p.invoke_options -- save
	local used_options_memo = mw.clone(p.used_options) -- save
	p.used_options = {}
	local t = "options_from_args_test:" or t
	t = t .. CA.Th() .. CA.Tc("mode_options") .. CA.Tc("invoke_options") .. CA.Tc("opt can replace mode_options") .. CA.Tc("available options") .. CA.Tc("option result")
	local function options_from_args(mode_options, invoke_options, opt, key)
		p.mode_options = mode_options
		p.invoke_options = invoke_options
		local ifyes, available_options = p.option(key, opt)
		return CA.Tr() .. CA.Td(mode_options or "") .. CA.Td(invoke_options or "") .. CA.Td(opt or "")
		.. CA.Td( tostring(available_options) ) .. CA.Td(CA.ta(key, ifyes) )
	end
	t = t .. options_from_args("nobox nocat", "en docview", nil,			"nobox")
	t = t .. options_from_args("nobox nocat", "en docview", nil,			"nocat")
	t = t .. options_from_args("nobox nocat", "en docview", nil,			"en")
	t = t .. options_from_args("nobox nocat", "en docview", nil,			"docview")
	t = t .. options_from_args("nobox nocat", ": docview",	nil,			":")
	t = t .. options_from_args("nobox nocat", "en docview", "docline fr",	"docline")
	t = t .. options_from_args("nobox nocat", "en docview", "docline fr",	"docline")
	t = t .. options_from_args("		   ", nil,			"docline fr",	"docline")
	t = t .. options_from_args("		   ", nil,			"docline fr",	"fr")
	t = t .. options_from_args("		   ", "en docview", "docline fr",	"en")
	t = t .. options_from_args("nobox nocat", nil,			"docline fr",	"docline")
	t = t .. options_from_args("nobox nocat", "en docview", "docline fr",	nil)
	t = t .. options_from_args("nobox nocat", "en docview", nil,			"docline")
	t = t .. CA.Te()
	t = t .. p.used_options_list(nil, CA.used_options)
	t = t .. "\n* After these tests, anterior options are restored. Après ces tests, les options antérieures sont restaurés."
	p.mode_options = mode_options_memo -- restore
	p.invoke_options = invoke_options_memo -- restore
	p.used_options = used_options_memo -- restore
	return t
end -- function p.options_from_args_test(t)

function p.wordstotable(txt, opt) -- convertit un texte en table de mots
	local t = ""
	local tab = {}
	local function inserer(ti)
		if ti ~= "" then table.insert(tab, ti.."") end
	end
	local xyz = string.gsub( txt, "(%S*%-*%S*)", inserer ) -- "(%w*%-*%'*%w*)", "(%S*%-*%S*)"
	for key, val in pairs(tab) do -- Pour tous les mots
		t = t .. " ( " .. tostring(key) .. " = " .. tostring(val) .. " ) "
	end
	t = "\n* wordstotable txt = " .. tostring(xyz) .. " " .. t
	return tab, t, opt
end -- function p.wordstotable(txt, opt)

------------------------------------------------------------
-- Argts : Manage arguments. Gestione argumentos. Gérer les arguments.
------------------------------------------------------------

-- Compute the Levenshtein distance between 2 ASCII words.
function CA.levenshtein(word1, word2)
	-- prevent exceptions
	local cout = 0
	if (word1 == nil) or (word2 == nil) then
		return 999, "<br>lev: " .. CA.ta("word1", word1) .. CA.ta("word2", word2)
	end
	local len1 = string.len(word1)
	local len2 = string.len(word2)
	local lev = 0
	local t = "<br>lev: " .. CA.ta("word1", word1) .. CA.ta("word2", word2)
	if (type(word1) ~= "string") or (type(word2) ~= "string") or (word1 == "") or (word2 == "") then
		lev = len1 + len2
		return lev, t .. CA.ta("lev", lev)
	end
	-- simple case
	if (word1 == word2) then
		lev = 0
		return lev, t .. CA.ta("lev", lev)
	end
	local d = {}
	for i = 1, len1+2 do -- for i = 1, len1-1+1 do
		d[i] = {}
		--	d[i][1] = 0 -- d[i][1] = i
		for j = 1, len2+2 do
			--	d[i][j] = {}
			d[i][j] = 0 -- d[1][j] = j
		end
	end
	-- simulate double dimensions tables
	for i = 2, len1+1 do -- for i = 1, len1-1+1 do
		--	d[i] = {}
		d[i][1] = i-1 -- d[i][1] = i
	end
	for j = 2, len2+1 do
		d[1][j] = j-1 -- d[1][j] = j
	end
	for i = 2, len1+1 do -- for i = 2, len1+1 do
		for j = 2, len2+1 do -- for j = 2, len2+1 do
			-- on récupère les deux caractères
			local c1 = string.byte(word1, i-1)
			local c2 = string.byte(word2, j-1)
			if (c1 == c2) then
				cout = 0
				d[i][j] = d[i-1][j-1]
			else
				cout = 1
				d[i][j] = math.min(d[i-1][j], d[i][j-1], d[i-1][j-1]) + 1
			end
			--	d[i][j] = math.min(d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1]+cout)
		end
	end
	local lev = d[len1+1][len2+1] -- return d[len1-1][len2] - 1
	return lev, t .. CA.ta("lev", lev)
end -- function CA.levenshtein(word1, word2)

function p.levenshtein_test_1(search, word, max)
	-- search = mot cherché dans la liste
	-- word = un des mot de la liste
	-- p.max_nearest_argument = 3 -- Limite de differences des arguments proposables à l'utilisateur
--	if not max then max = p.max_nearest_argument end
--	if not max then max = 3 end
	local diffmaxi = p.similar_args_diffmaxi( string.len(search) )
	local len1, len2, lev, tlev, t, diff
--	t = t .. "<br>- diff: " .. p.ta("mot1", mot1) .. p.ta("mot2", mot2) .. p.ta("diff", diff)
	t = "<br>levenshtein : "
	if (not search ) or (not word) then
		diff = 99
		t = t .. p.ta("diff", diff) .. " no word. "
	elseif search == word then
		diff = 0
		t = t .. p.ta("diff", diff) .. " , " .. search .. " = " .. word .. " "
	else
	--	len1 = string.len(search)
	--	len2 = string.len(word)
		lev, tlev = p.levenshtein(search, word)
		if (lev <= diffmaxi) then
			t = t .. p.ta("diffmaxi", diffmaxi) .. " >= " .. p.ta("lev", lev) .. " '''" .. search .. " ==> " .. word .. "''' "
		else
			t = t .. p.ta("diffmaxi", diffmaxi) .. " >= " .. p.ta("lev", lev) .. " " .. search .. " / " .. word .. " "
		end
		diff = lev
	end
	return diff, t, search, word
end -- function p.levenshtein_test_1(search, word, max)

function p.levenshtein_test(res, c)
	if type(res) ~= "string" then res = nil end
	local res = res or ("\n* " .. CA.str_vars("max_nearest_argument_msg") )
	local errors = ""
	local n, t = p.levenshtein_test_1( "nom", "nom")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "nom", "Nom")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "top", "pot")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "ami", "amis")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "nom", "name")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "m", "mu")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "m", "mur")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "mur", "m")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "c", "C")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "c", "cf")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "c", "long")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "xxx", "")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "", "xyz")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "xxx", "xyz")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "prénom", "Prenom")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "catégorie", "Category")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "description", "Description")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "anneeDeces", "anneeNaissance")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "anoNacimiento", "anneeNaissance")
	res = res .. tostring(t)
	local n, t = p.levenshtein_test_1( "avant-après", "après-avant")
	res = res .. tostring(t)
	if errors ~= "" then res = res .. "\n* '''levenshtein_test''' errors = " .. p.error_color(errors) end
	return res
end -- function p.levenshtein_test( res, c)

-- Maximum number of different letters between 2 argument names
-- Número máximo de letras diferentes entre 2 nombres de argumento
-- Nombre maximum de lettres différentes entre deux noms d'arguments
function p.similar_args_diffmaxi(length, t) -- diffmaxi from length of arg arglingual name
	local coef = CA.constants.near_word_search_diff_coef or 0.30
	local constant = CA.constants.near_word_search_diff_const or 0.32
	local diffmaxi = math.floor( coef * length + constant )
	if t then t = t .. tostring(coef) .. " * length + " .. tostring(constant) end
	return diffmaxi, t
end -- function p.similar_args_diffmaxi(length, t)

-- Pour un argument inconnu, cherche le nom d'argument le plus proche, parmi les arguments connus traduits
function p.similar_args_list(args_known)
	local list, arglingual = {}, "xxx"
	if type(args_known) ~= "table" then args_known = CA.args_known end
	if type(args_known) ~= "table" then return "similar_args_list", 1 end
	-- faire la liste des arguments possibles, c'est a dire connus
	for key, argm in pairs(args_known) do
		if not tonumber(key) then -- Pour les arguments nommés seulement
			key = tostring(key)
			arglingual = tostring(CA.wiki_translations[key])
			list[key] = arglingual
		end
	end
	return list
end -- function p.similar_args_list(args_known)

-- For an unknown argument, seeking the name of the nearest argument among the known arguments
-- Para un argumento desconocido, buscando el nombre del argumento más cercana entre los argumentos conocidos
-- Pour un argument inconnu, cherche le nom d'argument le plus proche, parmi les arguments connus
function p.similar_args_search(search, list)
	local dist, lengths, tlev = 9, 0
	local trouve1, trouve2, trouve3 = nil, nil, nil
	local min1, min2, min3 = 99, 99, 99
	local diffmaxi = p.similar_args_diffmaxi( string.len(search) )
	local t = ", " .. tostring(diffmaxi) .. " / " .. tostring(length) .. " "
	for key, arglingual in pairs(list) do
		-- Search the most similar and same length. Buscar las más similares y la misma longitud. Chercher le plus ressemblant et la même longueur.
	--	dist = p.levenshtein_test_1(tostring(cherche), tostring(mot)) -- obsolete
		dist, tlev = p.levenshtein(search, arglingual)
	--	if type(dist) ~= "number" then dist = 9 end
		if (dist <= min1) then
			trouve3 = trouve2
			min3 = min2
			trouve2 = trouve1
			min2 = min1
			min1 = dist
			trouve1 = tostring(arglingual)
		end
	end
	t = t .. "<br>" .. p.ta("cherche", cherche) .. p.ta("trouve1", trouve1) .. p.ta("min1", min1) .. p.ta("trouve2", trouve2) .. p.ta("min2", min2) .. " " .. t
	if trouve1 and min1 == 0 then t = t .. CA.wikidata_color(p.ta("connu", trouve1)) end
	if trouve1 and min1 <= diffmaxi then t = t .. CA.wikidata_color(p.ta("min1 "..min1.."<="..diffmaxi, trouve1)) end
	if trouve2 and min2 <= diffmaxi then t = t .. CA.wikidata_color(p.ta("min2 "..min2.."<="..diffmaxi, trouve2)) end
	if trouve3 and min3 <= diffmaxi then t = t .. CA.wikidata_color(p.ta("min3 "..min3.."<="..diffmaxi, trouve3)) end
	return trouve1, min1, trouve2, min2, t
end -- function p.similar_args_search(search, list)

--	t = p.similar_args_test1( t, "nomm", "digit")
function p.similar_args_test1( t, search, liste)
	local args_list = p.similar_args_list(CA.args_known)
	local trouve1, min1, trouve2, min2 = p.similar_args_search(search, args_list)
--	local trouve1, min1, trouve2, min2 = "aaa", 1, "bbb", 22
	t = t .. "\n* similar_args_test1 : " .. CA.ta("search", search) .. CA.ta("trouve1", trouve1) .. CA.ta("min1", min1) .. CA.tam("trouve2", trouve2) .. CA.tam("min2", min2)
	return t or " similar_args_test1 "
end -- function p.similar_args_test1( t, search, liste)

function p.similar_args_test( t, args_known)
	if type(args_known) ~= "table" then args_known = CA.args_known end
	local err = CA.verify_args_tables(args_known, CA.args_source)
	if err then return err end
	--
	local key, argsyn, arglingual, txt
--	local coef = CA.constants.near_word_search_diff_coef
--	local constant = CA.constants.near_word_search_diff_const
	t = "\n* " .. (t or "Formula to compute the near words limit: Formule de calcul de limite des mots proches : ")
	local diffmaxi
	diffmaxi, t = p.similar_args_diffmaxi(10, t .. "diffmaxi = ")
	t = t ..  "\n* List of diffmaxi / lengths : "
	for length = 1, 16 do -- For all lengths
	--	local diffmaxi = math.floor( coef * length + constant )
		diffmaxi = p.similar_args_diffmaxi(length)
		t = t .. ", " .. tostring(diffmaxi) .. " / " .. tostring(length) .. " "
	end
	t = t .. "\n* List of known arguments and '''synonyms''' : "
	local txt, lingual = "", ""
	local ref_words = {}
	for key_known, argm in pairs(args_known) do -- Pour tous les paramètres connus
		if argm.syn == 1 then
			key = argm.keyword
			argsyn = key_known .. ">" -- synonym argument
		else
			key = key_known
			argsyn = "" -- synonym argument
		end
	--	CA.user_translations = p.i18n[lang] or CA.user_translations
	--	CA.wiki_translations = mw.language.getContentLanguage().code
		lingual = CA.wiki_translations[key] or "-" -- importer un argument source
		txt = argsyn .. key .. "/" .. lingual
		if argm.syn == 1 then
			t = t .. ", '''" .. txt .. "''' " -- '''synonyms'''
		else
			t = t .. ", " .. txt .. " "
		end
		ref_words[key] = {}
		ref_words[key].argmt = key
		ref_words[key].lingual = lingual
	end
	t = t .. "\n* Test similar arguments 1."
	t = p.similar_args_test1( t, "anneedece", {["a"]="but", ["b"]="porter", ["c"]="anneedeces"})
	t = t .. "\n* Test similar arguments 2."
	t = p.similar_args_test1( t, "porte", {["a"]="but", ["b"]="porter", ["c"]="pot"})
	return t
end -- function p.similar_args_test( t, args_known)

-- Check if the value of an argument is among the possible values.
-- Vérifier si la valeur d'un argument est parmi les valeurs possibles.
function CA.multiple_values(argmt, argvalue, args_final, args_known)
	local args_final = args_final or CA.args_final
	local args_known = args_known or CA.args_known
	local argvalue = argvalue or args_final[argmt]
	local arg_values, key_values, keyword, keyval, argval, rank
	local argm = args_known[argmt]
	if argm then
		arg_values = CA.wiki_translations[argm.arg_values]	or "" -- example "no,nada,cn,50,us,70,mpf" in local language
		key_values = argm.key_values						or "" -- example "no,none,cn,50,us,70,mpf" in referal english
	end
	if type(arg_values) == "string" and type(key_values) == "string" then
		local arg_tab = mw.text.split(arg_values, ',') -- table of arg
		local key_tab = mw.text.split(key_values, ',') -- table of key
		-- Default values
		keyword = nil
		rank = 0 -- rank of local value and key value
		keyval = nil -- key value
		argval = nil
		if argm and arg_values and argvalue then
			for i, key in ipairs(arg_tab) do
				if key == argvalue then -- Search argvalue in arg_tab
					rank = i
					keyval = key_tab[i] -- Return correponding keyval in key_tab
					argval = argvalue
					keyword = argm.keyword
				end
			end
		end
	end
	return keyword, keyval, argval, rank
end -- function CA.multiple_values(argmt, argvalue, args_final, args_known)

function CA.multiple_values_tests(t)
	t = t or "\n* Test '''multiple_values''' :"
	t = t .. CA.Th() .. CA.Tc("argm") .. CA.Tc("argvalue") .. CA.Tc("args_final") .. CA.Tc("keyword") .. CA.Tc("keyval") .. CA.Tc("argval") .. CA.Tc("rank")
	local function multiple_values_tests1(t, argm, argvalue, args_final)
		local keyword, keyval, argval, rank = CA.multiple_values(argm, argvalue, args_final)
		t = (t or "") .. CA.Tr() .. CA.Td(argm) .. CA.Td(argvalue) .. CA.Td(args_final) .. CA.Td(keyword) .. CA.Td(keyval) .. CA.Td(argval) .. CA.Td(rank)
		return t
	end -- function multiple_values_tests1(t, argm, argval)
	t = multiple_values_tests1(t, "region", "inde")
	t = multiple_values_tests1(t, "region", "inconnue")
	t = multiple_values_tests1(t, "rights", "mpf")
	t = multiple_values_tests1(t, "rights", "non")
	t = multiple_values_tests1(t, "rights", "aucun")
	t = multiple_values_tests1(t, "sex", "femme")
	t = multiple_values_tests1(t, "sex", "homme")
	t = multiple_values_tests1(t, "sex", "enfant")
	args_final = { region = "india", rights = "non", sex = "femme", }
	t = (t or "") .. CA.Tr() .. CA.Tc() .. CA.Tc() .. CA.Tc('args_final = {') .. CA.Tc('region = "india"') .. CA.Tc('rights = "non"') .. CA.Tc('sex = "femme"') .. CA.Tc('}')
	t = t .. CA.Tr() .. CA.Tc("argm") .. CA.Tc("argvalue") .. CA.Tc("args_final") .. CA.Tc("keyword") .. CA.Tc("keyval") .. CA.Tc("argval") .. CA.Tc("rank")
	t = multiple_values_tests1(t, "region", "inde", args_final)
	t = multiple_values_tests1(t, "region", nil, args_final)
	t = multiple_values_tests1(t, "rights", "mpf", args_final)
	t = multiple_values_tests1(t, "rights", "non", args_final)
	t = multiple_values_tests1(t, "rights", nil, args_final)
	t = multiple_values_tests1(t, "sex", nil, args_final)
	t = multiple_values_tests1(t, "sex", nil, args_final)
	t = multiple_values_tests1(t, "sex", "enfant", args_final)
	t = t .. CA.Te()
	return t
end -- function CA.multiple_values_tests(t)

function CA.multiple_selection(opt, selector, to_select)
	-- Select items to select containing selecting items
	local t, selected_txt, selector_txt, selector_tab, to_select_txt, to_select_tab, selected_tab = "", ""
	local cut = string.sub( opt, 1, 1 ) or ","
	if type(selector) == "table" then selector_tab = selector end
	if type(selector) == "string" then selector_tab = mw.text.split(selector, cut, true) end
	if type(to_select) == "table" then to_select_tab = clone(to_select) end
	if type(to_select) == "string" then to_select_tab = mw.text.split(to_select, cut, true) end
	selected_tab = {}
	--
	local k, Nsel, N, maxi, pos = 0, 0, 1, 999, nil
	local reject_select = false
	local equal = true
	for i, select in ipairs(selector_tab) do -- select authorities only following selectors
		if Nsel >= maxi then break end
		select = mw.text.trim(select)
		N = tonumber(select)
		if select == "+" then -- select all items
			for key, val in pairs(to_select_tab) do
				Nsel = Nsel + 1
				selected_tab[key] = val
			end
		elseif select == "-" then -- minus sign rejects all
			reject_select = true
		elseif N and N < 1 then -- minus sign rejects all
			reject_select = true
		elseif N and (string.sub(select, 1, 1) == "+") then -- select +N more items
			maxi = Nsel + N
		elseif N then -- select N maximum total items
			maxi = N
		else -- select ONE item from to_select_tab if it matches select ( not - or + or +N or N )
			for key, val in pairs(to_select_tab) do
				local select_t, val_t = select, val
				if not CA.is_in("U", opt) then select_t = string.lower(select_t) end -- recognize lowercase and uppercase
				if not CA.is_in("t", opt) then select_t = mw.text.trim(select_t) end -- recognize after trim
				if not CA.is_in("U", opt) then val_t = string.lower(val_t) end -- recognize lowercase and uppercase
				if not CA.is_in("t", opt) then val_t = mw.text.trim(val_t) end -- recognize after trim
				equal = CA.is_in("=", opt) -- recognize only equal string
				if equal then equal = (select == val) else equal = CA.is_in(select_t, val_t) end
			--	if CA.is_in(select, val) then
				if equal then -- recognize if select is equal or is inside an item from to_select_tab
					t = t .. CA.ta(select, val)
					selected_tab[select] = val
					Nsel = Nsel + 1
					to_select_tab[key] = " " -- Delete the selected item to use it only once
					selected_txt = selected_txt .. val .. ', '
				end
			end
		end
	end
--	selected_txt = table.concat(selected_tab)
	return selected_txt, selected_tab, t
end -- function CA.multiple_selection(opt, selector, to_select)

function CA.multiple_selection_test(t)
	t = (t or "") .. "\n* '''multiple_selection''' options: " .. CA.ta("=", "equal only") .. CA.ta("t", "not trim before and after") .. CA.ta("U", "not lowercase and uppercase")
	local function multiple_selection_test1(t, opt, selector, to_select)
		local selected_txt, selected_tab, txt = CA.multiple_selection(opt, selector, to_select)
		t = t .. CA.Tr() .. CA.Td(opt) .. CA.Td(selector) .. CA.Td(to_select) .. CA.Td( txt .. CA.ta("selected_txt", selected_txt) )
		return t, opt, selector, to_select -- , selected_txt, selected_tab
	end
	local opt = ", "
	t = t .. "\n: selector = <b>nobel,+1,président,3,député,prix</b> signifie : sélectionner le premier, puis 1 de plus parmi les suivants, puis 3 en tout au maximum."
	local head = mw.text.split( CA.str_vars("multiple_selection_test_header") , ',')
	t = t .. CA.Th() .. CA.Tc(head[1]) .. CA.Tc(head[2]) .. CA.Tc(head[3]) .. CA.Tc(head[4])
	-- Todo ? P39 = fonction = "président de Pologne, député à l'Assemblée, Prix Nehru, Nobel de la paix"
	-- multiple_selection_test_select	= "2, nobel, president, deputy, price",
	t = multiple_selection_test1( t, opt, "2, nobel, président, député, prix", "président de Pologne,député à l'Assemblée, Prix Nehru, Nobel de la paix" )
	t = multiple_selection_test1( t, ",U", "2, nobel, président, député, prix", "président de Pologne,député à l'Assemblée, Prix Nehru, Nobel de la paix" )
	t = multiple_selection_test1( t, opt, "3, député, prix, nobel, président", "président de Pologne,député à l'Assemblée, Prix Nehru, Nobel de la paix" )
	t = multiple_selection_test1( t, ",=", "3,député, prix,nobel,président", "président de Pologne,député, Prix Nehru,Nobel" )
	t = multiple_selection_test1( t, ",t", "3,député, prix,nobel,président", "président de Pologne,député, Prix Nehru,Nobel" )
	t = multiple_selection_test1( t, ";", "3; prix; nobel;+1; président; député", "président de Pologne;député à l'Assemblée; Prix Nehru; Nobel de la paix" )
	t = multiple_selection_test1( t, ";", "3; président; nobel; député; prix", "président de Pologne;député à l'Assemblée; Prix Nehru; Nobel de la paix" )
	t = multiple_selection_test1( t, ";t", "3;député; prix;nobel;président", "président de Pologne;député; Prix Nehru;Nobel" )
	t = multiple_selection_test1( t, ";U", "2; nobel; président; député; prix", "président de Pologne;député à l'Assemblée; Prix Nehru; Nobel de la paix" )
	t = t .. CA.Te()
	return t
end -- function CA.multiple_selection_test(t)

------------------------------------------------------------
-- Datas wikidata from mw.wikibase
------------------------------------------------------------

function p.import_wikidata(args_known, id)
-- wikidata structure p.father = mw.wikibase.label( "Q" .. entity.claims.p107[0].mainsnak.datavalue.value["numeric-id"])
	if type(args_known) ~= "table" then args_known = CA.args_known end
	local t, adr, val, proplabel = "", nil, nil, nil
	local wd = {}
	local wd_mng = {} -- wikidata manager
	if type(id) == "string" then -- Le 2013-06-19 acces restreint par Lua pour un autre élément
		-- https://bugzilla.wikimedia.org/show_bug.cgi?id=49805
		-- T49930 Allow accessing data from a Wikidata item not connected to the current page - arbitrary access (tracking)
		-- Reported: 2013-06-19 10:52 UTC by Rical
		wd_mng.wd_id = id
	--	t = t .. CA.ta("wd_error", wd.wd_error)
	--	t = t .. "\n* " .. CA.ta("wd_id", wd_mng.wd_id)
	--	t = t .. " see [https://phabricator.wikimedia.org/T49930 bug T49930]" -- DEBUG
		wd_mng.t = t or ""
	--	wd_mng.wd_error = "wd_id"
--		wd_mng.wd_id = nil -- anti-bug 47930
--		p.args_wikidata = wd
--		return wd, t, wd_mng
	end
	wd_mng.wd_base = mw.wikibase
	if not wd_mng.wd_base then -- Wikidata disponible ?
		wd_mng.wd_error = "wd_base"
		t = t .. CA.ta("wd_error", wd_mng.wd_error)
		wd_mng.t = t
		p.args_wikidata = wd
		return wd, t, wd_mng
	end
	--
	local function getEntityObject_xpcall( wd_id ) -- wikidata arbitrary access must not fail and block the page.
	--	local entity = mw.wikibase.getEntityObject( wd_id )
	--	if type(wd_id) ~= "string" then wd_id = nil end
		local success, entity = pcall( mw.wikibase.getEntity, wd_id ) -- pcall or xpcall can run any function without blocking page.
		if success then return entity else return nil end
		-- T49930 Allow accessing data from a Wikidata item not connected to the current page - arbitrary access (tracking)
	end
--	wd_mng.wd_entity = getEntityObject_xpcall( wd_mng.wd_id )
	if getEntityObject_xpcall( wd_mng.wd_id ) then -- Page Wikidata disponible ?
		wd_mng.wd_entity = mw.wikibase.getEntity( wd_mng.wd_id ) -- ex getEntityObject
	end
	--
--	wd_mng.wd_entity = mw.wikibase.getEntityObject( ) -- anti-bug 47930
	if wd_mng.wd_entity then -- Page Wikidata disponible ?
		if wd_mng.wd_entity.claimRanks then
			wd_mng.wd_claimRanks = wd_mng.wd_entity.claimRanks
			wd_mng.wd_RANK_TRUTH = wd_mng.wd_entity.claimRanks.RANK_TRUTH
			wd_mng.wd_RANK_PREFERRED = wd_mng.wd_entity.claimRanks.RANK_PREFERRED
			wd_mng.wd_RANK_NORMAL = wd_mng.wd_entity.claimRanks.RANK_NORMAL
			wd_mng.wd_RANK_DEPRECATED = wd_mng.wd_entity.claimRanks.RANK_DEPRECATED
			-- https://www.mediawiki.org/wiki/Extension:WikibaseClient/Lua#mw.wikibase.entity.claimRanks
			-- Return the normal ranked claims with the property id P5
			-- entity:formatPropertyValues( 'P5', { mw.wikibase.entity.claimRanks.RANK_NORMAL } )
			-- Return all claims with id P123 (as the table passed contains all possible claim ranks)
			-- entity:formatPropertyValues( 'P123', mw.wikibase.entity.claimRanks )
			-- mw.wikibase.entity.claimRanks = RANK_TRUTH, RANK_PREFERRED, RANK_NORMAL, RANK_DEPRECATED
		end
		wd_mng.sitelink = wd_mng.wd_entity:getSitelink( )
		wd_mng.label = wd_mng.wd_entity:getLabel( ) -- Returns a string, like "Berlin" with 'de'
		wd_mng.props = wd_mng.wd_entity:getProperties() -- or {} Returns a table like: { "P123", "P1337" }
		wd_mng.props_maxn = tostring(table.maxn( wd_mng.props ) )
		wd_mng.props_txt = "T " .. mw.text.listToText( wd_mng.props )
		--
		wd_mng.props_list = " "
		for i, pp in ipairs(wd_mng.props) do -- Properties list
			wd_mng.formatPropertyValues = wd_mng.wd_entity:formatPropertyValues(  pp )
			if wd_mng.wd_claimRanks then
				wd_mng.formatPropertyValues_claimRanks = wd_mng.wd_entity:formatPropertyValues(	 pp, { wd_mng.wd_claimRanks } )
			end
			if wd_mng.wd_RANK_TRUTH then
				wd_mng.formatPropertyValues_RANK_TRUTH = wd_mng.wd_entity:formatPropertyValues(	 pp, { wd_mng.wd_RANK_TRUTH } )
			end
			if wd_mng.wd_RANK_PREFERRED then
				wd_mng.formatPropertyValues_RANK_PREFERRED = wd_mng.wd_entity:formatPropertyValues(	 pp, { wd_mng.wd_RANK_PREFERRED } )
			end
			if wd_mng.wd_RANK_TRUTH then
				wd_mng.formatPropertyValues_wd_RANK_NORMAL = wd_mng.wd_entity:formatPropertyValues(	 pp, { wd_mng.wd_RANK_NORMAL } )
			end
			if wd_mng.wd_RANK_TRUTH then
				wd_mng.formatPropertyValues_RANK_DEPRECATED = wd_mng.wd_entity:formatPropertyValues(  pp, { wd_mng.wd_RANK_DEPRECATED } )
			end
			val = wd_mng.formatPropertyValues.value
			proplabel = wd_mng.formatPropertyValues.label
			wd_mng.props_list = wd_mng.props_list .. " , " .. i .. "=" .. pp .. "=" .. CA.ta(proplabel, val)
			-- .. "/" .. CA.ta(proplabel, wd_mng.formatPropertyNORMAL)
			if pp == "P569" then CA.TimeName = pp .. ".1.mainsnak" ; CA.TimeTest = wd_mng.wd_entity.claims[pp][1].mainsnak end -- birthyear P569 for test DEBUG
			if pp == "P570" then CA.TimeName = pp .. ".1.mainsnak" ; CA.TimeTest = wd_mng.wd_entity.claims[pp][1].mainsnak end -- deathyear P570 for test DEBUG
		end
	else -- entity unknown for the page. entity introuvable pour la page.
		wd_mng.wd_error = "wd_entity"
		t = t .. CA.ta("wd_error", wd_mng.wd_error)
		wd_mng.t = t
		p.args_wikidata = wd
		return wd, t, wd_mng
	end
	if wd_mng.wd_error then -- Show an error, Signaler une erreur
		if wd_mng.wd_error == "wikidata_props" then err = CA.str_vars("err_wikidata_error", "wikidata_props") end
		if wd_mng.wd_error == "wd_base" then err = CA.str_vars("err_wikidata_wikibase") end
		if wd_mng.wd_error == "wd_entity" then err = CA.str_vars("err_wikidata_getEntity", wd_mng.wd_entity) end
		if wd_mng.wd_error == "wd_property" then err = CA.str_vars("err_wikidata_property", wd_mng.wd_property) end
		-- elem -> id
		if wd_mng.wd_error == "wd_id" then err = CA.str_vars("err_wikidata_getEntityObject", wd_mng.wd_id) end
		err = CA.err_add(err)
		t = t .. err
		local err_wikidata_cat = p.cat_add("err_wikidata_cat")
	else
		for key, pp in pairs(args_known) do -- Pour tous les paramètres connus
			if pp.prop then
				if pp.prop == "label" then val = wd_mng.label
				elseif pp.prop == "sitelink" then val = wd_mng.sitelink
				elseif pp.prop == "entityid" then val = wd_mng.wd_entity.id
				elseif pp.prop == "description" then val = wd_mng.wd_base.description( wd_mng.wd_id ) -- wikibase.description( id )
				elseif pp.prop == "claims" then val = wd_mng.wd_base.renderSnak( wd_mng.wd_entity['claims'] ) -- Returns the given Snaks formatted as wiki text.
			--	local entity = mw.wikibase.getEntityObject()
			--	local snaks = entity['claims']['P342'][1]['qualifiers']
			--	mw.wikibase.renderSnaks( snaks ) -- Returns the given Snaks formatted as wiki text.
				else
					wd_mng.formatPropertyValues = wd_mng.wd_entity:formatPropertyValues( pp.prop ) -- "P" .. pp.prop
					-- Returns a table like: { value = "Formatted claim value", label = "Label of the Property" }
					if wd_mng.wd_RANK_TRUTH then
						wd_mng.formatPropertyValues = wd_mng.wd_entity:formatPropertyValues(  pp, { wd_mng.wd_RANK_TRUTH } )
					end
					if wd_mng.wd_claimRanks then
						wd_mng.formatPropertyValues = wd_mng.wd_entity:formatPropertyValues(  pp, { wd_mng.wd_claimRanks } )
					end
					val = wd_mng.formatPropertyValues.value
					proplabel = wd_mng.formatPropertyValues.label
				end
				if pp.format == "year" then
					val = mw.ustring.sub( val, -4, -1 )
				end
				wd[key] = val
				t = t .. CA.ta(key, val) .. CA.ta(key, wd_mng.formatPropertyNORMAL)
			end
		end
	end
--	wd.lastname = wd.label
	wd_mng.t = t
	CA.args_wikidata = wd
	return wd, t, wd_mng
end -- function p.import_wikidata(args_known, id)

function CA.wikidata_details_test(t)
	local t = t or "\n* '''wikidata_details_test''' :"
	local wd, tw, wd_mng
	--	t = t .. CA.arbitrary_access_test()
	wd, tw, wd_mng = p.import_wikidata( p.args_known )
	if wd_mng and wd_mng.wd_entity and wd_mng.wd_entity.id then
		t = t .. "\n* Entity.id: " .. (wd_mng.wd_entity.id or "") -- entityid
		local structured_data_txt = CA.str_vars("structured_data_txt")
		t = t .. ' <span style="color:#232388; font-size:140%; line-height:120%; ">⦁&nbsp;&nbsp;</span>[[d:' .. wd_mng.wd_entity.id .. '|' .. structured_data_txt .. ']]<br>'
	end
	t = t .. "\n* Asked properties: " .. tw
	t = t .. "\n* Properties number maxn: " .. (wd_mng.props_maxn or "")
--	t = t .. "\n* Properties txt: " .. (wd_mng.props_txt or "")
	t = t .. "\n* Properties list: " .. (wd_mng.props_list or "")
	return t
end -- function CA.wikidata_details_test(t)

function CA.arbitrary_access_test(t)
-- T49930 Allow accessing data from a Wikidata item not connected to the current page - arbitrary access (tracking)
	t = t or ""
	local function one_arbitrary_access(id)
		local wd, tw, wd_mng = p.import_wikidata(CA.args_known, id) -- p.args_known, id) -- example Q535 = Victor Hugo = Victor Marie Hugo
		local args_import, tx = CA.import_arguments() -- p.args_known_default, CA.args_source )
		if wd_mng and wd_mng.wd_entity and wd_mng.wd_entity.id then
			t = "\n* " .. CA.str_vars("wikidata_arbitrary_access_text")
			.. (wd_mng.wd_id or "") .. ", " .. (wd_mng.title or "") .. " ( " .. (wd.birthyear or "") .. " - " .. (wd.deathyear or "") .. " ) "
			local structured_data_txt = CA.str_vars("structured_data_txt")
			t = t .. ' <span style="color:#232388; font-size:140%; line-height:120%; ">⦁&nbsp;&nbsp;</span>[[d:' .. wd_mng.wd_entity.id .. '|' .. structured_data_txt .. ']]'
		else
			t = "<br>* No arbitrary access for: " .. tostring(id)
		end
		return t
	end
	t = t .. one_arbitrary_access("Q535") -- Q535 = Victor Hugo = Victor Marie Hugo
	t = t .. one_arbitrary_access("Q899264") -- Q899264 = Martin Fleischmann
--	t = t .. one_arbitrary_access("Martin Fleischmann") -- Martin Fleischmann(Q899264)
--	t = t .. one_arbitrary_access("x y z t") -- Q899264 = Martin Fleischmann
	-- Restore normal arguments after test
	CA.import_wikidata()
	CA.import_arguments()
	CA.interact_args_final() -- Interactions between argumensts
	return t
end -- function CA.arbitrary_access_test(t)

function p.verify_args_tables(_known, _source)
	-- initialize all the values to "" in arg table
	-- p.init_args(args_known, p.args_source, "fr")
	if type(_known) == "table" then
		CA.args_known = _known
	end
	if type(CA.args_known) ~= "table" then
		CA.err_add("err_no_known_arguments")
		p.cat_add("cat_no_known_arguments")
		return CA.error_color(" Internal error : no source or no known arguments ! ")
	end
	if type(_source) == "table" then
		CA.args_source = _source
	end
	if type(p.args_source) ~= "table" then
		CA.err_add("err_no_source_arguments")
		CA.cat_add("cat_no_source_arguments")
		return CA.error_color(" Internal error : no source or no known arguments ! ")
	end
	return
end -- function p.verify_args_tables(_known, _source)

function p.init_args(args_known, args_source, _user_lang, _msgs_list, _wiki_lang, _wiki_translations)
	-- p.init_args(args_known, p.args_source, "fr")
	p.init_spaces()
	--
	if type(args_known) ~= "table" then args_known = p.args_known end
	if type(args_known) ~= "table" then args_known = p.args_known_default end
	if type(args_known) == "table" then p.args_known = args_known end
	--
	if type(args_source) ~= "table" then args_source = p.args_source end
	if type(args_source) ~= "table" then args_source = p.args_source_example end
	if type(args_source) == "table" then p.args_source = args_source end
	--
	local err = CA.verify_args_tables(args_known, args_source)
	if err then return err end
	--
	local _args = args_source
--	CA.trac_lang("CA.init_args:") -- DEBUG
--	CA.trac_lang_t = CA.trac_lang_t .. CA.ta("_args.c", _args.c) .. CA.ta("_args.wikilang", _args.wikilang)
--	CA.trac_lang("CA.init_args end:", args_source)
	--
	-- Limite de differences des arguments proposables à l'utilisateur
--	p.max_nearest_argument = tonumber( p.i18n[p.wiki_lang]["max_nearest_argument"] ) or 3
	--
--	CA.erron = true -- Errors actived or no. Errores activo o no. Erreurs activées ou non.
--	p.errors_list = {} -- initialise la collecte des erreurs
	return p.args_known, p.args_source, p.user_lang, p.i18n[p.wiki_lang], p.i18n
end -- function p.init_args(args_known, args_source, _user_lang, _msgs_list, _wiki_lang, _wiki_translations)

-- Global arguments process. Processus global des arguments :
-- args = frame...
-- args_known = p.args_known
-- CA.wiki_translations = i18n.wiki_lang
-- wd = wikidata
-- import_args en 2 boucles : 1 Importer des args normaux
-- import_args en 2 boucles : 2 Traiter les anomalies apres wikidata et import
-- interact args
-- utilisation normale
-- tests unitaires

function CA.import_arguments(args_known, args_source, wiki_translations, args_wikidata)
	--	import all arguments from template, or wikidata
	local args_import = {} -- table d'arguments internationale simple
	local err = nil
	local cats = ""
	-- entrees all_versions et erreurs
	if type(args_known) ~= "table" then args_known = CA.args_known end
	if type(args_known) ~= "table" then args_known = CA.args_known_default end
	if type(args_source) ~= "table" then args_source = CA.args_source end
	if type(wiki_translations) ~= "table" then wiki_translations = CA.wiki_translations end
	if type(args_wikidata) ~= "table" then args_wikidata = CA.args_wikidata end
	CA.import_arguments_err = ""
	CA.import_arguments_track = ""
	local err = CA.verify_args_tables(args_known, args_source)
	if err then return args_import, err end
	--
	local err, er1, t2 = "", "", ""
	local key, argval, argid = "kkk", "xxx", ""
	local argknw, arglingual, argreceived = nil, "", ""
--	local trouve1, min1, trouve2, min2 = nil, 999, nil, 999
	local argm = {} -- argument usuel
--	CA.erron = true -- Errors actived or no. Errores activo o no. Erreurs activées ou non.
--	CA.errors_list = {} -- collecte de toutes les erreurs
	local key_N, key_NN = 0, 0
	local arg_found, rec_found, already_found = false, false, false
	--
	for key_known, argm in pairs(args_known) do
		argm.found = 0 -- Initialiser d'abord tous les arguments connus
	end
	--
	CA.args_unknown = mw.clone(CA.args_source) -- unknown arguments to detect are source arguments without known arguments.
	local key_known_init = nil
--	local argm_orig = nil
	local argm_syn = nil
	-- Try to read all known arguments. Intentar leer todos los argumentos conocidos. Essayer de lire tous les arguments connus.
	for key_known, argm in pairs(args_known) do
		argm.src = nil
		argm.trk = " n"
		key_known_init = key_known
		-- Initialiser d'abord chaque argument connu
		CA.import_arguments_track = tostring(CA.import_arguments_track) .. " - " .. tostring(key_known)
		--
--		argm_orig = nil -- DEBUG
		argm_syn = args_known[argm.keyword]
		if argm.syn == 2 then
			-- Name an unnamed argument, positional, by its synonym. Nommer un argument non nommé, positionné, par son synonyme.
			-- Rename a named argument, by its synonym. Renommer un argument nommé, par son synonyme.
--			argm_orig = key_known -- DEBUG
			key_known = argm.keyword
			--	synonyms are defined with the stamp "syn = 2", then here stamp the basic argument with "syn = 1".
			args_known[key_known].syn = 1
			argm = args_known[key_known] -- new variable argm
			argm.src = nil
			argm.trk = "s"
			CA.import_arguments_track = tostring(CA.import_arguments_track) .. ">" .. tostring(key_known)
		end
		-- initialiser un argument
		arg_found = false
		argval = nil
		argm.trk = argm.trk.."="
		-- importer un argument wikidata
		if args_wikidata[key_known] then
			argval = args_wikidata[key_known]
			argm.src = "wd"
			argm.trk = argm.trk.."w"
			if argm_orig then
			--	args_known[argm_orig].src = "wd"
			--	args_known[argm_orig].trk = (args_known[argm_orig].trk or "").."w"
			end
--			if argm_orig then argm_orig = key_known end -- DEBUG
			arg_found = true
			CA.import_arguments_track = CA.import_arguments_track .. "='''" .. tostring(argval) .. "''' "
		end
		-- import a source argument. importer un argument source.
		arglingual = wiki_translations[key_known]
		CA.import_arguments_track = CA.import_arguments_track .. "/" .. tostring(arglingual)
		if arglingual then -- The argument name has a translation in wiki language
		--	if CA.frame[arglingual] then
		--		argval = CA.frame[arglingual]
		--		argm.src = "inv" -- arg comes from invoke
		--	end
			if CA.frame.args[arglingual] then
	--			argval = CA.frame.args[arglingual]
	--			argm.src = "inv" -- arg comes from invoke
	--			arg_found = true
			end
			if CA.frame:getParent().args then
	--			argval = CA.frame:getParent().args[arglingual]
	--			argm.src = "tpl" -- arg comes from template
	--			arg_found = true
			end

			if args_source[arglingual] then -- the argument come from template else from invoke else from wikidata
	--		if argval then -- the argument value exist and come from template else from invoke else from wikidata

				argval = args_source[arglingual]
				argm.src = "args"
				argm.trk = argm.trk.." a"
				arg_found = true
				local arg_values = wiki_translations[argm.arg_values]
				if argm.keys_values and arg_values then
					-- The argument is limited to multiple values with arg_values and keys_values, and the values are defined.
					local pos = string.find(arg_values, argval)
					if pos then
						-- The value of the argument is in the multiple values of the arguments.
				--		argm.src = "args"
						argm.trk = argm.trk.."m"
				--		arg_found = true
						if argm_orig then
						--	args_known[argm_orig].src = "args"
						--	args_known[argm_orig].trk = (args_known[argm_orig].trk or "").."d"
						end
					else
						CA.err_add("args_values_err", argm.keyword, argval, arg_values)
				--		argval = nil
					end
				else
				--	argm.src = "args"
					argm.trk = argm.trk.."c"
				--	arg_found = true
					if argm_orig then
					--	args_known[argm_orig].src = "args"
					--	args_known[argm_orig].trk = (args_known[argm_orig].trk or "").."c"
					end
				end
				CA.import_arguments_track = CA.import_arguments_track .. "='''" .. tostring(argval) .. "''' "
			end -- not args_source[arglingual] is normal
		else -- internal error and category
			CA.err_add("err_module_miss_i18n", key_known)
			-- Generate a category to list all modules with missing translation
			cats = cats .. CA.cat_add( "err_module_miss_i18n_cat" )
		end
		--
		key_N = tonumber(key_known_init)
		if key_N and not args_known[key_N] then
--			CA.err_add("err_too_unnamed_arguments", key_N, argval)
		end
		-- Record the argument. Guarde el argumento. Enregistrer l'argument.
		if arg_found == true then
			argm.found = argm.found + 1 -- compter les arguments redéfinis
			argm.val = argval
			args_import[key_known] = argval -- table d'arguments internationale simple
			if CA.args_unknown[arglingual] then
				CA.args_unknown[arglingual] = nil -- unknown arguments are source arguments without known arguments.
			end
		end
	end
	-- CA.import_arguments: after import itself, some surrounding checks.
	for key_known, argm in pairs(args_known) do -- For all known arguments
		-- Redefined arguments. Argumentos redefinieron. Arguments redéfinis.
	--	if argm.found and (argm.found > 1) then
	--		CA.err_add("err_value_re_defined", argm["keyword"])
	--		cats = cats .. CA.cat_add("cat_usage_error")
	--	end
	--	synonyms are defined with the stamp "syn = 2", then here stamp the basic argument with "syn = 1".
	--	if argm.keyword and args_known[argm.keyword] and args_known[argm.keyword].syn then -- if the argument is a synonym, increase the found level
		if argm.keyword and args_known[argm.keyword] and args_known[argm.keyword].syn then -- if the argument is a synonym, increase the found level
			if argm.found and (argm.found > 2) then
				CA.err_add("err_value_re_defined", (argm["keyword"] or "**") ) --.. ">2")
				cats = cats .. CA.cat_add("cat_usage_error")
			end
		else
			if argm.found and (argm.found > 1) then
				CA.err_add("err_value_re_defined", (argm["keyword"] or "**") ) --.. ">1")
				cats = cats .. CA.cat_add("cat_usage_error")
			end
		end
		-- need = 0 not necessary argument
		-- need = 1 necessary from argument
		-- need = 2 necessary from argument or module interaction
		-- Missing Arguments. Argumentos que faltan. Arguments manquants.
		if argm.need and (argm.need == 1) and (not argm.val) then
			arglingual = wiki_translations[key_known]
			if arglingual then
				CA.err_add("err_need_value", arglingual)
				cats = cats .. CA.cat_add("cat_usage_error")
			end
		end
	end
	--
	-- Tous les arguments sources ont-ils été utilisés ?
	--	CA.args_unknown -- unknown arguments are source arguments without known arguments.
	local args_list = p.similar_args_list(CA.args_known)
	for key_src, val_src in pairs(CA.args_unknown) do -- For all unknown source arguments.
		arglingual = tostring(key_src) -- chercher l'argument traduit
		key_N = tonumber(arglingual)
		-- No error for unmamed arguments
		-- Pas d'erreur pour les arguments non nommés
		if not key_N then
			CA.err_add("err_unknown_argument", arglingual, val_src)
			cats = cats .. p.cat_add("cat_usage_error")
			-- "Erreur : Le paramètre '''%1''' est inconnu dans ce modèle. Vérifier ce nom ou signaler ce manque.",
			-- Cherche un argument connu et de nom ressemblant
			local diffmaxi = p.similar_args_diffmaxi( string.len(arglingual) )
			local trouve1, min1, trouve2, min2 = p.similar_args_search(arglingual, args_list)
			if min1 and (min1 <= diffmaxi) then
				CA.err_add("err_nearest_argument", trouve1)
			end
			if min2 and (min2 <= diffmaxi) then
				CA.err_add("err_nearest_argument", trouve2)
			end
		end
		key_N = tonumber(arglingual)
		if key_N and not args_known[key_N] then
			CA.err_add("err_too_unnamed_arguments", key_N, val_src)
			p.cat_add("cat_usage_error")
		end
	end -- For all unknown source arguments.
	CA.nowyear = tonumber(os.date("%Y") ) -- local now_date = os.date("%Y-%m-%d %H:%M:%S")
	args_import.nowyear = CA.nowyear
	CA.args_import = args_import
--	CA.trac_lang("CA.import_arguments:", args_import) -- DEBUG
	return CA.args_import
end -- function CA.import_arguments(args_known, args_source, wiki_translations, args_wikidata)

------------------------------------------------------------
-- Argts : Generate documentation. Generar documentación. Générer la documentation.
------------------------------------------------------------

function p.generDoc1(paramName, n, paramData, opt)
	-- t = t .. generDoc1("nom", "ws-name", "docline docdef")
	-- Normaliser les parametres et signaler les erreurs
	-- Toujours afficher quelque chose dans la documentation du module ou du modèle.
	if type(paramName) ~= "string" or type(paramData) ~= "string" then -- signaler l'erreur
		CA.err_add("err_generDoc1_paramName", tostring(paramName) )
		return " "
	end
	opt = " "..tostring(opt).." " -- transparent, sinon peut donner nil, non génant
	--
	-- former le texte à afficher
	local newline = "<br>"
	-- Chaque parametre non nommé est traité par son synonyme.
	if n then return "" end
	if p.option("docline", opt) then newline = "" end
	local argm = p.args_known[paramName]
	if argm then
		--[ [
		if CA.docolor then --	Document the data sources by colors
			if argm.src == "wd" then
				paramData = p.wikidata_color(paramData)
			elseif argm.src == "args" then
				paramData = p.invoke_color(paramData)
		--	elseif argm.src == "inter" then
		--		paramData = p.inter_color(paramData)
			else
				paramData = p.other_color(paramData)
			end
		end
		--] ]
		paramData = paramData -- .. (argm.trk or "")
	end
	return " | " .. paramName .. " = <b>" .. paramData .. "</b>" .. newline
end -- function p.generDoc1(paramName, paramId, opt)

function p.generDoc(opt, args_final, module_name)
	-- List of paramètres for the module documentation
	-- Lister des paramètres pour la documentation du module
	-- "docview" ajouter le panneau de documentation
	-- "docmin" quelques paramètres de base
	-- "docdef" seulement les paramètres définis, ayant une valeur non nulle
	-- "docmax" tous les paramètres connus
	-- "docnotice" generer les documentations des notices
	-- "docline" mettre tous les paramètres sur une seule ligne
	-- "docsrc" mettre les paramètres en couleurs selon les sources
	-- p.options = " : docdata docmin docdef docmax docline docview docafter docnotice docsrc" -- for documentation
	-- p.options = " erron noerr nobox nocat " -- without normal result
	-- p.options = " debug tests en es fr " -- for debug or enforce language
	local args_known = CA.args_known
	local err = CA.verify_args_tables(args_known, CA.args_source)
	if err then return args_import, err end
	if type(module_name) ~= "string" then module_name = p.module_name end
	if type(module_name) ~= "string" then module_name = p.frame:getTitle() end -- main module, example "Auteur"
	if type(module_name) ~= "string" then module_name = "ControlArgs" end
	local n, t, val = 0, "", ""
	if type(args_final) ~= "table" then t = t.."err_args_final="..type(args_final).."<br>" end -- optional arguments
	if type(args_final) ~= "table" then args_final = p.args_final end -- optional arguments
	local key, argknw, argval, arglingual = "", "", ""
	local lst = true
	local lst_doc, lst_1, lst_t = {}, {}, ""
	for key, parm in pairs(args_final) do -- for all known arguments
		val = ""
		n = tonumber(key)
		if n then key = tostring(parm["keyword"]) end -- key for unnamed arguments, in numeric sort
		argknw = args_known[key]
		arglingual = tostring(CA.user_translations[key]) -- multilingual name of the arg in the template
		if arglingual == "nil" then arglingual = key end -- if argument is translatable
		val = parm -- tostring(args_import[key])
		if not p.isDef(val) then val = "" end
		lst = false
		opt = opt .. " docdef " -- optional display
		if p.option("docmin", opt) and (argknw["list"] == 1) then lst = true end
		if p.option("docdef", opt) and (val ~= "") then lst = true end
		if p.option("docmax", opt) then lst = true end
		if not args_known[key] then lst = false end
		if key and args_known[key] and args_known[key].typ == "sys" then lst = false end
		if lst then -- list if found and selected
			lst_t = p.generDoc1(arglingual, n, val, opt)
			table.insert(lst_doc, {lst_t = lst_t, key = key, user_lang_key = CA.user_translations[key] or "trans"} )
		end
	end
	table.sort(lst_doc, function (a, b) return (a.user_lang_key < b.user_lang_key) end ) -- alphabetic sort of translated arguments
	for i, parm in ipairs(lst_doc) do -- List all found arguments
		t = t .. parm.lst_t or "lst_t"
	end
	t = "\n{{" .. module_name .. " " .. t .. "}}" -- <br>
	return t
end -- function p.generDoc(opt, args_final, module_name)

-- Interact parameters in international args_final
function CA.interact_args_final(args_final)
	-- args_final = CA.interact_args_final(args_import)
	if type(args_final) ~= "table" then args_final = CA.args_final end
	if type(args_final) ~= "table" then args_final = CA.args_import end
	local a = args_final
	local i = {} -- interact
--	t = "\n* begin :" .. CA.ta("initiale", a.initiale) .. CA.ta("firstname", a.firstname) .. CA.ta("lastname", a.lastname) .. CA.ta("title", a.title)
	--
	local tit = nil
	if not a.title then -- If title is undefined, enforce it.
		if a.lastname and a.firstname then
			tit = a.firstname .. " " .. a.lastname
		end
		i.title = a.label or tit or a.sitelink or a.lastname or CA.module_name
	end
	--
	if not a.initiale then -- If initiale is undefined, enforce it.
		-- if absent, default initiale come from the last word of title
		local title = a.title or i.title
		if title then
			local tab = mw.text.split(title, '%s') -- table of words
			local max = table.maxn( tab )
			i.initiale = tab[max] -- select the last word
			i.initiale = string.sub( i.initiale, 1, 1 ) -- select the first letter
			i.initiale = string.upper( i.initiale or "" )
		end
	end
	--
	-- if absent, synonym of basic arguments, syn = 2
	if not a.firstname then i.firstname = (i.firstname2 or a.firstname2) end
	if not a.lastname then i.lastname = (i.lastname2 or a.lastname2) end
	if not a.firstname2 then i.firstname2 = (i.firstname or a.firstname) end
	if not a.lastname2 then i.lastname2 = (i.lastname or a.lastname) end
	--
	if a.birth and not a.birthyear then
		local tt, err = p.date_to_part(a.birth, CA.str_vars("date_to_part_format"), "yyyy")
		if tt then i.birthyear = tt else
			CA.err_add(err, CA.str_vars("birthyear"), "yyyy")
			CA.cat_add("date_to_part_call_cat")
		end
	end
	if a.selectversions then
		i.selectversions = a.selectversions
	end
	if a.allversions then
		i.allversions = a.allversions
	--	What to do when the user or the template ask a versionall or a selectversions ?
	--	1 : Build the submodules before import all arguments and do nothing.
	--	2 : Build the submodules only after import all arguments.
	--	CA.versions.selector = args_final.selectversions
	--	CA.versions.all_versions = args_final.allversions
	--	The versions mecanism can also use this argument
	end
	--
	-- memorize interactions in CA.args_final and show errors or messages
	local n = 0
	for key, val in pairs(i) do
		local args_kwn = CA.args_known[key]
		if args_kwn then
			args_final[key] = val -- = i[key]
			args_kwn.src = "inter"
			args_kwn.trk = args_kwn.trk.." i"
			n = n + 1
			if (args_kwn.need == 2) and not a[key] then --
				-- need=2 necessary from argument or module interaction
				CA.msg_add("msg_auto_val_warning", CA.user_translations[key], val)
			end
		else
			CA.err_add("msg_auto_val_unknown", CA.wiki_translations[key], val)
		--	CA.err_add("msg_unknown_auto_arg", CA.wiki_translations[key], val)
		end
	end
	if CA.args_known.title then CA.args_known.title.trk = (CA.args_known.title.trk or "").."i="..n end
--	t = t .. "\n*: end :" .. CA.ta("initiale", a.initiale) .. CA.ta("firstname", a.firstname) .. CA.ta("lastname", a.lastname) .. CA.ta("title", a.title)
	CA.args_final = args_final
--	CA.invoke_options =
--	CA.trac_lang("interact_args_final end:", CA.args_final)
	return args_final, t
end -- function CA.interact_args_final(args_final)

function CA.sources_of_datas_colors()
	local res = ""
	if CA.docolor then --	Document the data sources by colors
		local sources_of_datas = CA.str_vars("sources_of_datas")
		--	sources_of_datas = "Informations from: /Wikidata, /template or module, /other, /message, /error",
		local splitxt = mw.text.split(sources_of_datas, "/", true)
		res = res .. splitxt[1]	 .. " ''' " .. CA.wikidata_color(splitxt[2]) .. CA.invoke_color(splitxt[3]) .. CA.other_color(splitxt[4]) .. CA.message_color(splitxt[5]) .. CA.error_color(splitxt[6]) .. ".''' <br>"
	else -- Document the data sources in black on white
--		local sources_of_datas = CA.str_vars("sources_of_datas")
--		res = res .. string.gsub(sources_of_datas, "/", "") .. ".''' <br>"
	end
	return res
end -- function CA.sources_of_datas_colors()

function p.docbox_namespace_error_and_cat()
	-- If DocBox is displayed out of Module or Template namespace, generate an error and a category "Module with usage error"
	-- Si DocBox est affiché hors de l'espace de nom Module ou Modèle, générer une erreur et une catégorie "erreur d'utilisation"
	local namespace = mw.title.getCurrentTitle().namespace
	local nsText = mw.title.getCurrentTitle().nsText
	if namespace ~= 10 and namespace ~= 828 then -- ns:Template and ns:Module
		CA.err_add("docbox_namespace_error_err", nsText)
		CA.cat_add("docbox_namespace_error_cat")
	end
	return
end -- function p.docbox_namespace_error_and_cat()

function p.normal_box(args_final, title)
	local res = ""
	local warning_versions = CA.versions.warning_short()
	if type(args_final) ~= "table" then args_final = p.args_final end
	if not title then title = args_final.title end
	if not title then title = "TITLE" end
	res = res .. "<center><b><big>" .. title .. "</big></b><br></center>"
	local flag_of_image = CA.str_vars("flag_of_image")
	if type(flag_of_image) == "string" then -- and image ~= ""
		flag_of_image = '[[File:' .. flag_of_image .. '|frameless|40x40px||class=photo]] '
	else
		flag_of_image = "&nbsp;"
	end
	res = res .. flag_of_image
	res = res .. "L'auteur	'''" .. tostring(args_final.firstname) .. " " .. tostring(args_final.lastname) .. "''' "
	res = res .. " est mort en '''" .. tostring(args_final.deathyear) .. "'''"
	res = res .. CA.ta("firstname", args_final.firstname)
	res = res .. CA.ta("lastname", args_final.lastname)
	res = res .. CA.ta("title", args_final.title)
	res = res .. CA.ta("language_cat", args_final.language) -- |language=french,italian,chinese
	res = res .. CA.ta("occupation_cat", args_final.occupation) -- |occupation=Académiciens,Personnalités politiques
	res = res .. CA.ta("description", args_final.description)
	res = warning_versions .. '<div style="margin-right:5px; box-shadow:0.2em 0.3em 0.2em #B7B7B7; background-color:#F1F1DE; padding:0.3em; width=90%; overflow-x:hidden; ">' .. res .. '</div>'
	return res
end -- function p.normal_box(args_final, title)

function CA.get_editstate()
	local t = ""
	local mwtitle = mw.title.getCurrentTitle()
	local url = tostring(mwtitle:canonicalUrl() )
	local n = string.find(url, "/w/")
	CA.EditState = false
	if tonumber( n ) then CA.EditState = true end
	return CA.ta("EditState", CA.EditState) .. " - " .. url
end -- function CA.get_editstate()

function CA.module_init(frame)
	CA.frame = frame or CA.frame
--	Mix arguments from {{#invoke:}}, then arguments from the prioritary template which replace ones from {{#invoke:}}.
	local v2, nn, ni = 0, 0, 0
	local args_tab, mode = {}
	local templat = frame:getParent().args -- arguments from template
	local invoked = frame.args -- arguments from #invoke module
	 -- Mix invoked modified arguments from prioritary template arguments.
	 -- Argument 1 from template become the mode in #invoke. Other i arguments must be shifted.
	args_tab = mw.clone(invoked)
	for key, val in pairs(templat) do
		local key = mw.text.trim(key)
		local val = mw.text.trim(val)
		local i = tonumber(key)
		if i then
			if i == 1 then mode = val -- mode = template[1]
			else args_tab[i-1] = val end -- transmit other unnamed arguments template[i], but shifted because the mode in template[1]
		else args_tab[key] = val end -- transmit any named template arguments template
	end
	CA.args_source = args_tab
	-- Default values of main module version.
	CA.versions.main_versionName = CA.versions.main_versionName or "ControlArgs0"
	CA.versions.main_versionNumber = CA.versions.main_versionNumber or "0.00"
	CA.versions.main_versionDate = CA.versions.main_versionDate or "2013-03-24"
	CA.categories_init()
--	CA.init_user_lang()
	CA.init_args()
--	CA.verif_i18n()
	return
end

------------------------------------------------------------
-- Main interface to templates
-- Interfaz principales de modelos
-- Interface principal avec les modèles
------------------------------------------------------------

------------------------------------------------------------
------------------------------------------------------------
-- TEST part, with use of options, modes and resulting text
-- TEST parte, con el uso de opciones, modos y texto resultante
-- TEST partie, avec l'utilisation d'options, modes et texte résultant
------------------------------------------------------------
------------------------------------------------------------

------------------------------------------------------------
-- Document the tables and their structures. List a table content, with formating.
-- Documentar las tablas y sus estructuras. Lista de un contenido de la tabla, con el formateo.
-- Documenter les tables et leurs structures. Lister un contenu de la table, avec le formatage.
------------------------------------------------------------

-- Dump and format the content of a table and its sub-tables ; with limits in length, deep and exceptions.
-- Listar y formatar le contento de una tabla y su sub-tablas.
-- Lister et formater le contenu d'une table et ses sous-tables ; avec des limites en longueur, profondeur et exceptions.
-- For each (sub)table, list : in first vars, then functions, then sub-tables list, then sub-tables contents

function p.testable_recursive(tbl, uppername, name, level_i, levelmaxi, max_n, exclude1, exclude2, exclude3)
	if type(name) ~= "string" then name = "table" end
	local res, newname, part, shift = "", "", "", ""
	local sep, N = ", ", 0
	local isempty = true
	local levelname = uppername .. "." .. name
	local tot_vars, tot_func, tot_tabs = 0, 0, 0
	local nbr_vars, lst_vars = 0, ""
	local nbr_func, lst_func = 0, ""
	local nbr_tabs, lst_tabs = 0, ""
	local max, lst_subtabs = 0, ""
	local st, vr, fn, tb = "", 0, 0, 0
	local tobreak = nil
	-- limit the number of the level of sub-tables
	level_i = tonumber(level_i)
	if type(level_i) ~= "number" then level_i = 1 end
	levelmaxi = tonumber(levelmaxi)
	if type(levelmaxi) ~= "number" then levelmaxi = 99 end
	if level_i > levelmaxi then
		return p.message_color( " display list levelmaxi=" .. tostring(levelmaxi) ), tot_vars, tot_func, tot_tabs
	end
	max_n = tonumber(max_n) or 999
	shift = string.rep("*", level_i)
	--
	-- Do not list if exclude1, exclude2 or exclude3 are in the table name.
	if type(exclude1) == "string" and exclude1 ~= "" then
		if string.find(name, exclude1) ~= nil then
			return "", tot_vars, tot_func, tot_tabs
		end
	end
	if type(exclude2) == "string" and exclude2 ~= "" then
		if string.find(name, exclude2) ~= nil then
			return "", tot_vars, tot_func, tot_tabs
		end
	end
	if type(exclude3) == "string" and exclude3 ~= "" then
		if string.find(name, exclude3) ~= nil then
			return "", tot_vars, tot_func, tot_tabs
		end
	end
	-- display table error
	if type(tbl) ~= "table" then
		return '<br>The variable "' .. tostring(name) .. '" is not a table.<br>', tot_vars, tot_func, tot_tabs
	end
	--
	-- List and count vars, functions and sub tables
	-- All named elements, including [1] and ["1"].
	-- Tous les élements, y compris [1] et ["1"].
	for k, v in pairs(tbl) do
		k = tostring(k)
		if type(v) == "table" then
			lst_tabs = lst_tabs .. k .. sep
			nbr_tabs = nbr_tabs + 1
			isempty = false
			newname = tostring(k)
			max = max + 1
			-- List recursively (or no) each sub-table
			if level_i < levelmaxi then
				st, vr, fn, tb = p.testable_recursive(v, levelname, newname, level_i+1, levelmaxi, max_n, exclude1, exclude2, exclude3)
				lst_subtabs = lst_subtabs .. st
				tot_vars = tot_vars + vr
				tot_func = tot_func + fn
				tot_tabs = tot_tabs + tb + 1
			end
			if level_i >= levelmaxi then
				local table_listlimit_levelmaxi = CA.str_vars("table_listlimit_levelmaxi", levelmaxi)
				res = res .. " " .. p.message_color( table_listlimit_levelmaxi )
				break
			end
			local sep = ""
			if max == max_n then
				local table_listlimit_max_n = CA.str_vars("table_listlimit_max_n", max_n)
				res = res .. "\n" .. shift .. " Table '''" .. levelname .. "''' " .. p.message_color(table_listlimit_max_n)
				break
			end
--			end
		elseif type(v) == "function" then
			lst_func = lst_func .. k .. sep
			nbr_func = nbr_func + 1
			tot_func = tot_func + 1
			isempty = false
		else -- type(v) == other
			lst_vars = lst_vars .. type(v) .. " - " .. tostring(k) .. " =''' " .. tostring(v) .. "''' " .. sep
			nbr_vars = nbr_vars + 1
			tot_vars = tot_vars + 1
			isempty = false
		end
	end
	res = res .. "\n" .. shift .. " Table '''" .. levelname .. "''', " .. tostring(nbr_vars) .. " vars: " .. lst_vars
	res = res .. "\n" .. shift .. " Table '''" .. levelname .. "''', " .. tostring(nbr_func) .. " functions: " .. lst_func
	res = res .. "\n" .. shift .. " Table '''" .. levelname .. "''', " .. tostring(nbr_tabs) .. " tables: " .. lst_tabs .. lst_subtabs
	return res, tot_vars, tot_func, tot_tabs
end -- function p.testable_recursive(tbl, uppername, name, level_i, levelmaxi, max_n, exclude1, exclude2, exclude3)

-- Auto test of limits of the table list.
-- Auto test des limites de liste de table.
p.tablim = { "one", "two", max1 = "MAX1", max2 = "MAX2", max3 = "MAX3"}
p.tablim.life = { animal = "dog", vegetal = "carot"}
p.tablim.life.animals = { "turtle"}
p.tablim.comfort = { "tv", mobile = "car"}
p.tablim.house = { "kitcheen", "bedroom"}
p.tablim.house.garden = { flower = "rose", nature = "river"}

function p.testable_lister(table, tablename, opt)
	local res = "\n* Content of the '''" .. tostring(tablename) .. "''' table, begin:"
	-- test : check mw content
	if not opt then opt = { levelmaxi = 99 } end
	local levelmaxi = opt.levelmaxi or 99
	local max_n = opt.max_n or 9999
	local exclude1 = opt.exclude1 or ""
	local exclude2 = opt.exclude2 or ""
	local exclude3 = opt.exclude3 or ""
	local tot_vars, tot_func, tot_tabs = 0, 0, 0
	-- limit the number of the level of sub-tables
	level_i = tonumber(level_i)
	if type(level_i) ~= "number" then level_i = 1 end
	levelmaxi = tonumber(levelmaxi)
	if type(levelmaxi) ~= "number" then levelmaxi = 99 end
	if level_i > levelmaxi then
		return "", tot_vars, tot_func, tot_tabs
	end
	res = res .. " ( " .. CA.ta("levelmaxi", levelmaxi) .. CA.ta("max_n", max_n) .. CA.ta("exclude1", exclude1) .. CA.ta("exclude2", exclude2) .. CA.ta("exclude3", exclude3) .. " ) "
	local st, vr, fn, tb = p.testable_recursive(table, "", tablename, 1, levelmaxi, max_n, exclude1, exclude2, exclude3)
	res = res .. st
	tot_vars = tot_vars + vr
	tot_func = tot_func + fn
	tot_tabs = tot_tabs + tb
	res = res .. "\n* Content of the '''" .. tostring(tablename) .. "''' table, end: "
	res = res .. CA.str_vars(" %1 variables, %2 functions, %3 sub-tables.\n", vr, fn, tb)
	return res, tot_vars, tot_func, tot_tabs
end -- function p.testable_lister(table, tablename, opt)

function CA.cat_add_test_1(t, test, user_lang, wiki_lang, key,vals )
	CA.catView = ":"
	CA.init_wiki_lang(wiki_lang) -- res = res ..
	CA.init_user_lang(user_lang) -- res = res ..
	if test == "cat_add" then catext = p.cat_add(key, vals)
	elseif test == "catGroup" then catext = p.catGroup(key, vals) end
	CA.options_to_catView()
	return t .. CA.Tr() .. CA.Td(test or "-") .. CA.Td(user_lang or "-") .. CA.Td(wiki_lang or "-") .. CA.Td(catext or "-")
end

function CA.cat_add_test(t) -- tester les categories
	t = t or "\n* Test the function '''cat_add''' : " .. CA.ta("user_lang", CA.user_lang) .. CA.ta("wiki_lang", CA.wiki_lang)
	local cat_memo, catView, user_lang, wiki_lang = p.categories_list, CA.catView, CA.user_lang_memo, CA.wiki_lang_memo
	local wiki_memo, user_memo = CA.wiki_lang, CA.user_lang
	p.categories_init()
	CA.catView = ":"
	t = t .. CA.Th() .. CA.Tc("Test type") .. CA.Tc("user language") .. CA.Tc("wiki language") .. CA.Tc("Categories")
	t = CA.cat_add_test_1(t, "cat_add", nil, nil, "err_module_miss_i18n_cat")
	t = CA.cat_add_test_1(t, "cat_add", nil, nil, "cat_usage_error")
	t = CA.cat_add_test_1(t, "cat_add", "en", nil, "cat_internal_error")
	t = CA.cat_add_test_1(t, "cat_add", "en", "es", "err_module_miss_i18n_cat")
	t = CA.cat_add_test_1(t, "catGroup", "en", "fr", "language_cat", "french,italian,chinese")
	t = CA.cat_add_test_1(t, "catGroup", "en", "en", "occupation_cat", "Académiciens,Personnalités politiques")
	t = CA.cat_add_test_1(t, "cat_add", "fr", "en", "cat_internal_error")
	t = CA.cat_add_test_1(t, "cat_add", "es", "en", "cat_internal_error")
	t = CA.cat_add_test_1(t, "cat_add", "en", "x-y-z", "cat_internal_error")
	t = CA.cat_add_test_1(t, "cat_add", "x-y-z", "en", "cat_internal_error")
	t = t .. CA.Te()
	CA.init_wiki_user_lang(wiki_memo, user_memo)
	t = t .. "\n* Test the function <b>categories_lister</b> : " .. p.categories_lister(":") -- generate the categories wikitext
	t = t .. "\n* This test reset categories before the test."
	CA.options_to_catView()
	return t
end -- function p.cat_add_test(tst)

function p.i18n_lister(t, i18n_tables)
	if type(i18n_tables) ~= "table" then i18n_tables = CA.i18n end
	t = t or "\n* <b>i18n_lister</b> :"
	t = t .. CA.error_color("\n* <b>This list show all the texts, but cannot replace the original declarations.</b> ")
	t = t .. CA.error_color("\n* <b>" .. CA.str_vars("i18n_list_all_texts") .. " </b> " )
	for lang, lang_table in pairs(i18n_tables) do
		t = t .. "\np.i18n." .. lang .. ' = { '
		if type(lang_table) == "table" then
			for key, text in pairs(lang_table) do
				if key and text then
					t = t .. "\n:" .. key .. ' = "' .. text .. '",'
				end
			end
		end
		t = t .. "\n}\n"
	end
	return t
end -- function p.i18n_lister(t, i18n_tables)

-- mw.language:formatDate lang:formatDate( format, timestamp, local )
-- Formats a date according to the given format string. timestamp else current time. The value for local must be a boolean or nil; if true, the time is formatted in the wiki's local time rather else in UTC.
-- The format string and supported values for timestamp are identical to those for the #time parser function from Extension:ParserFunctions. Note that backslashes may need to be doubled in the Lua string where they wouldn't in wikitext:
-- {{#time:d F Y|1988-02-28|nl}} → 28 februari 1988
-- {{#time: U | now }} → 1424087375
-- {{#time: r|@1424087374}} → Mon, 16 Feb 2015 11:49:34 +0000

function CA.day_to_UTC(jj, mm, aaaa)
	local t = "@" .. tostring( jj*86400 + mm*30*86400 + (aaaa-1970)*31556926 )
	return t
end

function CA.day_to_stamp(jj, mm, aaaa)
	jj = tonumber(jj)
	if not jj then jj = "2" else jj = string.sub("0000" .. tostring(jj), -2, -1 ) end
	mm = tonumber(mm)
	if not mm then mm = "2" else mm = string.sub("0000" .. tostring(mm), -2, -1 ) end
	aaaa = tonumber(aaaa)
	if not aaaa then aaaa = "2000" else aaaa = string.sub("0000" .. tostring(aaaa), -4, -1 ) end
	return aaaa .. "-" .. mm .. "-" .. jj
--	mm = tostring( mm or "1" )
--	aaaa = tostring( aaaa or "1" )
--	if jj == 1 and CA.language_obj:getCode() == "fr" then t = string.gsub(t, "1 ", "1<sup>er</sup> " ) end
--	if jj == 1 and CA.language_obj:getCode() == "en" then t = string.gsub(t, "1 ", "1<sup>st</sup> " ) end
end

function CA.format_date(format, timestamp, lang, loc)
	if not CA.language_obj then CA.language_obj = mw.language.new( "fr" ) end
	if lang then CA.language_obj = mw.language.new( lang ) end
	if CA.language_obj then CA.language_code = CA.language_obj:getCode() end
	if type(loc) ~= "boolean" then loc = true end
	return tostring(CA.language_obj:formatDate(format, timestamp, loc))
end -- function CA.format_date(format, timestamp, loc)

function CA.time_format_test_1(t, test, format, timestamp, lang, loc)
	if lang then CA.language_obj = mw.language.new( lang ) end
	t = t .. CA.Tr() .. CA.Td(test or "-") .. CA.Td(format or "-") .. CA.Td(timestamp or "-") .. CA.Td(tostring(lang)) .. CA.Td( CA.format_date(format, timestamp, loc) )
	return t
end

function p.time_format_test(t)
	if not CA.language_obj then CA.language_obj = mw.language.new( "fr" ) end
	if CA.language_obj then CA.language_code = CA.language_obj:getCode() end
	t = t or "\n'''time_format_test''' :"
	t = t .. "\nCoding and conversion of dates by the parser function language_obj:formatDate(format, timestamp, loc)."
	t = t .. CA.ta("CA.language_code", CA.language_code)
	t = t .. "<br>Verify some date formats: Vérifier quelques formats de dates :"
	t = t .. CA.Th() .. CA.Tc("Test dates from seconds") .. CA.Tc("format") .. CA.Tc("timestamp") .. CA.Tc("lang") .. CA.Tc("Formated date")
	t = CA.time_format_test_1(t, " now", "U", "now")
	t = CA.time_format_test_1(t, " seconds to full UTC ", "r", "@1421117374")
	t = CA.time_format_test_1(t, " seconds to full UTC ", "r", "@1424087374")
	t = CA.time_format_test_1(t, " 2015-02-16T11:49:34+00:00 ", "c", "@1424107003")
	t = CA.time_format_test_1(t, " 14/7/1789 to english ", "F j Y", CA.day_to_UTC(14, 7, 1789), "en")
	t = CA.time_format_test_1(t, " seconds to french format ", "j F Y", "@1499997003")
	t = CA.time_format_test_1(t, " french 14/7/1789 ", "j F Y", CA.day_to_UTC(14, 7, 1789), "fr")
	t = CA.time_format_test_1(t, " french 14/7/234 ", "j F Y", CA.day_to_UTC(14, 7, 234), "fr")
	t = t .. CA.Tr() .. CA.Tc("Test from UTC") .. CA.Tc("format") .. CA.Tc("timestamp") .. CA.Tc("lang") .. CA.Tc("Formated date")
	t = CA.time_format_test_1(t, " french date ", "j F Y", "1915-02-16T17:16:43+00:00", "fr")
	t = CA.time_format_test_1(t, " french date ", "j F Y", "1515-02-22T17:16:43+00:00", "fr")
	t = t .. CA.Tr() .. CA.Tc("Test dates only") .. CA.Tc("format") .. CA.Tc("timestamp") .. CA.Tc("lang") .. CA.Tc("Formated date")
	t = CA.time_format_test_1(t, " french 2015-02-16 ", "j F Y", "2015-02-16", "fr")
	t = CA.time_format_test_1(t, " french 32-12-25 ", "j F Y", "32-12-25", "fr")
	t = CA.time_format_test_1(t, " french 0032-12-25 ", "j F Y", "0032-12-25", "fr")
	t = CA.time_format_test_1(t, " french 3/4/5 ", "j F Y", CA.day_to_stamp(3, 4, 5), "fr")
	t = CA.time_format_test_1(t, " french 24/11/31 ", "j F Y", CA.day_to_stamp(24, 11, 31), "fr")
	t = CA.time_format_test_1(t, " french 24/11/32 ", "j F Y", CA.day_to_stamp(24, 11, 32) )
	t = CA.time_format_test_1(t, " french 24/12/32 ", "j F Y", CA.day_to_stamp(24, 12, 32) )
	t = CA.time_format_test_1(t, " french 25/12/32 ", "j F Y", CA.day_to_stamp(25, 12, 32) )
	t = CA.time_format_test_1(t, " french 25/12/32 roman year ", "j F xrY", CA.day_to_stamp(25, 12, 32) )
	t = t .. CA.Tr() .. CA.Tc("Test partial formats & missing datas") .. CA.Tc("format") .. CA.Tc("timestamp") .. CA.Tc("lang") .. CA.Tc("Formated date")
	t = CA.time_format_test_1(t, " partial format 3/4/5 ", "F Y", CA.day_to_stamp(3, 4, 5) )
	t = CA.time_format_test_1(t, " partial format 3/4/5 ", "Y", CA.day_to_stamp(3, 4, 5) )
	t = CA.time_format_test_1(t, " missing day -/4/5 ", "j F Y", CA.day_to_stamp(nil, 4, 5) )
	t = CA.time_format_test_1(t, " missing month 3/-/5 ", "j F Y", CA.day_to_stamp(3, nil, 5) )
	t = CA.time_format_test_1(t, " missing year 3/4/- ", "j F Y", CA.day_to_stamp(3, 4, nil) )
	t = CA.time_format_test_1(t, " missing day -/4/5 ", "F Y", CA.day_to_stamp(nil, 4, 5) )
	t = CA.time_format_test_1(t, " missing month 3/-/5 ", "j Y", CA.day_to_stamp(3, nil, 5) )
	t = CA.time_format_test_1(t, " missing year 3/4/- ", "j F", CA.day_to_stamp(3, 4, nil) )
	t = t .. CA.Te()
	return t
end -- function p.time_format_test(t)

function p.date_to_part_test_1(t, nom, date, part)
	t = t .. CA.Tr() .. CA.Td(nom) .. CA.Td(date) .. CA.Td(part) .. CA.Td(tostring(CA.date_to_part(date, part)))
	return t
end

function p.date_to_part_test(t)
	t = t or "\n* '''date_to_part''' :"
	t = t .. "\n* Verify each value of part of date: Vérifier chaque valeur de partie de date :"
	t = t .. CA.Th() .. CA.Tc("Exemple") .. CA.Tc("date") .. CA.Tc("partie") .. CA.Tc("valeur")
	t = CA.date_to_part_test_1(t, "Socrate nais.", "470 BCE", "era")
	t = CA.date_to_part_test_1(t, "Tite-Live nais.", "59 BCE", "yyyy")
	t = CA.date_to_part_test_1(t, "Tite-Live mort.", "17", "yyyy")
	t = CA.date_to_part_test_1(t, "vide", "", "yyyy")
	t = CA.date_to_part_test_1(t, "Révolution", "14 juillet 1789", "mmmm")
	t = CA.date_to_part_test_1(t, "Marche sur la Lune", "20 juillet 1969", " ")
	t = CA.date_to_part_test_1(t, "English date", "July 20 1969", "yyyy")
	t = CA.date_to_part_test_1(t, "English date", "July 20 1969", "mmmm")
	t = CA.date_to_part_test_1(t, "Nelson Mandela nais.", "juillet 1918", "yyyy")
	t = CA.date_to_part_test_1(t, "Nelson Mandela décès", "5 décembre 2013", "dd")
	t = t .. CA.Te()
	return t
end -- function p.date_to_part_test(t)

function CA.table_count(tab, tab_name)
	local tab_name, tbl, t
	if type(tab) == "string" then
		tab_name = tab
		tbl = p[tab_name]
	elseif type(tab) == "table" then
		tab_name = tab_name or "table?"
		tbl = tab
	end
	if type(tbl) == "table" then
		local st, vr, fn, tb = p.testable_lister(tbl, "p."..tab_name)
	--	local t = CA.str_vars("Table '''%1''' : comptes tabs='''%2''', vars='''%3''', funcs='''%4''' ", tab_name, tb, vr, fn)
		t = CA.str_vars("table_counts", tab_name, tb, vr, fn)
		t = CA.str_vars("Table '''%1''' : counts tabs='''%2''', vars='''%3''', funcs='''%4'''.", tab_name, tb, vr, fn) -- DEBUG
	else
		t = CA.str_vars("table_dont_exists", tab_name or tbl)
	end
	return t or "", vr, fn, tb
end -- function CA.table_count(tab, tab_name)

function CA.table_n_vars(tab, tab_name)
	local st, vr, fn, tb = CA.table_count(tab, tab_name)
	return vr
end

function CA.tables_counts(t, ...)
	local t = t
	if not ( type(t) == "string" ) then t = nil end
	t = t or "- Counts of contents of tables: "
	t = t .. "<br>" .. CA.table_count("i18n")
	t = t .. "<br>" .. CA.table_count("wiki_translations")
	t = t .. "<br>" .. CA.table_count("user_translations")
	t = t .. "<br>" .. CA.table_count("args_known")
	t = t .. "<br>" .. CA.table_count("args_wikidata")
	t = t .. "<br>" .. CA.table_count("args_invoke")
	t = t .. "<br>" .. CA.table_count("args_template")
	t = t .. "<br>" .. CA.table_count("args_source")
	t = t .. "<br>" .. CA.table_count("args_import")
	t = t .. "<br>" .. CA.table_count("args_final")
	t = t .. "<br>" .. CA.table_count("args_unknown")
	t = t .. "<br>" .. CA.table_count("args_selected")
	t = t .. "<br>" .. CA.table_count("errors_list")
	t = t .. "<br>" .. CA.table_count("categories_list")
	p.requires = CA.versions.requires
	t = t .. "<br>" .. CA.table_count("requires")
	p.inits = CA.versions.inits
	t = t .. "<br>" .. CA.table_count("inits")
	local func = "" -- ipairs
	for n, tab_name in ipairs( {...} ) do
		if type(tab_name) == "string" then
			t = t .. "<br>" .. CA.table_count(tab_name) -- CA.table_count(tab, tab_name)
		end
	end
	CA.i18n_trac("tables_counts", "end", 1)
	return t
end -- function CA.tables_counts(t)

function p.debug_traceback(t)
	if type(t) ~= "string" then t = "\n* '''debug.traceback()''' : " end
	t = t .. debug.traceback()
	t = string.gsub(t, "Module", "\n* Module" )
	return t
end

function p.form_tests_init(res, args_source)
-- Special init for the test mode
	if type(res) ~= "string" then res = "\n* Mode test : " end
	if type(args_source) ~= "table" then args_source = {} end
	if p.i18n and p.i18n.en then p.i18n.en.error_i18n_wanted_to_test_missing_translation = 'English error i18n wanted for tests missing translation' end
	if p.i18n and p.i18n.es then p.i18n.es.error_i18n_deseada_para_probar_traduccion_faltan = 'Espagnol error i18n deseada para probar traducción faltan' end
	if p.i18n and p.i18n.fr then p.i18n.fr.error_i18n_voulue_pour_test_de_traduction_manquante = 'Français erreur i18n voulue pour tests de traduction manquante' end
--	if not args_source.userlang then args_source.userlang = "en" end
--	if not args_source.wikilang then args_source.wikilang = "es" end
	if not args_source.name then args_source.name = "Jack Smith" end
	if not args_source.nom then args_source.nom = "Victor Hugo" end
	if not args_source.region then args_source.region = "india" end
	if not args_source["région"] then args_source["région"] = "chine" end
	if not args_source.description then args_source.description = "Victor Hugo est très connu." end
	if not args_source.langue then args_source.langue = "français,japonais" end
	if not args_source.occupation then args_source.occupation = "Académiciens,Personnalités politiques" end
	p.args_source = args_source
	return res
end -- function p.form_tests_init(res, args_source)

--	Documentations et tests détaillés supplémentaires
function p.form_tests(args_final)
	if type(args_final) ~= "table" then args_final = CA.args_final end -- optional arguments
	local res = "" -- "\n:.\n"
	local content = ""
	-- CA.options = " : docdata docmin docdef docmax docline docsrc docview docafter docnotice docsrc" -- for documentation
	-- CA.options = " erron noerr nobox " -- without normal result
	-- CA.options = " debug tests en es fr " -- for debug or enforce language
--	CA.form_tests_init(res, CA.args_source)
	--
	---------------------------------------------------------------
	-- "\n* any texte" is forbiden before dropbox. Is it a bug ? --
	---------------------------------------------------------------
	--
--	CA.init_wiki_user_lang(CA.wiki_lang, CA.user_lang)
--[[	res = res .. "<h3>" .. CA.str_vars("page_tests_h3_title") .. "</h3>"
	res = res .. "<br> " .. CA.str_vars("page_tests_title")
	res = res .. "<br> " .. CA.user_wiki_lang
	res = res .. "<small><br> " .. p.all_versions_text() .. "</small>"
	res = res .. CA.track_i18n_t("form_tests page")
	--]]
	--
	res = res .. "<h3>" .. CA.str_vars("page_tests_h3_title") .. "</h3>"
	res = res .. "<small><small>" .. CA.all_versions_text() .. "</small></small>"
	res = res .. "<br/> " .. CA.str_vars("page_tests_title") .. "<br/>"
	res = res .. "<br> " .. CA.user_wiki_lang
	res = res .. CA.dropdown_func(1, CA.versions.warning_short() .. CA.str_vars("actual_versions_title"), CA.versions.warning_report )
	res = res .. CA.dropdown_func(1, "used_options_title", CA.used_options_list, CA.used_options)
	res = res .. CA.dropdown_func(1, "tables_count_title", CA.tables_counts)
	res = res .. CA.dropdown_func(1, "list_all_args_title", CA.list_all_args)
	res = res .. CA.dropdown_func(1, "wikidata_details_test_title", CA.wikidata_details_test)
	res = res .. CA.dropdown_func(1, "wikidata_any_page_title", CA.wikidata_any_page_test, "Q535", "Q535")
	res = res .. CA.dropdown_func(1, "spaces_page_names_title", CA.spaces_page_names_test)
	res = res .. CA.dropdown_func(1, "table_args_source_title", CA.testable_lister, CA.args_source, "CA.args_source")
	res = res .. CA.dropdown_func(1, "table_args_unknown_title", CA.testable_lister, CA.args_unknown, "CA.args_unknown")
	res = res .. CA.dropdown_func(1, "table_args_known_title", CA.testable_lister, CA.args_known, "CA.args_known")
	res = res .. CA.dropdown_func(1, "wikidata_arbitrary_access_title", CA.arbitrary_access_test)
	--
	res = res .. "<h3>*" .. CA.str_vars("internal_tests_h3_title") .. "</h3>"
	res = res .. "<br>* " .. CA.str_vars("internal_tests_title") .. "<br>"
	--
	res = res .. CA.dropdown_func(1, "options_from_mode_title", p.options_from_mode_test,
	{ boxstyle = "boxstyle", width = "80%", text_color="green", alignT="left", alignB="center", margin_all="3em", background_color="yellow", border_color="red", height="2em"} )
	res = res .. CA.dropdown_func(1, "options_from_args_title", p.options_from_args_test)
	res = res .. CA.dropdown_func(1, "versions_details_title", CA.versions.warning_versions_details )
	res = res .. CA.dropdown_func(1, "versions_manage_test_title", CA.versions.check_versions_test)
	res = res .. CA.dropdown_func(1, "date_to_part_test_title", CA.date_to_part_test)
	res = res .. CA.dropdown_func(1, "similar_args_test_title", CA.similar_args_test)
	res = res .. CA.dropdown_func(1, "levenshtein_test_title", CA.levenshtein_test)
	res = res .. CA.dropdown_func(1, "testable_lister_title", CA.testable_lister, CA.tablim, "CA.tablim", {} )
	res = res .. CA.dropdown_func(1, "testable_limit_title", CA.testable_lister, CA.tablim, "CA.tablim",
	{ boxstyle = "boxstyle", levelmaxi = 2, max_n = 2, exclude1 = "hou" }, {width = "88%", text_color = "blue"} )
	res = res .. CA.dropdown_func(1, "missing_translations_title", CA.verif_i18n, CA.i18n)
	res = res .. CA.dropdown_func(1, "combined_translations_title", CA.i18n_lister)
	res = res .. CA.dropdown_func(1, "dummy_languages_title", CA.dummy_languages)
	res = res .. CA.dropdown_func(1, "time_format_test_title", CA.time_format_test)
	res = res .. CA.dropdown_func(1, "cat_add_test_title", CA.cat_add_test)
	res = res .. CA.dropdown_func(1, "multiple_values_tests_title", CA.multiple_values_tests)
	res = res .. CA.dropdown_func(1, "multiple_selection_test_title", CA.multiple_selection_test)
	res = res .. CA.dropdown_func(1, "Time test table from wikidata time properties", CA.testable_lister, CA.TimeTest, "CA.TimeTest.claims." .. (CA.TimeName or "Pxxx") )
	return res
end -- function p.form_tests(args_final)

function CA.tests_time( if_view, res, time1, time2, time3, time4)
	-- Display example :
	-- Execution and test time : 2014-02-22 18:09:15 UTC , url = http://fr.wikisource.org/wiki/Module:ControlArgs/Documentation ,
	-- start = 84 mS , import + 8 mS , generate page + 0 mS , tests + 127 mS , total = 221 mS ControlArgs:tests:fr
	CA.time4 = os.clock()
	if not if_view then return "" end
	time1 = time1 or p.time1 or os.clock()
	time2 = time2 or p.time2 or time1
	time3 = time3 or p.time3 or time1
	time4 = time4 or p.time4 or time1
	if time2 < time1 then time2 = time1 end
	if time3 < time2 then time3 = time2 end
	if time4 < time3 then time4 = time3 end
	local nowtime = os.date("%Y-%m-%d %H:%M:%S")
	local mwtitle = mw.title.getCurrentTitle()
	local url = tostring(mwtitle:canonicalUrl( ))
	local time2d = time2 - time1
	local time3d = time3 - time2
	local time4d = time4 - time3
	local duration = time2d + time3d + time4d
	time1  = tostring(math.floor( time1	 * 1000 )) .. " mS"
	time2d = tostring(math.floor( time2d * 1000 )) .. " mS"
	time3d = tostring(math.floor( time3d * 1000 )) .. " mS"
	time4d = tostring(math.floor( time4d * 1000 )) .. " mS"
	duration = tostring(math.floor( duration * 1000 )) .. " mS"
	res = res or ""
	res = res .."\n* Execution and test time : " .. nowtime .. " UTC" .. p.ta("url", url) -- prefixedText
	res = res .. p.ta("<br>start in page", time1) .. p.ta("import", time2d, "+")
	res = res .. p.ta("form result", time3d, "+") .. p.ta("tests", time4d, "+") .. p.ta("duration", duration) .. "<br>"
	-- DEBUG wait for : T68051 Get the user language to help admins to maintain a module or a template (userInfo: String user and password)
	local mwuri = mw.uri.canonicalUrl(mwtitle.prefixedText, { host, authority, user, password, } )
--	res = res .. CA.ta("mwtitle.prefixedText", mwtitle.prefixedText) .. CA.ta("mwuri", mwuri)
	res = res .. CA.ta("host", tostring(mwuri.host)) .. CA.ta("authority", tostring(mwuri.authority))
	res = res .. CA.ta("user", tostring(mwuri.user)) .. CA.ta("user_language", tostring(mwuri.language))
	--	spaces_page_names_test : , mwtitle = Auteur:Nelson Mandela , nsText = Auteur , baseText = Nelson Mandela , url =
	-- subpageText: If this is a subpage, just the subpage name. Otherwise, the same as title.text.
	res = res .. CA.ta("text", tostring(mwtitle.text)) .. CA.ta("baseText", tostring(mwtitle.baseText))
	res = res .. CA.ta("rootText", tostring(mwtitle.rootText)) .. CA.ta("subpageText", tostring(mwtitle.subpageText))
	res = res .. "<br/>In this wiki: "
	res = res .. CA.ta("modules ns(828)", tostring(mw.site.stats.pagesInNamespace( 828 ) ) )
	res = res .. CA.ta("42 patrollers(patroller)", tostring(mw.site.stats.usersInGroup( "patroller" ) ) )
	res = res .. CA.ta("18 administrators (Rical ?) (sysop)", tostring(mw.site.stats.usersInGroup( "sysop" ) ) )
	res = res .. CA.ta("12 bots (bot)", tostring(mw.site.stats.usersInGroup( "bot" ) ) )
	res = res .. CA.ta("checkuser", tostring(mw.site.stats.usersInGroup( "checkuser" ) ) )
	res = res .. CA.ta("bureaucrats (bureaucrat)", tostring(mw.site.stats.usersInGroup( "autoconfirmed" ) ) )
	res = res .. CA.ta("0 (accountcreator)", tostring(mw.site.stats.usersInGroup( "accountcreator" ) ) )
	return res
end -- function p.tests_time( if_view, res, time1, time2, time3, time4)

------------------------------------------------------------
-- Running internal tests and their documentations.
-- Ejecución de las pruebas internas y su documentación.
-- Exécution des tests internes et de leurs documentations.
--
-- Errors are needed to test their detection. Do not correct them.
-- Errores se necesitan para probar su detección. No corregirlos se.
-- Les erreurs sont nécessaires pour tester leur détection. Ne pas les corriger.
------------------------------------------------------------

local args_test_errors = { "aaa", "bbb", "ccc", "ddd", ["prénom"] = "Arthur",
	options = ' docdef docview docsrc ', lastXXname = 'Voltaire', birthyear = '1999', birthyear = '2000',
} -- Arguments pour auto-test

------------------------------------------------------------
-- Arguments sources examples
--
-- Errors are needed to test their detection. Do not correct them.
-- Errores se necesitan para probar su detección. No corregirlos se.
-- Les erreurs sont nécessaires pour tester leur détection. Ne pas les corriger.
------------------------------------------------------------

p.WikidataEN = { label = "John Smith", deathyear = "1789",	country = "France" }

p.ArgtestEN = { "mode One", "Rimbaud 2", name = "Rimbaud", firstname = "Arnaud", rights = "70", deathyear = "MDCCCJL",	langue = "allemand,français,espagnol" }

p.ArgtestES = { nombre = "Rimbaud", apellido = "Arthur", optionsES = " ", derechoss = "70", anoMuerte = "MDCCCJL"}

p.ArgtestFR = { "mode Un", "Rimbaud 2", "Jonh", nom = "Smith", ["prénom"] = "Arnaud", anneeDeces = "1234", }

function p.trc(fn_mode, t)
	local res = ""
	-- to put in comment to desactivate
--	res = res .. "<br>* " .. t .. CA.ta("fn_mode", fn_mode) .. CA.ta("CA.mode_name", CA.mode_name) .. CA.ta("CA.mode_options", CA.mode_options) .. CA.ta("CA.invoke_options", CA.invoke_options)
	res = res .. CA.ta("trc CA", CA.str_vars("internal_tests_title")) .. CA.ta("MR", CA.str_vars("err_J_before_end")) .. CA.ta("AT", CA.str_vars("deathyear"))
	return res
end

function CA.options_from_mode(mode_name)
	-- mode_name = mode_name or (CA.args_final and CA.args_final.mode) or (CA.args_import and CA.args_import.mode) or p.mode_name or CA.mode_name or "normal"
	mode_name = mode_name or "normal"
	local mode_options = ""
	if CA.options_for_modes and CA.options_for_modes[mode_name] then mode_options = CA.options_for_modes[mode_name] end
--	CA.mode_options = mode_options
	return mode_options, mode_name
end

function p.options_from_mode_test(t)
	local t = "options_from_mode_test:" or t
	for md, opt in pairs(CA.options_for_modes) do t = t .. "<br>- " .. CA.ta(md, opt) end
	t = t .. CA.Th() .. CA.Tc("Mode") .. CA.Tc("List of options") .. CA.Tc("noerr value") .. CA.Tc("docview value") .. CA.Tc("tests value")
	local function test_options_from_mode(md, op1, op2, op3)
		local opstest = p.options_from_mode(md) or ""
		return CA.Tr() .. CA.Td(md) .. CA.Td(opstest) .. CA.Td(CA.option(op1, opstest)) .. CA.Td(CA.option(op2, opstest)) .. CA.Td(CA.option(op3, opstest))
	end
	t = t .. "<br>options_from_mode_test:"
	t = t .. test_options_from_mode("normal", "noerr", "docview", "tests")
	t = t .. test_options_from_mode("edit", "noerr", "docview", "tests")
	t = t .. test_options_from_mode("tests", "noerr", "docview", "tests")
	t = t .. CA.Te()
	return t
end

function CA.get_arg_mode(mode_name, source_key, try_lang, args_source)
	local mode_key = "mode"
	args_source = args_source or CA.args_source
	try_lang = try_lang or tostring(mw.language.getContentLanguage().code) or "en"
	local i18n_lang = CA.i18n[try_lang] or CA.i18n[tostring(mw.language.getContentLanguage().code)] or CA.i18n["en"]
	source_key = source_key or i18n_lang[mode_key] or mode_key -- translation of the mode_key "mode" in wiki language
	mode_name = mode_name or args_source[source_key] or "normal"
	CA.mode_name = mode_name or CA.mode_name or "normal"
	return mode_name, source_key, try_lang
end

-- Display the documentation in an infobox, similar to edit-boxs
-- Affichage de documentation dans un cadre (box), semblable aux boites d'edition
function p.formDocBox(args_final)
	-- p.options = " : docdata docmin docdef docmax docline docsrc docview docafter docnotice docsrc" -- for documentation
	-- p.options = " erron noerr nobox " -- without normal result
	-- p.options = " debug tests en es fr " -- for debug or enforce language
	if type(args_final) ~= "table" then args_final = CA.args_final end -- optional arguments
	local err = CA.verify_args_tables(CA.args_known, CA.args_source)
	if err then return err end
	res = ""
	res = res .. CA.error_color("<center><b>" .. CA.str_vars("err_delete_docbox") .. "</b><br/></center>")
	res = res .. CA.dropdown_func(1, CA.versions.warning_short() .. CA.str_vars("actual_versions_title"), CA.versions.warning_report )
--	res = res .. CA.dropdown_func(1, "Module Author: warning_versions_details", CA.versions.warning_versions_details )
--	p.docbox_namespace_error_and_cat()
--	res = res .. "<br/> " .. CA.user_wiki_lang
--	res = res .. "<small><br/>" .. CA.all_versions_text("formDocBox", versions.all_versions) .. "</small>"
	if CA.docolor then res = res .. "\n:.\n: " .. CA.sources_of_datas_colors() end
	if CA.option("debug")		then res = res .. "\n*" .. CA.ta("CA.catView", CA.catView) .. CA.ta("p.invoke_options", p.invoke_options) .. CA.ta("p.mode_options", p.mode_options) end
	if CA.option("docdata")		then res = res .. "\n*" .. CA.generDoc(" docdef docline ", CA.args_wikidata, "Wikidata") end
	if CA.option("docview")		then res = res .. "\n*" .. CA.generDoc("", args_final, "Arguments") end
	if not CA.option("noerr")	then res = res .. "\n*" .. CA.errors_lister() end
	res = res .. "\n* " .. CA.categories_lister(":")
	res = '<div style=" width=90%; border: 1px solid #AAAAAA; margin:1em; background-color:#F1F1F1; padding:0.3em; ">' .. res .. '</div>'
	return res
end -- function p.formDocBox(args_final)

-- Normal result of the module
-- Resultado normal del módulo
-- Résultat normal du module
function p.form_result(args_final)
	if type(args_final) ~= "table" then args_final = CA.args_final end -- optional arguments
	local res = ""
--	res = res .. CA.dropdown_func(1, "cat_add_test_title", CA.cat_add_test)
	p.gener_categories(args_final) -- Produire les catégories sans les activer
	if CA.option("docview") then res = res .. p.formDocBox(args_final) end
	if not CA.option("nobox") then res = res .. p.normal_box(args_final) end
	-- Display categories. Afficher les catégories.
	if ( not CA.option("nobox") ) and ( CA.option("catview") or CA.option(":") ) then res = res .. CA.categories_lister(":") end
	CA.time3 = os.clock()
	if CA.option("tests") or CA.args_final.mode == "tests" then res = res .. p.form_tests() end
	CA.time4 = os.clock()
	if CA.option("tests") or CA.args_final.mode == "tests" then res = res .. CA.tests_time(true, "") end
	return res
end -- function p.form_result(args_final)

------------------------------------------------------------
------------------------------------------------------------
-- Interfaces, alias and functions to templates
-- Interfaces, alias y funciones para modelos
-- Interfaces, allias et fonctions pour les modèles
------------------------------------------------------------
------------------------------------------------------------

CA.options_for_modes = { -- default init
	normal	= " noerr ",
	edit	= " : catview docview docdef docline docsrc docdata ",
	doc1	= " nobox noerr nocat ",
	tests	= " : catview docview docdef docline docsrc docdata tests ",
	-- Option nocat means "Do not categorize and do not show categories."
}

-- Basic use, only translate and verify arguments, without use of options, modes and resulting text
-- Uso básico, sólo traducir y verificar argumentos, sin el uso de opciones, modos y texto resultante
-- Utilisation de base, seulement traduire et vérifier les arguments, sans utilisation d'options, modes et texte résultant
function p.base(frame, mode_name, args_known_default, doc1_id)
	-- The base function only imports, translates and verifies arguments.
	-- It imports arguments from wikidata, module and template with increasing priorities.
	local res = ""
	CA.time1 = os.clock()
	CA.frame = frame
	args_known_default = args_known_default or CA.args_known_default
	CA.add_i18n_track_txt = " "
	local t, tbl = CA.get_arg_mode(mode_name, source_key, try_lang, args_source)
	--
--	CA.trac_lang("base1", CA.args_source) --
	local t, tbl = CA.versions.init_i18n()
	CA.add_i18n_track_txt = CA.add_i18n_track_txt .. (t or "")
--	res = res .. CA.add_i18n_track_txt
	CA.module_init(frame)
	local args_source = CA.args_source
--	CA.categories_init() -- initialize the category list
	CA.verif_i18n()
	p.errors_list = {} -- Table to collect errors and messages
	local source_mode_name = CA.get_arg_mode()
	--
	CA.args_source.id = "Q21157618" -- Charles Maumené
--	args_source.id = "Q131671" -- Xénophane
	--
	CA.init_args(args_known_default, CA.args_source, lang, msgs_list)
	local doc1_doc = CA.args_source.dockey or CA.args_source[1]
	local doc1_id = CA.args_source.entityid or CA.args_source.id or CA.args_source[2]
	CA.args_wikidata, t = CA.import_wikidata(CA.args_known, doc1_id)
--	res = res .. p.trc(mode_name, "after import_wikidata")
	CA.args_import, t = CA.import_arguments(args_known_default, CA.args_source )
--	CA.trac_lang("CA.import_arguments:", CA.args_import) -- DEBUG
	--
	CA.time2 = os.clock()
	if type(CA.args_import.c) == "string" then
		CA.invoke_options = CA.args_import.c
	end
	if type(CA.args_import.mode) == "string" then
		mode_name = CA.args_import.mode
	end
--	res = res .. p.trc(mode_name, "after args_import")
	if CA.args_import then
		-- en : initialize or change the user and the wiki languages and their tables
		CA.init_wiki_user_lang(CA.args_import.wikilang, CA.args_import.userlang) -- CA.wiki_lang)
	end
--	CA.trac_lang("base CA.init_user_lang", CA.args_import) --
	CA.invoke_options = CA.args_import.c or ""
	CA.mode_options = p.options_from_mode(source_mode_name or mode_name)
--	res = res .. p.trc(mode_name, "base options_from_mode")
--	CA.trac_lang("base CA.options_from_mode end", CA.args_import) --
--	res = res .. CA.track_i18n_vers
--	res = res .. "<br>" .. CA.versions.warning_versions_details()
	return res
end -- function p.base(frame, mode_name, args_known_default)

function p.normal(frame)
	-- The normal function imports, translates and verifies arguments.
	-- It generates normal text and uses options to include edit or tests
	local res, t = "", ""
	res = res .. p.base(frame, "normal", p.args_known_default)
	CA.args_final = CA.interact_args_final(CA.args_import) -- Interactions between argumensts
--	CA.init_user_lang() -- ("en", "es")
	CA.time3 = os.clock()
--	res = res .. CA.trac_lang_t
	res = res .. p.form_result()
--	CA.trac_lang("normal args_final", CA.args_final) --
	res = res .. CA.trac_lang_t
	if CA.args_final and (CA.args_final.selectversions or CA.args_final.allversions) then -- version select from arguments
		res = res .. CA.versions.warning_report("Track if args_final selectversions or allversions. ")
	end
	res = res .. CA.trac_lang_t
	t = t .. "<br>" .. CA.table_count("args_source")
	return res
end -- function p.normal(frame)

function p.doc(frame) return p.edit(frame) end -- alias function
function p.edit(frame)
	-- The edit function imports, translates and verifies arguments.
	-- It generates edit panel and normal text.
	local res, t = "", ""
	res = res .. p.base(frame, "edit", p.args_known_default)
	CA.args_final = CA.interact_args_final(CA.args_import) -- Interactions between argumensts
--	CA.trac_lang("edit args_final", CA.args_final) --
--	CA.init_user_lang() -- ("en", "es")
--	CA.trac_lang("edit init_user_lang", CA.args_final) --
	res = res .. p.formDocBox()
	CA.time3 = os.clock()
	res = res .. p.form_result(CA.args_final)
--	CA.trac_lang("form_result args_final", CA.args_final) --
	res = res .. CA.trac_lang_t
	res = res .. CA.tests_time(true, "")
	return res
end -- function p.edit(frame)

function CA.wikidata_any_page_test(doc1_doc, doc1_id) -- Import datas for any page
	local wd, tw, wd_mng = p.import_wikidata(CA.args_known, doc1_id) -- p.args_known, id) -- example Q535 = Victor Hugo = Victor Marie Hugo
	local args_import, tx = CA.import_arguments() -- p.args_known_default, CA.args_source )
	if wd_mng and wd_mng.wd_entity and wd_mng.wd_entity.id then
		t = "\n* " .. CA.str_vars("wikidata_arbitrary_access_text")
		.. (wd_mng.wd_id or "") .. ", " .. (wd_mng.label or "") .. " ( " .. (wd.birthyear or "") .. " - " .. (wd.deathyear or "") .. " ) "
		local structured_data_txt = CA.str_vars("structured_data_txt")
		t = t .. ' <span style="color:#232388; font-size:140%; line-height:120%; ">⦁&nbsp;&nbsp;</span>[[d:' .. wd_mng.wd_entity.id .. '|' .. structured_data_txt .. ']]'
		t = t .. ", " .. (wd_mng.props_maxn or "") .. " properties."
		t = t .. "\n* Asked properties: " .. tw
	--	t = t .. "\n* Properties number maxn: " .. (wd_mng.props_maxn or "")
	else
		t = "<br>* No arbitrary access for: " .. tostring(id)
	end
	return t
end -- function CA.wikidata_any_page_test(doc1_doc, doc1_id)

function p.doc1(frame)
	-- Form as documentation, a test in a dropbox.
	local res, t = "", ""
	res = res .. p.base(frame, "doc1", p.args_known_default)
--	CA.args_selected, p.selected_autorities, p.authorities_from_args = p.authorities_select()
	CA.args_final = p.interact_args_final(CA.args_import) -- Interactions between argumensts
	local doc1_doc = CA.args_source.dockey or CA.args_source[1]
	local doc1_id = CA.args_source.entityid or CA.args_source.id or CA.args_source[2]
	--[[
	local function is_id(x) -- Verify the wikidata item format "Q12345".
		if type(x) == "string" then
			local Q = string.upper( string.sub(x, 1, 1) )
			local N = tonumber(string.sub(x, 2, -1) )
		end
		return x and (Q == "Q") and (type(N) == "number")
	end -- wikidata_details_test_title
	local templat = frame:getParent().args -- arguments from template
	local invoked = frame.args -- arguments from #invoked module
	if not is_id( doc1_id ) then
		res = res .. "<br>** missing_entityid ** " .. t .. CA.ta("doc1_doc" ,doc1_doc) .. CA.ta("doc1_id" ,doc1_id) 
		.. CA.ta("Template 1", (templat[1] or "-") .. ", 2=" .. (templat[2] or "-") .. ", 3=" .. (templat[3] or "-") ) .. ") , ("
		.. CA.ta("#invoke  1", (invoked[1] or "-") .. ", 2=" .. (invoked[2] or "-") .. ", 3=" .. (invoked[3] or "-") ) .. ") "
	--	doc1_doc = "tables_count_title"
	end
	--]]
	CA.time3 = os.clock()
	local doc1_list = {
		actual_versions_title			= CA.dropdown_func(1, CA.versions.warning_short() .. CA.str_vars("actual_versions_title"), CA.versions.warning_report ),
		versions_details_title			= CA.dropdown_func(1, "versions_details_title", CA.versions.warning_versions_details ),
		used_options_title				= CA.dropdown_func(1, "used_options_title", CA.used_options_list),
		tables_count_title				= CA.dropdown_func(1, "tables_count_title", CA.tables_counts),
		list_all_args_title				= CA.dropdown_func(1, "list_all_args_title", CA.list_all_args),
		wikidata_details_test_title		= CA.dropdown_func(1, "wikidata_details_test_title", CA.wikidata_details_test),
		wikidata_any_page_title			= CA.dropdown_func(1, "wikidata_any_page_title", CA.wikidata_any_page_test, doc1_doc, doc1_id), -- lng
		spaces_page_names_title			= CA.dropdown_func(1, "spaces_page_names_title", CA.spaces_page_names_test),
		table_args_source_title			= CA.dropdown_func(1, "table_args_source_title", CA.testable_lister, CA.args_source, "CA.args_source"),
		table_args_unknown_title		= CA.dropdown_func(1, "table_args_unknown_title", CA.testable_lister, CA.args_unknown, "CA.args_unknown"),
		table_args_known_title			= CA.dropdown_func(1, "table_args_known_title", CA.testable_lister, CA.args_known, "CA.args_known"),
		options_from_mode_title			= CA.dropdown_func(1, "options_from_mode_title", p.options_from_mode_test,
		{ boxstyle = "boxstyle", width = "77%", text_color="green", alignT="center", alignB="left", margin_all="3em", background_color="yellow", border_color="red", height="2em"} ),
		options_from_args_title			= CA.dropdown_func(1, "options_from_args_title", p.options_from_args_test),
		versions_manage_test_title		= CA.dropdown_func(1, "versions_manage_test_title", CA.versions.check_versions_test),
		date_to_part_test_title			= CA.dropdown_func(1, "date_to_part_test_title", CA.date_to_part_test),
		similar_args_test_title			= CA.dropdown_func(1, "similar_args_test_title", CA.similar_args_test),
		levenshtein_test_title			= CA.dropdown_func(1, "levenshtein_test_title", CA.levenshtein_test,
		{ boxstyle = "boxstyle", width = "88%", text_color = "#4488CC", margin_bottom="55px"} ),
		testable_lister_title			= CA.dropdown_func(1, "testable_lister_title", CA.testable_lister, CA.tablim, "CA.tablim", {} ),
		testable_limit_title			= CA.dropdown_func(1, "testable_limit_title", CA.testable_lister, CA.tablim, "CA.tablim",
		{ boxstyle = "boxstyle", levelmaxi = 2, max_n = 2, exclude1 = "hou" } ),
		missing_translations_title		= CA.dropdown_func(1, "missing_translations_title", CA.verif_i18n, CA.i18n),
		combined_translations_title		= CA.dropdown_func(1, "combined_translations_title", CA.i18n_lister),
		dummy_languages_title			= CA.dropdown_func(1, "dummy_languages_title", CA.dummy_languages),
		time_format_test_title			= CA.dropdown_func(1, "time_format_test_title", CA.time_format_test),
		cat_add_test_title				= CA.dropdown_func(1, "cat_add_test_title", CA.cat_add_test),
		wikidata_arbitrary_access_title = CA.dropdown_func(1, "wikidata_arbitrary_access_title", CA.arbitrary_access_test),
	}
	local doc1_txt = doc1_list[doc1_doc]
	res = res .. (doc1_txt or "doc1_txt missing ")
	return res
end -- function p.doc1(frame)

function p.tests(frame)
	local res, t = "", ""
	res = res .. p.base(frame, "tests", p.args_known_default)
	CA.args_final = CA.interact_args_final(CA.args_final) -- Interactions between argumensts
	p.form_tests_init()
	CA.args_final.mode = "tests"
	CA.mode_name = "tests"
	CA.mode_options = " : docdata docdef docview docline docsrc tests"
	local res_function = "<br>* " .. CA.versions.version .. ":" .. CA.mode_name .. ":" .. CA.wiki_lang .. " "
	local res_function = "<br/>\n" .. (CA.versions.main_versionName or "MainModule_t") .. " " .. (CA.versions.main_versionNumber or "0.0_t") .. ":" .. CA.mode_name .. ":" .. CA.wiki_lang .. " "
--	CA.init_user_lang("en", "es")
--	CA.trac_lang("init_user_lang en/es", CA.args_final) --
	res = res .. res_function .. " Begin:" .. "<br>"
	local loaded_modules, loaded_tab, modu = CA.versions.loaded_modules()
	CA.loaded_modules_track = "p.tests : " .. loaded_modules
	res = res .. "<br>" .. CA.loaded_modules_track -- DEBUG
--	versions.warning = versions.warning or " -warning- "
--	versions.tree = versions.tree or " -tree- "
--	res = res .. "<b><b>Résumé</b></b><small><small>" .. versions.warning .. "<br>p.tests" .. CA.all_versions_text() .. "</small></small><br>" .. versions.tree
	CA.time3 = os.clock()
	res = res .. p.form_result() -- Generate normal wikitext, categories, and others
--	CA.trac_lang("form_result", CA.args_final) --
	res = res .. res_function .. " End." .. "<br>"
	res = res .. CA.trac_lang_t
--[ [
--	if 1 < 2 then return res end
	res = res .. "<br>* <b>funct = doc1, [1] = missing_translations_title</b>"
						.. "<br><nowiki>{{#invoke:ControlArgs|doc1|missing_translations_title}}</nowiki> "
	res = res .. frame:preprocess(	   "{{#invoke:ControlArgs|doc1|missing_translations_title}}")
	--
	res = res .. "<br>* <b>funct = doc1, [1] = options_from_mode_title</b>"
						.. "<br><nowiki>{{#invoke:ControlArgs|doc1|options_from_mode_title}}</nowiki> "
	res = res .. frame:preprocess(	   "{{#invoke:ControlArgs|doc1|options_from_mode_title}}")
	--
	res = res .. "<br>* <b>funct = doc1, [1] = used_options_title</b>"
						.. "<br><nowiki>{{#invoke:ControlArgs|doc1|used_options_title}}</nowiki> "
	res = res .. frame:preprocess(	   "{{#invoke:ControlArgs|doc1|used_options_title}}")
	--
	res = res .. "<br>* <b>funct = doc1, [1] = wikidata_any_page_title, [2] = wikidata ID</b>"
						.. "<br><nowiki>{{#invoke:ControlArgs|doc1|wikidata_any_page_title|Q535}}</nowiki> "
	res = res .. frame:preprocess(	   "{{#invoke:ControlArgs|doc1|wikidata_any_page_title|Q535}}")
	--
	res = res .. "<br>* <b>funct = doc1, [1] = versions_details_title</b>"
						.. "<br><nowiki>{{#invoke:ControlArgs|doc1|versions_details_title}}</nowiki> "
	res = res .. frame:preprocess(	   "{{#invoke:ControlArgs|doc1|versions_details_title}}")
	--] ]
	return res
end -- function p.tests(frame)

return p