Open main menu
Lua
CodeDiscussionEditHistoryLinksLink countNo test API Subpages:DocumentationTestsResultsSandboxLive code All modules
Usage
This module is used to display Navigation bar for taxa (called taxonavigation) by Taxonavigation,{{TaxonavigationIncluded}} and{{TaxonavigationIncluded2}}
Functions and their usage
How to improve and test this module
  1. develop your modification in Module:Taxonavigation/sandbox, the sandbox of this module
    which is used in the sandbox templates Taxonavigation/sandbox
  2. verify your changes in Module_talk:Taxonavigation/sandbox/testcases
  3. if needed improve the testcases Module:Taxonavigation/sandbox/testcases
  4. ask an administrator to report your modifications in Module:Taxonavigation
  5. verify again your changes in Module_talk:Taxonavigation/testcases
  6. if needed improve the testcases Module:Taxonavigation/testcases
See also

Code

local _common=require('Module:Biology')

-- Get a language object for formatDate and ucfirst.
local lang = mw.language.getContentLanguage()

----------------------------------------------------------------------------------------------------
---------- Exists utilities ------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
function articleExists(articleName)
	local page = mw.title.new(articleName, nil )
	if page then
		--_common.addDebug('articleExists(' .. articleName .. ')', tostring(page.exists))
		return page.exists
	else
		return false
	end
end

function categoryExists(categoryName)
	local categoryNameLC = lang:lc(categoryName)
	if not string.startsWith(categoryNameLC, 'category:') then
		categoryName = 'Category:' .. categoryName
	end
	local category = mw.title.new(categoryName, nil )
	if category then
		_common.addDebug('categoryExists(' .. categoryName .. ')', tostring(category.exists))
		return category.exists
	else
		return false
	end
end

----------------------------------------------------------------------------------------------------
---------- Rank utilities --------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
local _singularLatinRanksNeedingItalic = {
	cladus=false,
	['subtaxon']=false,
	['???']=false,
	['(unranked)']=false,
	supergroup=false,
	group=false,
	['informal group']=false,
	['⇒']=false,

	empire=false,
	kingdom=false,
	superdomain=false,
	domain=false,
	subdomain=false,

	superregnum=false,
	regnum=false,
	subregnum=false,
	infraregnum=false,

	superdivisio=false,
	divisio=false,
	subdivisio=false,
	infradivisio=false,
	superphylum=false,
	phylum=false,
	subphylum=false,
	infraphylum=false,

	superclassis=false,
	classis=false,
	subclassis=false,
	parvclassis=false,
	infraclassis=false,

	megacohors=false,
	supercohors=false,
	cohors=false,
	subcohors=false,
	infracohors=false,

	superordo=false,
	ordo=false,
	subordo=false,
	parvordo=false,
	infraordo=false,
	microordo=false,

	--suprafamilia=false,
	superfamilia=false,
	familia=false,
	subfamilia=false,

	supertribus=false,
	tribus=false,
	subtribus=false,
	infratribus=false,

	['genus group']=true,
	genus=true,
	nothogenus=true,
	subgenus=true,

-- here we manage botanic section (between species/series and genus) and zoology section (between family and ordo)
	supersectio=true,
	sectio=true,
	subsectio=true,

	series=true,
	subseries=true,

	species=true,
	hybrid=true,
	nothospecies=true,
	subspecies=true,
	nothosubspecies=true,

	varietas=true,
	cultivar=true,
	forma=true,
	subforma=true,
}

-- true  means must contain a space
-- false means must not contain a space
-- nil   means it depends
local _singularLatinRanksNeedingMultiWordTaxonName = {
	--cladus=false,
	--['subtaxon']=false,
	--['???']=false,
	--['(unranked)']=false,
	--supergroup=false,
	--group=false,
	--['informal group']=false,
	--['⇒']=false,

	--empire=false,
	--kingdom=false,
	--superdomain=false,
	--domain=false,
	--subdomain=false,

	superregnum=false,
	regnum=false,
	subregnum=false,
	infraregnum=false,

	superdivisio=false,
	divisio=false,
	subdivisio=false,
	infradivisio=false,
	superphylum=false,
	phylum=false,
	subphylum=false,
	infraphylum=false,

	superclassis=false,
	classis=false,
	subclassis=false,
	parvclassis=false,
	infraclassis=false,

	megacohors=false,
	supercohors=false,
	cohors=false,
	subcohors=false,
	infracohors=false,

	superordo=false,
	ordo=false,
	subordo=false,
	parvordo=false,
	infraordo=false,
	microordo=false,

	suprafamilia=false,
	superfamilia=false,
	familia=false,
	subfamilia=false,

	supertribus=false,
	tribus=false,
	subtribus=false,
	infratribus=false,

	['genus group']=true, -- 'Campiglossa group'
	genus=false,
	nothogenus=true,      -- 'x Rhododendron' 
	subgenus=true,		  -- 'Rhododendron subg. Hymenanthes' or 'Rhododendron (Hymenanthes)'

-- here we manage botanic section (between species/series and genus) and zoology section (between family and ordo)
	--supersectio=false,
	--sectio=false,		  -- I don't know 'Rhododendron sect. Pontica'
	--subsectio=false,	  -- I don't know 'Rhododendron subsect. Taliensia'

	--series=false,       -- I don't know 'Liatris ser. Scariosae'
	--subseries=false,    -- I don't know 'Liatris ser. Scariosae'

	species=true,
	hybrid=true,          -- 'Rhododendron x Hymenanthes'
	nothospecies=true, 	  -- 'Rhododendron x Hymenanthes'
	subspecies=true,
	nothosubspecies=true,

	varietas=true,
	cultivar=true,
	forma=true,
	subforma=true,
}

local _singularLatinRanksThatCanHaveX = {
	nothogenus=true,      -- 'x Rhododendron' 

	nothospecies=true, 	  -- 'Rhododendron x Hymenanthes'
	nothosubspecies=true,

	varietas=true,
	cultivar=true,
}

-- Must be similar to https://commons.wikimedia.org/wiki/Template:CheckSingularLatinRank
function doesSingularLatinRankExist(singularLatinRank)
	local singularLatinRankLC = lang:lc(singularLatinRank)
	local needItalic = _singularLatinRanksNeedingItalic[singularLatinRankLC]
	return not (needItalic == nil)
end

