Basculer le menu
Changer de menu des préférences
Basculer le menu personnel
Non connecté(e)
Votre adresse IP sera visible au public si vous faites des modifications.
Version datée du 31 mai 2017 à 22:18 par module>Zebulon84 (-moz-border-radius n'est plus supporté depuis Firefox 13)
(diff) ← Version précédente | Version actuelle (diff) | Version suivante → (diff)

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

-- 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