-- Must be similar to https://commons.wikimedia.org/wiki/Template:RankNeedsItalic
function rankNeedsItalic(singularLatinRank)
	local singularLatinRankLC = lang:lc(singularLatinRank)
	local needItalic = _singularLatinRanksNeedingItalic[singularLatinRankLC]
	if needItalic == nil then
		_common.addDebug('rankNeedsItalic',singularLatinRankLC .. '=>' .. tostring(needItalic))
		return true
	else
		return needItalic
	end
end

-- Will be used to get parameter categorizeTribesIn
local _pluralEnglishRankUPFBySingularLatinRankLC = {
	species='Species',
	genus='Genera',
	subtribus='Subtribes',
	tribus='Tribes',
	subfamilia='Subfamilies',
	familia='Families',
	superfamilia='Superfamilies',
	subordo='Suborders',
	ordo='Orders',
}
-- Will be used to create 'Lauraceae by subtribe'
local _singularEnglishRankLCBySingularLatinRankLC = {
	species='species',
	genus='genus',
	subtribus='subtribe',
	tribus='tribe',
	subfamilia='subfamily',
	familia='family',
	superfamilia='superfamily',
	infraordo='infraorder',
	subordo='suborder',
	ordo='order',
}

-- Returns '' if everything is taxonName looks coherent to its rank
-- Returns an error + category otherwise
function checkTaxonNameForARank(singularLatinRank, taxonName)
	local singularLatinRankLC = lang:lc(singularLatinRank)

	-- Check 1: some ranks require taxonName with space, others require taxonName without space
	local needsMultiWordTaxonName = _singularLatinRanksNeedingMultiWordTaxonName[singularLatinRankLC]
	if needsMultiWordTaxonName == nil then
		-- no way to determine if the taxon name needs a space
	else
		local strangeTaxonName = string.find(taxonName,'Virus',1,true)			-- like ?
			or string.find(taxonName,'virus',1,true)							-- like 'Coxsackievirus'
			or string.find(taxonName,'-',1,true)								-- like 'HIV-1'
		if strangeTaxonName then
			-- Viruses have their own classification usage => no check can be done
		else
			local containsSpace = string.find(taxonName,' ',1,true)
			if (containsSpace and not needsMultiWordTaxonName) then
				-- more complex because there are disambiguation like 'genus (familia)'
				local taxonNameLC = lang:lc(taxonName)
				strangeTaxonName = string.find(taxonName,' (',1,true)			-- Disambiguation like 'genus (familia)' => no check can be done
					or string.find(taxonNameLC,'incertae sedis',1,true)			-- IncertaeSedis like 'Euheterodonta incertae sedis' => no check can be done
					or string.find(taxonNameLC,'unassigned',1,true)				-- IncertaeSedis like 'Unassigned Cyprinidae' => no check can be done Unassigned
					or string.find(taxonNameLC,'unplaced',1,true)	    		-- IncertaeSedis like 'Elapidae unplaced' => no check can be done Unassigned
					or string.find(taxonNameLC,'bacter',1,true)				    -- Bacteria like 'Gamma Proteobacteria' or 'Candidatus Liberibacter' have strange naming => no check can be done
					or string.find(taxonNameLC,'fossil',1,true)					-- Fossils like 'Diadectes fossils' or 'Fossil Diadectes' => no check can be done
				if strangeTaxonName then
					-- some taxonName cannot be checked
				else
					return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect ' .. singularLatinRankLC .. ' with space:', 'Taxonavigation')
				end
			elseif (needsMultiWordTaxonName and not containsSpace) then
				return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect ' .. singularLatinRankLC .. ' without space:', 'Taxonavigation')
			end
		end
	end

	-- Check 2: taxonName should not contain ' X ' nor ' x ' but ' × '
	if string.find(taxonName,' X ',1,true) or string.startsWith(taxonName, 'X ') then
		return _common.incorrectBiologyTemplateUsage('Taxonavigation', "Incorrect taxonName '" .. taxonName .. "' (and category name) containing X instead of ×", 'Taxonavigation')
	elseif string.find(taxonName,' x ',1,true) or string.startsWith(taxonName, 'x ') then
		return _common.incorrectBiologyTemplateUsage('Taxonavigation', "Incorrect taxonName '" .. taxonName .. "' (and category name) containing x instead of ×", 'Taxonavigation')
	end

	local xpos = mw.ustring.find(taxonName,'×',1,true)
	if xpos then
		-- Check 2bis: taxonName should not contain '×' not followed by space
		_common.addDebug('checkTaxonNameForARank','string.len(taxonName)=' .. tostring(mw.ustring.len(taxonName)))
		_common.addDebug('checkTaxonNameForARank','xpos=' .. tostring(xpos))
		if mw.ustring.len(taxonName) == xpos then
			return _common.incorrectBiologyTemplateUsage('Taxonavigation', "Incorrect taxonName '" .. taxonName .. "' ending with ×", 'Taxonavigation')
		end
		local nextCar = mw.ustring.sub(taxonName, xpos+1, xpos+1)
		_common.addDebug('checkTaxonNameForARank','nextCar="' .. tostring(nextCar) .. '"')
		if nextCar ~= ' ' then
			return _common.incorrectBiologyTemplateUsage('Taxonavigation', "Incorrect taxonName '" .. taxonName .. "' containing × not followed by space", 'Taxonavigation')
		end

		-- Check 3: taxonName containing × should be nothoXXX
		local canHaveX = _singularLatinRanksThatCanHaveX[singularLatinRankLC]
		if not canHaveX then
			return _common.incorrectBiologyTemplateUsage('Taxonavigation', "Incorrect rank '" .. singularLatinRankLC
				.. "' for a taxon containing × (Maybe it is a notho" .. singularLatinRankLC .. ")", 'Taxonavigation')
		end
	end
	return ''
end

-- Calculates a category/gallery's title out of the taxonavigation parameters.
-- We need to find the category/article's taxonName in the taxonavigation
-- paramaters to ensure that its associated rank requires italic 
function calcItalicTitle(taxonName)
	-- Is it a category/article with disambiguation ?
	local taxonNameSecondPart = ''
	if string.endsWith(taxonName,')') then
		-- "Bombus (disamb)" but also subgenera like "Bombus (Psithyrus)" but NOT species like "genus (subgenus) species"
		local parenthesisStart = string.find(taxonName,' (',1,true)
		if parenthesisStart then
			taxonNameSecondPart = ' <small>' .. string.sub(taxonName,parenthesisStart+1) .. '</small>'
			taxonName = string.sub(taxonName,1,parenthesisStart-1)
		end
	end
	
	local namespace = mw.title.getCurrentTitle().nsText
	if not string.isNilOrEmpty(namespace) then
		namespace = namespace .. ':' -- like Category:
	end
	return namespace .. "''" .. taxonName .. "''" .. taxonNameSecondPart
end

-- Returns ' • Genus<b>: <i>[[:Category:Cinnamomum|Cinnamomum]]</i></b>' out rank=Genus, taxonName=Cinnamomum
-- The returned string is to be added to a Taxonavigation
function addRankAndTaxonName(frame, namespace, pagename, firstTaxon, singularLatinRank, taxonName)
	local taxonavigationStr = ''
	-- Separator 1
	if not firstTaxon then
		taxonavigationStr = '&nbsp;• '
	else
		taxonHaveAlreadyBeenDisplayed = true
	end

	-- Check singularLatinRank
	if not doesSingularLatinRankExist(singularLatinRank) then
		taxonavigationStr = taxonavigationStr .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect rank:', 'Taxonavigation')
	end
	
	-- Detect extinct sign: |Eohiodon†| or |†Eohiodon| (must be before calc of currentTaxonNameIsPagename)
	local rankNeedsItalic = rankNeedsItalic(singularLatinRank)
	local extinctCross = ''
	if string.find(taxonName,'†',1,true) then
		if rankNeedsItalic then
			extinctCross = "</i>†<i>"
		else
			extinctCross = '†'
		end
		taxonName = string.gsub(taxonName, '†', '', 5)
		taxonName = mw.text.trim(taxonName)
	end

	-- Add rank	+ open bold
	local currentTaxonNameIsPagename = (lang:ucfirst(taxonName) == lang:ucfirst(pagename))
	singularLatinRank = lang:ucfirst(singularLatinRank)
	if currentTaxonNameIsPagename then
		if not string.isNilOrEmpty(extinctCross) then
			-- we need to change the cross because it must be more visible
			if rankNeedsItalic then
				extinctCross = "</i><big>†</big><i>"
			else
				extinctCross = '<big>†</big>'
			end
		end
		taxonavigationStr = taxonavigationStr .. '<b>' .. singularLatinRank .. ':&nbsp;'
	else
		taxonavigationStr = taxonavigationStr .. singularLatinRank .. '<b>:&nbsp;'
	end

	-- Open italic
	local openItalicIfNeeded = ''
	local closeItalicIfNeeded = ''
	if rankNeedsItalic then
		openItalicIfNeeded = '<i>'
		closeItalicIfNeeded = '</i>'
	end
	taxonavigationStr = taxonavigationStr .. openItalicIfNeeded
	
	-- Check that taxonName knowing its ranks
	taxonavigationStr = taxonavigationStr .. checkTaxonNameForARank(singularLatinRank, taxonName)

	-- Add taxonName
	local taxonNameToDisplay = nil
	if (taxonName == 'Incertae Sedis') or (taxonName == 'Incertae sedis') or (taxonName == 'incertae sedis') or (taxonName == '?') then
		taxonNameToDisplay = "''Incertae sedis''"
	else
		-- Disambiguation: '|Ibicella (Martyniaceae)|'
		local taxonDisplayName = taxonName
		if string.endsWith(taxonName,')') then
			-- "Bombus (disamb)" but also subgenera like "Bombus (Psithyrus)" but NOT species like "genus (subgenus) species"
			local parenthesisStart = string.find(taxonName,' (',1,true)
			if parenthesisStart then
				taxonDisplayName = string.sub(taxonName,1,parenthesisStart-1) .. closeItalicIfNeeded .. '<small>&nbsp;' .. string.sub(taxonName,parenthesisStart+1) .. '</small>' .. openItalicIfNeeded
			end
		end
		taxonDisplayName = string.gsub(taxonDisplayName, ' ', '&nbsp;')

		if namespace == 0 then
			-- article/gallery
			if (not currentTaxonNameIsPagename) and articleExists(taxonName) then
				-- Higher taxon => link to existing articles
				taxonNameToDisplay = '[[' .. taxonName ..'|<span style="color:#005896">' .. taxonDisplayName ..'</span>]]'
			end
		elseif namespace == mw.site.namespaces.Category.id then
			-- category
			if currentTaxonNameIsPagename and articleExists(taxonName) then
				-- article's taxon => link to existing article
				taxonNameToDisplay = '[[' .. taxonName ..'|<span style="color:#005896">' .. taxonDisplayName ..'</span>]]'
			end
		end
		if not taxonNameToDisplay then
			-- general case
			taxonNameToDisplay = '[[:Category:' .. taxonName ..'|' .. taxonDisplayName ..']]'
		end
	end
	taxonavigationStr = taxonavigationStr .. extinctCross .. taxonNameToDisplay
	
	-- Close italic
	taxonavigationStr = taxonavigationStr .. closeItalicIfNeeded

	-- Close bold
	taxonavigationStr = taxonavigationStr .. '</b>'

	-- Change title
	if currentTaxonNameIsPagename and rankNeedsItalic then
		local title = calcItalicTitle(taxonName)
		if title then
			_common.addDebug('addRankAndTaxonName','title: ' .. title)
			taxonavigationStr = taxonavigationStr .. ' ' .. frame:preprocess('{{DISPLAYTITLE:' .. title .. '}}') .. ' '
		end
	end
	return taxonavigationStr
end

-- Returns '[[Category:Lauraceae by subtribe|Cinnamomum]]' or '[[Category:Subtribes of Lauraceae|Cinnamomum]]'
--     out of categorizeIn='Lauraceae', currentPageTaxonSingularEnglishRankLC='subtribe', currentPageTaxonPluralEnglishRankUPF='Subtribes', pagename=Cinnamomum in Category:Cinnamomum
-- The returned string is to be added to a Taxonavigation
-- It uses parameters:
-- * categorizeIn like 'Lauraceae' or 'FAMILIA'
-- * currentPageTaxonSingularEnglishRankLC is the singular english rank lowercase       of the taxon described by the Taxonavigation (among 'species', 'genus', 'subtribe', 'tribe', 'subfamily', 'family')
-- * currentPageTaxonPluralEnglishRankUPF  is the plural   english rank uppercase first of the taxon described by the Taxonavigation (among 'Species', 'Genera', 'Subtribes', 'Tribes', 'Subfamilies', 'Families')
function calcCategory(options, categorizeIn, pagename, currentPageTaxonSingularEnglishRankLC, currentPageTaxonPluralEnglishRankUPF)
	local categorizeInLC = lang:lc(categorizeIn)
	local cleverCategorization = doesSingularLatinRankExist(categorizeInLC)
	if cleverCategorization then
		-- categorizeIn like 'FAMILIA', so we need to find the family
		local parametersStr = options['parameters']
		if not parametersStr then
			_common.addDebug('calcCategory','Failed to find rank ' .. categorizeInLC .. ' in non present parameters')
			return nil
		end
		local parameters = mw.text.jsonDecode(parametersStr)
		if not parameters then
			_common.addDebug('calcCategory','Failed to find rank ' .. categorizeInLC .. ' in non json parameters')
			return nil
		end
		local realCategorizeIn = parameters[categorizeInLC]
		if string.isNilOrEmpty(realCategorizeIn) then
			_common.addDebug('calcCategory','Failed to find rank ' .. categorizeInLC .. ' in ' .. tostring(table.getn(parameters)) .. ' parameters')
			return nil
		end
		realCategorizeIn = string.gsub(realCategorizeIn, '†', '', 5)
		realCategorizeIn = mw.text.trim(realCategorizeIn)

		-- categorizeIn=FAMILIA => we replace it with the family name => categorizeIn=Lauraceae
		_common.addDebug('calcCategory','categorizeIn(modified)=' .. tostring(realCategorizeIn))
		categorizeIn = realCategorizeIn
	end

	-- Step1: try fullCategory like 'Lauraceae by subtribe' or 'Plantae by family'
	local fullCategory = categorizeIn .. ' by ' .. currentPageTaxonSingularEnglishRankLC
	_common.addDebug('calcCategory','fullCategory(first case)=' .. fullCategory)
	if not categoryExists(fullCategory) then
		_common.addDebug('calcCategory','fullCategory(first case) "' .. fullCategory .. '" does not exists')
		-- Step2: try fullCategory like 'Subtribes of Lauraceae'
		fullCategory = currentPageTaxonPluralEnglishRankUPF .. ' of ' .. categorizeIn
		_common.addDebug('calcCategory','fullCategory(second case)=' .. fullCategory)
		if cleverCategorization and not categoryExists(fullCategory) then
			_common.addDebug('calcCategory','fullCategory(second case+clever) "' .. fullCategory .. '" does not exists')
			return nil
		end
	end
	return '[[Category:' .. fullCategory .. '|' .. pagename .. ']]'
end

-- Returns '[[Category:Lauraceae by subtribe|Cinnamomum]]' or '[[Category:Subtribes of Lauraceae|Cinnamomum]]'
--     out of currentPageTaxonSingularLatinRankLC='subtribus', pagename=Cinnamomum in Category:Cinnamomum
-- The returned string is to be added to a Taxonavigation
-- It uses parameters:
-- * currentPageTaxonSingularLatinRankLC is the singular latin   rank lowercase of the taxon described by the Taxonavigation (species, genus ...)
function calcCategories(options, pagename, currentPageTaxonSingularLatinRankLC)
	if string.startsWith(currentPageTaxonSingularLatinRankLC, 'notho') then
		-- nothogenus, nothospecies, nothosubspecies should be categorizes like genus, species, subspecies
		currentPageTaxonSingularLatinRankLC = string.sub(currentPageTaxonSingularLatinRankLC, string.len('notho')+1)
		_common.addDebug('calcCategories','currentPageTaxonSingularLatinRankLC ' .. tostring(currentPageTaxonSingularLatinRankLC) .. ' was a notho')
	end
	local currentPageTaxonPluralEnglishRankUPF = _pluralEnglishRankUPFBySingularLatinRankLC[currentPageTaxonSingularLatinRankLC]
	if not currentPageTaxonPluralEnglishRankUPF then
		_common.addDebug('calcCategories','currentPageTaxonSingularLatinRankLC ' .. tostring(currentPageTaxonSingularLatinRankLC) .. ' seems incorrect')
		return nil
	end

	local currentPageTaxonSingularEnglishRankLC = _singularEnglishRankLCBySingularLatinRankLC[currentPageTaxonSingularLatinRankLC]
	if not currentPageTaxonSingularEnglishRankLC then
		_common.addDebug('calcCategories','currentPageTaxonSingularLatinRankLC ' .. tostring(currentPageTaxonSingularLatinRankLC) .. ' seems incorrect')
		return nil
	end

	local optionBase = 'categorize' .. currentPageTaxonPluralEnglishRankUPF .. 'In'
	local categories = ''

	for optionIndex = 1, 5 do
		local optionName = optionBase
		if (optionIndex > 1) then
			optionName = optionBase .. tostring(optionIndex)
		end
		local categorizeIn = options[optionName]
		_common.addDebug('calcCategories','optionName=' .. tostring(optionName))
		_common.addDebug('calcCategories','categorizeIn=' .. tostring(categorizeIn))

		if not categorizeIn then
			-- categorizeSubtribesInX does not exists => stop iteration on X
			_common.addDebug('calcCategories','categories=' .. categories)
			return categories -- Returns less than 5 categories
		end

		local newCategory = calcCategory(options, categorizeIn, pagename, currentPageTaxonSingularEnglishRankLC, currentPageTaxonPluralEnglishRankUPF)
		if newCategory then
			categories = categories .. newCategory
		else
			-- categorizeSubtribesInX led to nothing, let us try categorizeSubtribesInX+1
		end
	end
	_common.addDebug('calcCategories','categories=' .. categories)
	return categories -- Returns 5 categories
end

-- Returns a string containing a category for include templates like Template:Angiospermes and 'Template:Lauraceae (APG)'
-- You can test the result of this function in Template:Angiosperms/sandbox and 'Template:Lauraceae (APG)/sandbox'
function calcIncludeTemplateCategory(options, namespace, pagename)
	local categorizeTemplate = options['categorizeTemplate']
	if categorizeTemplate == 'no' then
		return ''
	end
	local categoryName = 'Templates to include in Taxonavigation'	-- for templates without include using TaxonavigationIncluded
	local includeOption = options['include']
	if not string.isNilOrEmpty(includeOption) then
		includeOption = string.gsub(includeOption, '/sandbox', '', 1) -- for include=Angiosperms/sandbox
		categoryName = includeOption .. ' Templates to include in Taxonavigation'	-- for templates with include using TaxonavigationIncluded2
	end

	local errorCategory = ''
	if not categoryExists(categoryName) then
		errorCategory = _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Category "' .. categoryName .. '" does not exist', 'Taxonavigation')
	end
	return '[[Category:' .. categoryName .. ']]' .. errorCategory
end

-- Returns a string containing a documentation for include templates like Template:Angiospermes and 'Template:Lauraceae (APG)'
-- You can test the result of this function in Template:Angiosperms/sandbox and 'Template:Lauraceae (APG)/sandbox'
function calcIncludeTemplateDocumentation(options, namespace, pagename)
	local documentTemplate = options['documentTemplate']
	if documentTemplate == 'no' then
		return ''
	end

	local classificationLine = ''
	local documentTemplateWithClassification = options['documentTemplateWithClassification']
	if documentTemplateWithClassification == 'none' then
		_common.addDebug('displayIncludeDocumentation','documentTemplateWithClassification(case1): documentTemplateWithClassification=none')
	else
		if string.isNilOrEmpty(documentTemplateWithClassification) then
			local classification = options['classification']
			if string.isNilOrEmpty(classification) then
				-- classification= has not been provided by 'Template:Angiospermes' to Template:TaxonavigationIncluded
				-- => need to ask the user to call {{Taxonavigation|include=CurrentTemplate| with classification=
				classificationLine = '<dd>classification=?|</dd>'
				_common.addDebug('displayIncludeDocumentation','documentTemplateWithClassification(case2): documentTemplateWithClassification and classification are empty => display IOC')
			else
				-- classification= has been provided by 'Template:Lauraceae (APG)' to Template:TaxonavigationIncluded2
				-- => no need to ask the user to call {{Taxonavigation|include=CurrentTemplate| with classification=
				_common.addDebug('displayIncludeDocumentation','documentTemplateWithClassification(case3): documentTemplateWithClassification is empty AND classification has been provided => do not document classification')
			end
		else
			classificationLine = '<dd>classification=' .. documentTemplateWithClassification .. '|</dd>'
			_common.addDebug('displayIncludeDocumentation','documentTemplateWithClassification(case4): display ' .. tostring(documentTemplateWithClassification))
		end
	end

	local taxonavigationAutoCategory = nil
	local space = string.find(pagename, ' ', 1, true)
	if space then
		local taxonName = string.sub(pagename, 1, space-1)
		taxonavigationAutoCategory = '{{TaxonavigationAutoCategory|' .. taxonName .. '|includename=' .. pagename .. '}}'
	else
		taxonavigationAutoCategory = '{{TaxonavigationAutoCategory|' .. pagename .. '}}'
	end
	
	local doc = [=[<br/>
		<br/>
		<dl>
		<dt>Usage</dt>
		<dd>This template should be used in categories and articles as [[:Template:Taxonavigation|{{Taxonavigation}}]]'s parameter include=</dd>
		<dd>For that reason, those kind of templates are called 'taxonavigation include templates' or simply 'include templates'.</dd>
		<dd>There are such templates for:
		<table style="text-align:left">
		<tr>
			<td><ul><li>[[:Category:Templates to include in Taxonavigation|all classes]]</li></ul></td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Insecta Templates to include in Taxonavigation|all insect orders]]</li></ul></td>
			<td>WARNING: they are named '''[[Template:Orthoptera|&lt;orderName&gt;]]''' except for '''[[Template:Coleoptera (include)|Coleoptera (include)]]''' and '''[[Template:Lepidoptera (include)|Lepidoptera (include)]]'''</td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Angiosperms Templates to include in Taxonavigation|all angiospermes families]]</li></ul></td>
			<td>WARNING: as these families template follow [[APGIII]], they are named '''[[Template:Cactaceae (APG)|&lt;familyName&gt; (APG)]]''' and contain classification=APGIII</td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Pinopsida Templates to include in Taxonavigation|all conifer families]]</li></ul></td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Pteridophyta Templates to include in Taxonavigation|all fern families]]</li></ul></td>
			<td>WARNING: as these families template follow [[Smith System]], they are named '''[[Template:Schizaeaceae (Smith)|&lt;familyName&gt; (Smith)]]''' and contain classification=Smith</td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Aves Templates to include in Taxonavigation|all bird families]]</li></ul></td>
			<td>WARNING: as these families template follow [[Template:Taxonavigation/IOC_Classification|IOC classification]], they are named '''[[Template:Diomedeidae (IOC)|&lt;familyName&gt; (IOC)]]''' and contain classification=IOC</td>
		</tr>
		</table>
		</dd>
		</dl>
		<br/>
		<dl>
		<dt>Example</dt>
		<dd>{{Taxonavigation|<br /></dd>
		<dd><b>include=]=] .. pagename .. [=[</b>|<br /></dd>]=]
		.. classificationLine ..
		[=[<dd>...<br /></dd>
		<dd>authority=Linnaeus, 1758}}<br /></dd>
		</dl>
		<br/>
		<dl>
		<dt>Automatic categories</dt>]=]

	local needsParameterRanks = false
	local needsParameterParameters = false
	local categoriesOptionFound = false
	for clever = 0,1 do
		-- First iteration: we display non clever categories, second iteration: we display clever categories
		for singularLatinRankLC, pluralEnglishRankUPF in pairs(_pluralEnglishRankUPFBySingularLatinRankLC) do
			local optionBase = 'categorize' .. pluralEnglishRankUPF .. 'In'
			local singularEnglishRankLC = _singularEnglishRankLCBySingularLatinRankLC[singularLatinRankLC]
	
			for optionIndex = 1, 5 do
				local optionName = optionBase
				if (optionIndex > 1) then
					optionName = optionBase .. tostring(optionIndex)
				end
				local categorizeIn = options[optionName]
				_common.addDebug('displayIncludeDocumentation',optionName .. '=' .. tostring(categorizeIn))
				if not categorizeIn then
					break;
				end
				categoriesOptionFound = true

				local cat1 = categorizeIn .. ' by ' .. singularEnglishRankLC
				local cat2 = pluralEnglishRankUPF .. ' of ' .. categorizeIn

				local cleverCategorization = doesSingularLatinRankExist(categorizeIn)
				if (not cleverCategorization) and (clever == 0) then
					cat1 = cat1 .. '|' .. cat1
					cat2 = cat2 .. '|' .. cat2
					doc = doc .. '<dd>As you provided parameter ' .. optionName .. ', [[:Category:' .. cat1 .. ']] or [[:Category:' .. cat2 ..
						']] will be automatically added to ' .. string.lower(pluralEnglishRankUPF) .. ' using this template.</dd>'
					needsParameterRanks = true
				elseif cleverCategorization and (clever == 1) then
					-- categorizeIn like 'FAMILIA', so we need to find the family
					cat1 = cat1 .. '|<' .. categorizeIn .. '> by ' .. singularEnglishRankLC
					cat2 = cat2 .. '|' .. pluralEnglishRankUPF .. ' of <' .. categorizeIn .. '>'
					doc = doc .. '<dd>As you provided parameter ' .. optionName .. ', [[:Category:' .. cat1 .. ']] or [[:Category:' .. cat2 ..
						']] will be automatically added to ' .. string.lower(pluralEnglishRankUPF) .. ' using this template.</dd>'
					doc = doc ..
						'<dd><dl><dd>This automatic category should only contain <b>{{TaxonavigationAutoCategory|<' .. categorizeIn .. '>|includename=' .. pagename .. '}}</b></dd></dl></dd>'
					needsParameterRanks = true
					needsParameterParameters = true
				end
			end
		end

		if (clever == 0) and categoriesOptionFound then
			doc = doc ..
				'<dd><dl><dd>These automatic category should only contain <b>' .. taxonavigationAutoCategory .. '</b></dd></dl></dd>'
		end
	end

	if categoriesOptionFound then
		local wikicode = mw.title.getCurrentTitle():getContent()

		local parameter = 'rank={{{rank|}}}'
		if needsParameterRanks and not string.find(wikicode, parameter, 1, true) then
			doc = doc .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Please add ' .. parameter, 'Taxonavigation')
		end

		parameter = 'parameters={{{parameters|}}}'
		if needsParameterParameters and not string.find(wikicode, parameter, 1, true) then
			doc = doc .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Please add ' .. parameter, 'Taxonavigation')
		end
	else
		doc = doc .. [=[
			<dd>You could provide following parameters to have automatic categories added to species and genera:
			<dl>
			<dd><table style="text-align:left">
			<tr>
				<td><b>rank={{{rank|}}}|</b></td>
			</tr>
			<tr>
				<td><b>categorizeSpeciesIn=<i>parentName</i>|</b></td>
				<td>&lt;-- Only if <i>parent</i> contains multiple genera</td>
			</tr>
			<tr>
				<td><b>categorizeGeneraIn=<i>parentName</i>|</b></td>
				<td>&lt;-- Only if <i>parent</i> contains subfamilies or tribes</td>
			</tr>
			<tr>
				<td><b>categorizeSubtribesIn=<i>parentName</i>|</b></td>
				<td>&lt;-- Only if <i>parent</i> contains tribes AND subtribes</td>
			</tr>
			<tr>
				<td><b>categorizeTribesIn=<i>parentName</i>|</b></td>
				<td>&lt;-- Only if <i>parent</i> contains subfamilies AND tribes</td>
			</tr>
			<tr>
				<td><b>categorizeSubfamiliesIn=<i>parentName</i>|</b></td>
				<td>&lt;-- Only if <i>parent</i> contains clades containing subfamilies</td>
			</tr>
			<tr>
				<td><b>categorizeFamiliesIn=<i>parentName</i>|</b></td>
				<td>&lt;-- Only if <i>parent</i> contains multiple families</td>
			</tr>
			</table></dd>
			</dl>
			</dd>
			]=]
	end

	doc = doc ..
		[=[</dl>
		<br/>
		<dl>
		<dt>See also</dt>
		<dd>
		<ul>
		<li>[[Template:Taxonavigation|{{Taxonavigation}}]] to use in categories and articles</li>
		<li>[[Template:TaxonavigationIncluded|{{TaxonavigationIncluded}}]] to use in include templates</li>
		<li>[[Template:TaxonavigationIncluded2|{{TaxonavigationIncluded2}}]] to use in include templates that themself use include=</li>
		<li>[[Template:TaxonavigationAutoCategory|{{TaxonavigationAutoCategory}}]] to use in automatic categories</li>
		<li>[[Module:Taxonavigation]] that displays this documentation and adds categories</li>
		</ul>
		</dd>
		</dl>]=]
	return doc
end

local _templatesThatShouldNotBeAutoDocumented = {
	Taxonavigation=true,
	Coleoptera=true,
	Lepidoptera=true,
	Coleoptera2=true,
	Lepidoptera2=true,
	['Coleoptera/sandbox']=true,
}

-- Returns a string containing documentation and categories for include templates like Template:Angiospermes and 'Template:Lauraceae (APG)'
-- You can test the result of this function in Template:Angiosperms/sandbox and 'Template:Lauraceae (APG)/sandbox'
function calcIncludeTemplateDocumentationAndCategory(options, namespace, pagename)
	if (namespace ~= mw.site.namespaces.Template.id) then
		return ''
	end
	if string.startsWith(pagename,'TaxonavigationIncluded') or
	   string.startsWith(pagename,'Taxonavigation/') or
	   string.startsWith(pagename,'Biohist') or
	   _templatesThatShouldNotBeAutoDocumented[pagename] then
		-- Template:TaxonavigationIncluded, ..., Template:TaxonavigationIncluded2/sandbox have their own documentation that add their own categories
		-- Template:Taxonavigation/testcases, Template:Taxonavigation/sandbox/testcases have no need for documentation nor categories
		-- Template:Taxonavigation, Coleoptera, Lepidoptera have no need for documentation nor categories
		-- Template:Biohist and Template:Biohist/sandbox have no need for documentation nor categories
		return ''
	end
	return calcIncludeTemplateDocumentation(options, namespace, pagename) ..
		calcIncludeTemplateCategory(options, namespace, pagename)
end

-- Returns a string that Template:Taxonavigation will display
-- It uses parameters:
-- * indexed parameters containing Cladus,magnoliids,Ordo,Laurales,Familia,Lauraceae,Genus,Cinnamomum...
-- * namespaceForDebug
-- * pagenameForDebug
-- * rank (when called by TaxanavigationIncluded or TaxanavigationIncluded2)
-- * categorizeSpeciesIn2, categorizeSubtribesIn (when called by TaxanavigationIncluded)
function taxonavigation(frame, options)
	local taxonavigationStr = ''
	local firstTaxon = string.isNilOrEmpty(options['include']) --include=XXX => taxonHaveAlreadyBeenDisplayed=true
	local currentTitle = mw.title.getCurrentTitle()
	local namespace = currentTitle.namespace
	local pagename = currentTitle.text

	-- prepare NonRegression
	local namespaceForDebug = options['namespaceForDebug']
	local pagenameForDebug = options['pagenameForDebug']
	if not string.isNilOrEmpty(namespaceForDebug) then
		namespace = tonumber(namespaceForDebug,10)
	end
	if not string.isNilOrEmpty(pagenameForDebug) then
		pagename = pagenameForDebug
	end
	local isGalleryOrCategory = (namespace == 0) or (namespace == mw.site.namespaces.Category.id)
	_common.addDebug('taxonavigation','caller=' .. tostring(options['caller']) .. '----------------------------------------------------------')
	--_common.addDebug('taxonavigation','namespaceForDebug=' .. tostring(namespaceForDebug))
	--_common.addDebug('taxonavigation','pagenameForDebug=' .. tostring(pagenameForDebug))
	--_common.addDebug('taxonavigation','namespace=' .. tostring(namespace))
	--_common.addDebug('taxonavigation','pagename=' .. tostring(pagename))
	--_common.addDebug('taxonavigation','isGalleryOrCategory=' .. tostring(isGalleryOrCategory))
	--_common.addDebug('taxonavigation','serializeCurrentParameters=' ..serializeCurrentParameters(options))
	_common.addDebug('taxonavigation','options[parameters]=' .. tostring(options['parameters']))
	--_common.addDebug('taxonavigation','options[1]=' .. tostring(options['1']))
	--_common.addDebug('taxonavigation','options[2]=' .. tostring(options['2']))

	for paramIndex = 1, 1602, 2 do
		local singularLatinRank = options[tostring(paramIndex)]
		local taxonName         = options[tostring(paramIndex+1)]

		if string.isNilOrEmpty(taxonName) then
			if string.isNilOrEmpty(singularLatinRank) then
				--_common.addDebug('taxonavigation','stopped at index ' .. tostring(paramIndex) .. ' singularLatinRank=' .. tostring(singularLatinRank) .. ' taxonName=' .. tostring(taxonName))
			else
				taxonavigationStr = taxonavigationStr .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Strange parameter "' .. singularLatinRank .. '"', 'Taxonavigation')
			end
			break
		end
		singularLatinRank       = mw.text.trim(singularLatinRank)
		taxonName               = mw.text.trim(taxonName) -- Don't suppress †, it will be done by addRankAndTaxonName()

		if (singularLatinRank == 'EMPTY') and (taxonName == 'EMPTY') then
			-- Template:Coleoptera and Template:Lepidoptera have strange empty lines
		elseif (taxonName == 'NOTTOBEDISPLAYED') then
			-- Template:Lepidoptera have strange empty lines
		else
			taxonavigationStr = taxonavigationStr .. addRankAndTaxonName(frame, namespace, pagename, firstTaxon, singularLatinRank, taxonName)
			firstTaxon = false
		end
	end

	if isGalleryOrCategory then
		local currentPageTaxonSingularLatinRank = options['rank'] -- rank of the taxon described by the Taxonavigation
		_common.addDebug('taxonavigation','currentPageTaxonSingularLatinRank=' ..tostring(currentPageTaxonSingularLatinRank) .. ' (available in include templates only)')
		if not string.isNilOrEmpty(currentPageTaxonSingularLatinRank) then
			-- Called by Template:TaxonavigationIncluded
			local categories = calcCategories(options, pagename, lang:lc(currentPageTaxonSingularLatinRank))
			if categories then
				taxonavigationStr = taxonavigationStr .. categories
			end
		end
	end

	local mustBeEmpty = options['mustBeEmpty']
	if not string.isNilOrEmpty(mustBeEmpty) then
		taxonavigationStr = taxonavigationStr .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Parameter mustBeEmpty is not empty', 'Taxonavigation')
	end

	return taxonavigationStr ..
		calcIncludeTemplateDocumentationAndCategory(options, currentTitle.namespace, currentTitle.text)
end

-- Finds current page taxon singularLatinRank from Taxonavigation parameters.
-- For example, it returns 'Genus' out of {{Taxonavigation|...|Genus|Cinnamomum|..}} in Category:Cinnamomum
function findCurrentPageTaxonSingularLatinRank(options)
	local currentTitle = mw.title.getCurrentTitle()
	local pagename = currentTitle.text

	-- prepare NonRegression
	local pagenameForDebug = options['pagenameForDebug']
	if not string.isNilOrEmpty(pagenameForDebug) then
		pagename = pagenameForDebug
	end

	for paramIndex = 1, 1602, 2 do
		local singularLatinRank = options[tostring(paramIndex)]
		local taxonName         = options[tostring(paramIndex+1)]

		if string.isNilOrEmpty(singularLatinRank) or string.isNilOrEmpty(taxonName) then
			return ''
		end
		singularLatinRank       = mw.text.trim(singularLatinRank)
		taxonName               = string.gsub(taxonName, '†', '', 5)
		taxonName               = mw.text.trim(taxonName)

		if (singularLatinRank == 'EMPTY') and (taxonName == 'EMPTY') then
			-- Template:Coleoptera and Template:Lepidoptera have strange empty lines
		elseif (taxonName == 'NOTTOBEDISPLAYED') then
			-- Template:Lepidoptera have strange empty lines
		else
			local currentTaxonNameIsPagename = (lang:ucfirst(taxonName) == lang:ucfirst(pagename))
			if currentTaxonNameIsPagename then
				local currentPageTaxonSingularLatinRank = lang:ucfirst(singularLatinRank)
				return currentPageTaxonSingularLatinRank
			end
		end
	end
	return ''
end

-- Serializes rank and taxon parameters from Taxonavigation.
-- For example, it returns '{"species":"Cinnamomum camphora","CurrentPageTaxonRank":"Familia","genus":"Cinnamomum"}' out of {{Taxonavigation|...|Genus|Cinnamomum|Species|Cinnamomum camphora|..}} in Category:Cinnamomum
function serializeCurrentParameters(options)
	local currentTitle = mw.title.getCurrentTitle()
	local pagename = currentTitle.text

	-- prepare NonRegression
	local pagenameForDebug = options['pagenameForDebug']
	if not string.isNilOrEmpty(pagenameForDebug) then
		pagename = pagenameForDebug
	end

	-- parameters is the table to be returned serialized with json
	local parameters = {}

	-- provide caller for debug
	local caller = options['caller']
	if not string.isNilOrEmpty(caller) then
		parameters['caller'] = caller
	end

	for paramIndex = 1, 1602, 2 do
		local singularLatinRank = options[tostring(paramIndex)]
		local taxonName         = options[tostring(paramIndex+1)]

		if string.isNilOrEmpty(singularLatinRank) or string.isNilOrEmpty(taxonName) then
			return mw.text.jsonEncode(parameters)
		end
		singularLatinRank       = mw.text.trim(singularLatinRank)
		taxonName               = string.gsub(taxonName, '†', '', 5)
		taxonName               = mw.text.trim(taxonName)

		if (singularLatinRank == 'EMPTY') and (taxonName == 'EMPTY') then
			-- Template:Coleoptera and Template:Lepidoptera have strange empty lines
		elseif (taxonName == 'NOTTOBEDISPLAYED') then
			-- Template:Lepidoptera have strange empty lines
		else
			parameters[lang:lc(singularLatinRank)] = taxonName  -- can contains extinct sign
	
			local currentTaxonNameIsPagename = (lang:ucfirst(taxonName) == lang:ucfirst(pagename))
			if currentTaxonNameIsPagename then
				local currentPageTaxonSingularLatinRank = lang:ucfirst(singularLatinRank)
				parameters['CurrentPageTaxonRank'] = currentPageTaxonSingularLatinRank
			end
		end
	end
	return mw.text.jsonEncode(parameters)
end

local _acceptedNamedParameters = {
	authority=true,
	include=true,
	documentTemplate=true,
	documentTemplateWithClassification=true,
	categorizeTemplate=true,
	classification=true,
	parameters=true,
	namespaceForDebug=true,
	pagenameForDebug=true,
	caller=true,
	rank=true,
	mustBeEmpty=true
	-- + categorizeFamiliesInX
}

-- Detects that incorrect parameters have been passed to {{Taxonavigation}}
function detectTaxonavigationBadParameters(options)
	local authority = options['authority']
	if authority and string.find(authority, '†', 1, true) then
		return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Please transfer † from authority to Taxonavigation last taxon', 'Taxonavigation')
	end
	for index, lang in pairs(options) do
		if type(index) == 'string' then
			if _acceptedNamedParameters[index] then
				-- normal argument pair: include=Angiosperms, authority=L.
				-- _common.addDebug('detectTaxonavigationBadParameters', index .. ' is in _acceptedNamedParameters')
			elseif string.startsWith(index, 'categorize') then
				-- normal argument pair: categorizeFamiliesIn=FAMILIA
				-- _common.addDebug('detectTaxonavigationBadParameters', index .. ' starts with categorize')
			else
				-- bad    argument pair: Authority=BadOrtho, Familia=Saturniidae
				-- _common.addDebug('detectTaxonavigationBadParameters', index .. ' seems bad')
				return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect parameter "' .. index .. '"', 'Taxonavigation')
			end
		else
			-- normal argument pair: 1=Familia, 2=Saturniidae
			-- _common.addDebug('detectTaxonavigationBadParameters', tostring(index) .. ' is not a string')
		end
	end
	return ''
end

----------------------------------------------------------------------------------------------------
---------- PUBLIC FUNCTIONS ------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
local p = {}
local getArgs = require('Module:Arguments').getArgs

-- public version of taxonavigation
-- Used by {{Taxonavigation}}, {{TaxonavigationIncluded}} and {{TaxonavigationIncluded2}}
function p.taxonavigation(frame)
	local taxonavigationStr = taxonavigation(frame, getArgs(frame))

	--_common.addDebug('p.taxonavigation', detectTaxonavigationBadParameters(getArgs(frame)))
	taxonavigationStr = taxonavigationStr .. _common.getDebug('<BR/>')

	return taxonavigationStr .. detectTaxonavigationBadParameters(getArgs(frame))
end

-- public version of findCurrentPageTaxonRank
-- Used by {{Taxonavigation}} and {{TaxonavigationIncluded2}}
function p.findCurrentPageTaxonRank(frame)
	local args = getArgs(frame)

	return findCurrentPageTaxonSingularLatinRank(args)
end

-- public version of serializeCurrentParameters
-- Used by {{Taxonavigation}}
function p.serializeCurrentParameters(frame)
	local args = getArgs(frame)

	return serializeCurrentParameters(args)
end

----------------------------------------------------------------------------------------------------
---------- Testcase public functions (return string) -----------------------------------------------
----------------------------------------------------------------------------------------------------

function p.testcase_stringTrim(frame)
	return mw.text.trim(tostring(frame.args['1']))
end

function p.testcase_doesSingularLatinRankExist(frame)
	local singularLatinRank = frame.args['1']
	if string.isNilOrEmpty(singularLatinRank) then
		return false
	end
	return tostring(doesSingularLatinRankExist(singularLatinRank))
end

function p.testcase_rankNeedsItalic(frame)
	local singularLatinRank = frame.args['1']
	if string.isNilOrEmpty(singularLatinRank) then
		return true
	end
	return tostring(rankNeedsItalic(singularLatinRank))
end

function p.testcase_articleExists(frame)
	local article = frame.args['1']
	if string.isNilOrEmpty(article) then
		return false
	end
	return tostring(articleExists(article))
end

function p.testcase_categoryExists(frame)
	local category = frame.args['1']
	if string.isNilOrEmpty(category) then
		return false
	end
	return tostring(categoryExists(category))
end

function p.testcase_calcItalicTitle(frame)
	return mw.text.nowiki(calcItalicTitle(frame.args['1']))
end

return p