মডিউল:Navseasoncats/খেলাঘর

উইকিপিডিয়া, মুক্ত বিশ্বকোষ থেকে

সম্পর্কিত[সম্পাদনা]

Recognized category types
ধরন উদাহরণ বিষয়শ্রেণী বিসি(ই)? উদাহরণ আউটপুট
Season 2001–02 FA Cup No
  • ১৯৯৮–৯৯
  • ১৯৯৯–২০০০
  • ২০০০–০১
  • ২০০১–০২
  • ২০০২–০৩
  • ২০০৩–০৪
  • ২০০৪–০৫
TV season Futurama (season 1) episodes
Office term MEPs 2004–2009 No
  • ১৯৮৯–১৯৯৪
  • ১৯৯৪–১৯৯৯
  • ১৯৯৯–২০০৪
  • ২০০৪–২০০৯
  • ২০০৯–২০১৪
  • ২০১৪–২০১৯
  • ২০১৯–২০২৪
Numerical range Taxonbars with 30–34 taxon IDs
  • ১৮–২২
  • ২২–২৬
  • ২৬–৩০
  • ৩০–৩৪
  • ৩৪–৩৮
  • ৩৮–৪২
  • ৪২–৪৬
Decade 2020s awards BC
  • ১৯৭০-এর দশক
  • ১৯৮০-এর দশক
  • ১৯৯০-এর দশক
  • ২০০০-এর দশক
  • ২০১০-এর দশক
  • ২০২০-এর দশক
  • ২০৩০-এর দশক
  • ২০৪০-এর দশক
  • ২০৫০-এর দশক
  • ২০৬০-এর দশক
  • ২০৭০-এর দশক
Year 1999 in Scotland BC(E)
  • ১৯৯৪
  • ১৯৯৫
  • ১৯৯৬
  • ১৯৯৭
  • ১৯৯৮
  • ১৯৯৯
  • ২০০০
  • ২০০১
  • ২০০২
  • ২০০৩
  • ২০০৪
Year (condensed) Candidates in the 2000 US presidential election
  • ১৯৯৫
  • ১৯৯৬
  • ১৯৯৭
  • ১৯৯৮
  • ১৯৯৯
  • ২০০০
  • ২০০১
  • ২০০২
  • ২০০৩
  • ২০০৪
  • ২০০৫
Ordinal (temporal) 2nd-century rabbis BC(E)
  • ৪তম BCE
  • ৩তম BCE
  • ২তম় BCE
  • ১তম BCE
  • ১তম
  • ২তম়
  • ৩তম
  • ৪তম
  • ৫তম
  • ৬তম
  • ৭তম
Ordinal (numeric) 5th Lok Sabha members
  • ৫তম
  • ৪তম
  • ৩তম
  • ২তম়
  • ১তম
  • ১তম
  • ২তম়
  • ৩তম
  • ৪তম
  • ৫তম
  • ৬তম
Ordinal (word) Fifth Dynasty of Egypt
Roman numeral Deputies of Legislature X of the Kingdom of Italy
  • V
  • VI
  • VII
  • VIII
  • IX
  • X
  • XI
  • XII
  • XIII
  • XIV
  • XV
Mixed decade 1760s in the Province of Quebec (1763–1791)
  • ১৭১০-এর দশক
  • ১৭২০-এর দশক
  • ১৭৩০-এর দশক
  • ১৭৪০-এর দশক
  • ১৭৫০-এর দশক
  • ১৭৬০-এর দশক
  • ১৭৭০-এর দশক
  • ১৭৮০-এর দশক
  • ১৭৯০-এর দশক
  • ১৮০০-এর দশক
  • ১৮১০-এর দশক
Mixed year 1763 establishments in the Province of Quebec (1763–1791)
  • ১৭৭০
  • ১৭৭১
  • ১৭৭২
  • ১৭৭৩
  • ১৭৭৪
  • ১৭৭৫
  • ১৭৭৬
  • ১৭৭৭
  • ১৭৭৮
  • ১৭৭৯
  • ১৭৮০

আচরণ সন্ধান[সম্পাদনা]

Multi-year seasons/office terms/numerical ranges are acceptable as long as the duration/range size remains constant, and no years/numbers are randomly skipped. The length of each duration/range is automatically determined from the originating category name, up to and including 10 years. MOS:DATERANGE compliance is preferred, but some deviation is allowed and tracked. {{Category redirect}}s are followed, and tracked for either MOS contravention (to be corrected) or for navigational aid (no error). The gap size between successive durations/ranges is also automatically determined, up to and including 5 years if a surrounding category is found, and defaults to 0 (e.g. 1995–961996–97).

Condensed year display is supported for presidential categories only (at the moment), for gaps up to and including 5 years, and defaults to 1.

সীমাবদ্ধতা[সম্পাদনা]

  • Season/office term categories do not work for any years BC, which will be hidden, because no working examples were found.
  • Decade categories recognize BC, but not BCE, because no working examples were found.
  • Condensed year display is supported for presidential categories only, due to their consistency.
  • Condensed Olympics display is not supported due to inconsistencies; use {{Winter Olympics by year category navigation}}, etc., instead.
  • Ordinal words do not work above the ninety-ninth; no higher working examples found.
  • General: for large, permanent gaps between successive categories, or when the base category name changes, use {{Category pair}} in addition to {{Navseasoncats}} on both sides of the gap/name change. Even if {{Navseasoncats}} is isolated, it has the benefit of confirming the absence of nearby categories to the reader/maintainer.

সম্পর্কিত সিএফডি[সম্পাদনা]

ব্যবহার[সম্পাদনা]

Typical usage
Specify a minimum and/or maximum year to display
To not automatically follow {{Category redirect}}s
Exceptional cases

পরীক্ষামূলক[সম্পাদনা]

To test the output of the template on a particular category name, use the |testcase= parameter, and |testcasegap= if necessary:

  • ১৭৫০
  • ১৭৫১
  • ১৭৫২
  • ১৭৫৩
  • ১৭৫৪
  • ১৭৫৫
  • ১৭৫৬
  • ১৭৫৭
  • ১৭৫৮
  • ১৭৫৯
  • ১৭৬০


  • -৫
  • -৪
  • -৩
  • -২
  • -১

অনুসরণকরণ বিষয়শ্রেণী[সম্পাদনা]

Purge this page to update the totals

If the template encounters an issue, it displays an error message and/or places the category into one or more of the following tracking categories:

রক্ষণাবেক্ষণ প্রয়োজন[সম্পাদনা]

রক্ষণাবেক্ষণ সম্ভব[সম্পাদনা]

শুধুমাত্র অনুসরণকরণ[সম্পাদনা]

আরও দেখুন[সম্পাদনা]

local p = {}
local num_con = require('Module:Numeral converter').convert

local errors = ''
local nexistingcats = 0
local currtitle = mw.title.getCurrentTitle()
local testcasecolon = ''
local testcases = (currtitle.subpageText == 'testcases')
if    testcases then testcasecolon = ':' end
local navborder = true
local followRs = true
local listall = false
local listofalllinks = {}
local skipgaps = false
local misctrackingcats = {
	'', -- [1] placeholder for [[Category:Navseasoncats using cat parameter]]'
	'', -- [2] placeholder for [[Category:Navseasoncats using testcase parameter]]'
	'', -- [3] placeholder for [[Category:Navseasoncats range not using en dash]]
	'', -- [4] placeholder for [[Category:Navseasoncats range abbreviated]]
	'', -- [5] placeholder for [[Category:Navseasoncats range redirected (base change)]]
	'', -- [6] placeholder for [[Category:Navseasoncats range redirected (MOS)]]
	'', -- [7] placeholder for [[Category:Navseasoncats isolated]]
	'', -- [8] placeholder for [[Category:Navseasoncats default season gap size]]
	'', -- [9] placeholder for [[Category:Navseasoncats decade redirected]]
	'', --[10] placeholder for [[Category:Navseasoncats year redirected]]
	'', --[11] placeholder for [[Category:Navseasoncats roman numeral redirected]]
	'', --[12] placeholder for [[Category:Navseasoncats nordinal redirected]]
	'', --[13] placeholder for [[Category:Navseasoncats wordinal redirected]]
	'', --[14] placeholder for [[Category:Navseasoncats TV season redirected]]
	'', --[15] placeholder for [[Category:Navseasoncats using skip-gaps parameter]]
}
local avoidself =  (currtitle.text ~= 'Navseasoncats' and          --avoid self
					currtitle.text ~= 'Navseasoncats/توضیحات' and      --avoid self
					currtitle.text ~= 'Navseasoncats/تمرین' and  --avoid self
					(currtitle.nsText ~= 'الگو' or testcases)) --avoid nested transclusion errors (i.e. {{Infilmdecade}})


--[[==========================================================================]]
--[[                      Utility & category functions                        ]]
--[[==========================================================================]]

--Error message handling
--Also used by {{Navseasoncats with centuries below decade}}.
function p.errorclass( msg )
	return mw.text.tag( 'span', {class='error mw-ext-cite-error'}, '<b>خطا!</b> '..string.gsub(msg, '&#', '&amp;#') )
end

--Failure handling
--Also used by {{Navseasoncats with centuries below decade}}.
function p.failedcat( errors, sortkey )
	if avoidself then
		return (errors or '')..'&#42;&#42;&#42;Navseasoncats ناتوان در ایجاد جعبه ناوبری***'..
			   '[['..testcasecolon..'বিষয়শ্রেণী:Navseasoncats failed to generate navbox|'..(sortkey or 'O')..']]'
	end
	return ''
end

--Check for nav_*() navigational isolation (not necessarily an error).
--Used by all nav_*().
function isolatedcat()
	if nexistingcats == 0 and avoidself then
		misctrackingcats[7] = '[['..testcasecolon..'বিষয়শ্রেণী:Navseasoncats isolated]]'
	end
end

--
--Now unused, in favor of catlinkfollowr().
--
--Similar to {{LinkCatIfExists2}}: make a piped link to a category, if it exists;
--if it doesn't exist, just display the greyed link title without linking.
function catlink( catname, displaytext )
	catname = num_con("bn", mw.text.trim(catname or ''))
	displaytext = num_con("bn", mw.text.trim(displaytext or ''))
	local grey = '#888'
	local disp = catname
	if displaytext ~= '' then --use 'displaytext' parameter if present
		disp = mw.ustring.gsub(displaytext, '%s+%(.+$', ''); --strip any trailing disambiguator
	end
	
	local exists = mw.title.new( catname, 'বিষয়শ্রেণী' ).exists
	if exists then
		nexistingcats = nexistingcats + 1
		return '[[:বিষয়শ্রেণী:'..catname..'|'..disp..']]'
	else
		return '<span style="color:'..grey..'">'..disp..'</span>'
	end
end

--Similar to catlink() but follows {{Category redirect}}s.
--Returns { <#R target navelement>, <basetext of #R target> } if {{Category redirect}} followed;
--returns { <original navelement>, nil } otherwise.
--Used by all nav_*().
function catlinkfollowr( catname, displaytext )
	catname = num_con("bn", mw.text.trim(catname or ''))
	displaytext = num_con("bn", mw.text.trim(displaytext or ''))
	local grey = '#305'
	local disp = catname
	if displaytext ~= '' then --use 'displaytext' parameter if present
		disp = mw.ustring.gsub(displaytext, '%s+%(.+$', ''); --strip any trailing disambiguator
	end
	
	local link, nilorR
	local exists = mw.title.new( catname, 'বিষয়শ্রেণী' ).exists
	if exists then
		nexistingcats = nexistingcats + 1
		if followRs then
			local R = rtarget(catname)
			if R == catname then --no #R
				link = '[[:বিষয়শ্রেণী:'..catname..'|'..disp..']]'
				nilorR = nil
			else --#R followed
				link = '[[:বিষয়শ্রেণী:'..R..'|'..disp..']]'
				nilorR = R
			end
		else
			link = '[[:বিষয়শ্রেণী:'..catname..'|'..disp..']]'
			nilorR = nil
		end
	else
		link = '<span style="color:'..grey..'">'..disp..'</span>'
		nilorR = nil
		--return { '[[:বিষয়শ্রেণী:'..catname..'|'..disp..']]', nil } --for debugging
	end
	
	if listall then
		if nilorR then --#R followed
			table.insert( listofalllinks, '[[:বিষয়শ্রেণী:'..catname..']] → '..'[[:বিষয়শ্রেণী:'..nilorR..']] ('..link..')' )
		else --no #R
			table.insert( listofalllinks, '[[:বিষয়শ্রেণী:'..catname..']] ('..link..')' )
		end
	end
	
	return { link, nilorR }
end

--Returns the target of {{Category redirect}}, if it exists, else returns the original cat.
--Used by catlinkfollowr(), and so indirectly by all nav_*().
function rtarget( cat )
	local catcontent = mw.title.new( cat or '', 'বিষয়শ্রেণী' ):getContent()
	if string.match( catcontent or '', '*বিষয়শ্রেণী }}' ) then
		local regex = {
			--the following 11 pages (7 condensed) redirect to [[Template:Category redirect]] (as of 6/2019):
			{ '1', '{{ *[Cc]ategory *[Rr]edirect' }, --most likely match 1st
			{ '2', '{{ *বিষয়শ্রেণী *بهتر' },         --444+240 transclusions
			{ '3', '{{ *تغییر *مسیر *বিষয়শ্রেণী' },            --8+3
			{ '4', '{{ *تغییرمسیر *বিষয়শ্রেণী' },        --6
			{ '5', '{{ *تغییرمسیرবিষয়শ্রেণী' },              --6
			{ '6', '{{ *[Cc]atr' },                  --4
			{ '7', '{{ *[Cc]at *move' },             --0
		}
		for k, v in pairs (regex) do
			local rtarget = mw.ustring.match( catcontent, v[2]..'%s*|%s*([^|}]+)' )
			if rtarget then
				rtarget = mw.ustring.gsub(rtarget, '^1%s*=%s*', '')
				rtarget = string.gsub(rtarget, '^বিষয়শ্রেণী:', '')
				return rtarget
			end
		end
	end
	return cat
end

--Returns a numbered list of all {{Category redirect}}s followed by catlinkfollowr() -> rtarget().
--Used by all nav_*().
function listalllinks()
	local nl = '\n# '
	local out = ''
	if currtitle.nsText == 'বিষয়শ্রেণী' then
		errors = p.errorclass('پارامتر <b><code>|list-followed-redirects=yes</code></b> '..
							'نباید در فضای نام বিষয়শ্রেণী ذخیره شود، تنها باید پیش‌نمایانده شود.')
		out = p.failedcat(errors, 'Z')
	end
	if listofalllinks[1] then
		return out..nl..table.concat(listofalllinks, nl)
	else
		return out..nl..'پیوندی یافت نشد؟!'
	end
end

--Returns an unsigned string of the 1-4 digit decade ending in "0", else error.
--Used by nav_decade() only.
function sterilizedec( decade )
	if decade == nil or decade == '' then
		return nil
	end
	
	local dec = mw.ustring.match(decade, '^[-%+]?(%d?%d?%d?۰)$') or 
				mw.ustring.match(decade, '^[-%+]?(%d?%d?%d?۰)%D')
	if dec then
		return dec
	else
		--fix 2-4 digit decade
		local decade_fixed234 = mw.ustring.match(decade, '^[-%+]?(%d%d?%d?)%d$') or
								mw.ustring.match(decade, '^[-%+]?(%d%d?%d?)%d%D')
		if decade_fixed234 then
			return decade_fixed234..'۰'
		end
		
		--fix 1-digit decade
		local decade_fixed1   = mw.ustring.match(decade, '^[-%+]?(%d)$') or
								mw.ustring.match(decade, '^[-%+]?(%d)%D')
		if decade_fixed1 then
			return '۰'
		end
		
		--unfixable
		errors = 'sterilizedec() error'
		return nil
	end
end

--Check for nav_hyphen default gap size + isolatedcat() (not necessarily an error).
--Used by nav_hyphen() only.
function defaultgapcat( bool )
	if bool and nexistingcats == 0 and avoidself then
		--using "nexistingcats > 0" isn't as useful, since the default gap size obviously worked
		misctrackingcats[8] = '[['..testcasecolon..'বিষয়শ্রেণী:Navseasoncats default season gap size]]'
	end
end

--12 -> 12th, etc.
--Used by nav_nordinal(), nav_wordinal(), and {{Navseasoncats with centuries below decade}}.
function p.addord( i )
	if tonumber(i) then
		local s = tostring(i)
		
		local tens = mw.ustring.match(s, '۱%d$')
		if    tens then return s..'' end
		
		local  ones = mw.ustring.match(s, '%d$')
		if     ones == '۱' then return s..''
		elseif ones == '۲' then return s..''
		elseif ones == '۳' then return s..'' end
		
		return s..''
	end
	return i
end



--[[==========================={{  nav_decade  }}=============================]]

function nav_decade( firstpart, decade, lastpart)
	--Expects a PAGENAME of the form "Some sequential 2000 example cat", where 
	--	firstpart = Some sequential
	--	decade    = 2000
	--	lastpart  = example cat
	--	mindecade = 1800 ('min' decade parameter; optional)
	--	maxdecade = 2020 ('max' decade parameter; optional; defaults to next decade)
	
	--sterilize dec
	-- local dec = sterilizedec(decade)
	if errors ~= '' then
		errors = p.errorclass('عملگر nav_decade «'..(decade or '')..'» را به‌عنوان پارامتر دومش دریافت کবিষয়শ্রেণী، '..
							'اما انتظار دارد که ورودی یک سال چهار رقمی باشد که با «۰» پایان می‌یابد".')
		return p.failedcat(errors, 'D')
	end
	
	local ndec = tonumber(num_con("en", decade))
	local ncen = ndec
	
	--begin navdecade
	local bnb = '' --border/no border
	if navborder == false then --for embedding in ...
		bnb = ' border-style: none; background-color: transparent;'
	end
	
	local navd = '{| class="toccolours hlist" style="text-align: center; margin: auto;'..bnb..'"\n'..'|\n'
	
	local i = -50
	while i <= 50 do
		local d = ndec + i
	
			--determine target cat
			local disp = num_con("bn", d)
			local catlink = catlinkfollowr(firstpart..' '..num_con("bn", d)..' '..lastpart, disp)
			if catlink[2] and avoidself then --a {{Category redirect}} was followed
				misctrackingcats[9] = '[['..testcasecolon..'বিষয়শ্রেণী:Navseasoncats decade redirected]]'
			end
		
			--populate left/right navd
			local shown = '*'..catlink[1]..'\n'
			local hidden = '*'..disp..'\n'
			local exists = mw.title.new(firstpart..' '..num_con("bn", d)..' '..lastpart, 'বিষয়শ্রেণী').exists
		
			if exists then
				navd = navd..shown
			else
				navd = navd..hidden
			end
		
			if listall and hidden then
				listofalllinks[#listofalllinks] = listofalllinks[#listofalllinks]..' ('..hidden..')'
			end
	
		i = i + 10
	end
	
	isolatedcat()
	if listall then
		return listalllinks()
	else
		return navd..'|}'
	end
end

--[[==========================={{  nav_century  }}=============================]]

function nav_century( firstpart, century, lastpart)
	
	local ncen = tonumber(num_con("en", century))
	
	--begin navcentury
	local bnb = '' --border/no border
	if navborder == false then --for embedding in ...
		bnb = ' border-style: none; background-color: transparent;'
	end
	
	local navc = '{| class="toccolours hlist" style="text-align: center; margin: auto;'..bnb..'"\n'..'|\n'

	local i = -5
	while i <= 5 do
		local d = ncen + i

		--determine target cat
		local disp = num_con("bn", d)
		local catlink = catlinkfollowr(firstpart..' '..num_con("bn", d)..' '..lastpart, disp)
		if catlink[2] and avoidself then --a {{Category redirect}} was followed
			misctrackingcats[9] = '[['..testcasecolon..'বিষয়শ্রেণী:Navseasoncats decade redirected]]'
		end
	
		--populate left/right navd
		local shown = '*'..catlink[1]..'\n'
		local hidden = '*'..disp..'\n'
		local exists = mw.title.new(firstpart..' '..num_con("bn", d)..' '..lastpart, 'বিষয়শ্রেণী').exists
	
		if exists then
			navc = navc..shown
		else
			navc = navc..hidden
		end
	
		if listall and hidden then
			listofalllinks[#listofalllinks] = listofalllinks[#listofalllinks]..' ('..hidden..')'
		end

		i = i + 1
	end
	
	isolatedcat()
	if listall then
		return listalllinks()
	else
		return navc..'|}'
	end
end

--[[============================{{  nav_year  }}==============================]]

function nav_year(firstpart, year, lastpart)
	--Expects a PAGENAME of the form "Some sequential 1760 example cat", where 
	--	firstpart   = Some sequential
	--	year        = 1760
	--	lastpart    = example cat
	--	minimumyear = 1758 ('min' year parameter; optional)
	--	maximumyear = 1800 ('max' year parameter; optional)
	
	if year == nil then
		errors = p.errorclass('Function nav_year can\'t recognize the year sent to its 2nd parameter.')
		return p.failedcat(errors, 'Y')
	end
	
	local nyear = tonumber(num_con("en", year))
	
	--begin navyear
	local bnb = '' --border/no border
	if navborder == false then --for embedding in ...
		bnb = ' border-style: none; background-color: transparent;'
	end
	
	local navy = '{| class="toccolours hlist" style="text-align: center; margin: auto;'..bnb..'"\n'..'|\n'
	
	local i = -5
	while i <= 5 do
		local d = nyear + i
	
		--determine target cat
		local disp = num_con("bn", d)
		local catlink = catlinkfollowr(firstpart..' '..num_con("bn", d)..' '..lastpart, disp)
		if catlink[2] and avoidself then --a {{Category redirect}} was followed
			misctrackingcats[9] = '[['..testcasecolon..'বিষয়শ্রেণী:Navseasoncats decade redirected]]'
		end
	
		--populate left/right navd
		local shown = '*'..catlink[1]..'\n'
		local hidden = '*'..disp..'\n'
		local exists = mw.title.new(firstpart..' '..num_con("bn", d)..' '..lastpart, 'বিষয়শ্রেণী').exists
	
		if exists then
			navy = navy..shown
		else
			navy = navy..hidden
		end
	
		if listall and hidden then
			listofalllinks[#listofalllinks] = listofalllinks[#listofalllinks]..' ('..hidden..')'
		end
	
		i = i + 1
	end

	isolatedcat()
	if listall then
		return listalllinks()
	else
		return navy..'|}'
	end
end


--[[==========================={{  nav_hyphen  }}=============================]]

function nav_hyphen( start, hyph, finish, firstpart, lastpart, minseas, maxseas, testgap )
	--Expects a PAGENAME of the form "Some sequential 2015–16 example cat", where 
	--	start     = 2015
	--	hyph      = –
	--	finish    = 16 (sequential years can be abbreviated, but others should be full year, e.g. "2001–2005")
	--	firstpart = Some sequential
	--	lastpart  = example cat
	--	minseas   = 1800 ('min' starting season shown; optional)
	--	maxseas   = 2000 ('max' starting season shown; optional; 2000 will show 2000-01)
	--	testgap   = 0 (testcasegap parameter for easier testing; optional)
	
	--sterilize start
	if mw.ustring.match(start or '', '^%d%d?%d?%d?$') == nil then --1-4 digits, AD only
		local start_fixed = mw.ustring.match(start or '', '^%s*(%d%d?%d?%d?)%D')
		if start_fixed then
			start = num_con("en", start_fixed)
		else
			errors = p.errorclass('Function nav_hyphen can\'t recognize the number "'..(start or '')..'" '..
								  'in the first part of the "season" that was passed to it. '..
								  'For e.g. "2015–16", "2015" is expected via "|2015|–|16|".')
			return p.failedcat(errors, 'H')
		end
	end
	local nstart = tonumber(num_con("en", start))
	
	--en dash check
	if hyph ~= '-' then
		misctrackingcats[3] = '[['..testcasecolon..'বিষয়শ্রেণী:بازه‌های Navseasoncats که از دش فارسی استفاده نمی‌کنند]]' --nav still processable, but track
	end
	
	--sterilize finish
	local regularparent = true
	if finish == 0 then
		regularparent = false --"Members of the Scottish Parliament 2021–"
		if maxseas == nil or maxseas == '' then
			maxseas = start --hide subsequent ranges
		end
	end
	if mw.ustring.match(finish or '', '^%d+$') == nil then
		local finish_fixed = mw.ustring.match(finish or '', '^%s*(%d%d?%d?%d?)%D')
		if finish_fixed then
			finish = num_con("en", finish_fixed)
		else
			errors = p.errorclass('Function nav_hyphen can\'t recognize "'..(finish or '')..'" '..
								  'in the second part of the "season" that was passed to it. '..
								  'For e.g. "2015–16", "16" is expected via "|2015|–|16|".')
			return p.failedcat(errors, 'I')
		end
	else
		if string.len(num_con("en", finish)) >= 5 then
			errors = p.errorclass('The second part of the season passed to function nav_hyphen should only be four or fewer digits, not "'..(finish or '')..'". '..
								  'See [[MOS:DATERANGE]] for details.')
			return p.failedcat(errors, 'J')
		end
	end
	local nfinish = tonumber(num_con("en", finish))
	
	--sterilize min/max
	local nminseas = tonumber(minseas) or -9999 --same behavior as nav_year
	local nmaxseas = tonumber(maxseas) or  9999 --same behavior as nav_year
	if nminseas > nstart then nminseas = nstart end
	if nmaxseas < nstart then nmaxseas = nstart end
	
	local lspace = ' ' --assume a leading space (most common)
	local tspace = ' ' --assume a trailing space (most common)
	if mw.ustring.match(firstpart, '%($') then lspace = '' end --DNE for "Madrid city councillors (2007–2011)"-type cats
	if mw.ustring.match(lastpart,  '^%)') then tspace = '' end --DNE for "Madrid city councillors (2007–2011)"-type cats
	
	--calculate term length/intRAseason size & finishing year
	local t = 1
	while t <= 10 and regularparent do
		local nish = nstart + t --use switchADBC to flip this sign to work for years BC
		if nish == nfinish or mw.ustring.match(nish, '%d?%d$') == num_con("en", finish) then
			break
		end
		if t == 10 then
			local x = mw.ustring.match(nish, '%d?%d$')
			errors = p.errorclass('Function nav_hyphen can\'t determine a reasonable term length for "'
				     ..start..hyph..finish..'****'..'nish='..nish..'+'..'nstart='..nstart..'+'..'nfinish='..nfinish
				     ..' +x='..x..'+start='..start..'finish='..finish..'".')
			return p.failedcat(errors, 'K')
		end
		t = t + 1
	end
	
	--apply MOS:DATERANGE to parent
	local lenstart = string.len(num_con("en", start))
	local lenfinish = string.len(num_con("en", finish))
	if lenstart == 4 and regularparent then --"2001–..."
		if t == 1 then --"2001–02" & "2001–2002" both allowed
			if lenfinish ~= 2 and lenfinish ~= 4 then
				errors = p.errorclass('The second part of the season passed to function nav_hyphen should be two or four digits, not "'..finish..'".')
				return p.failedcat(errors, 'L')
			end
		else --"2001–2005" is required for t > 1; track "2001–05"; anything else = error
			if lenfinish == 2 then
				if avoidself then
					misctrackingcats[4] = '[['..testcasecolon..'Category:Navseasoncats range abbreviated]]'
				end
			elseif lenfinish ~= 4 then
				errors = p.errorclass('The second part of the season passed to function nav_hyphen should be four digits, not "'..finish..'".')
				return p.failedcat(errors, 'M')
			end
		end
		if finish == '00' and avoidself then --full year required regardless of term length
			misctrackingcats[4] = '[['..testcasecolon..'Category:Navseasoncats range abbreviated]]'
		end
	end
	
	--calculate intERseason gap size
	local hgap_default = 0 --assume & start at the most common case: 2001–02 -> 2002–03, etc.
	local hgap_limit = 5
	local hgap_success = false
	local hgap = hgap_default
	while hgap <= hgap_limit and regularparent do --verify
		local prevseason2 = firstpart..lspace..(nstart-t-hgap)..hyph..mw.ustring.match(nstart-hgap, '%d?%d$')    ..tspace..lastpart
		local nextseason2 = firstpart..lspace..(nstart+t+hgap)..hyph..mw.ustring.match(nstart+2*t+hgap, '%d?%d$')..tspace..lastpart
		local prevseason4 = firstpart..lspace..(nstart-t-hgap)..hyph..(nstart-hgap)    ..tspace..lastpart
		local nextseason4 = firstpart..lspace..(nstart+t+hgap)..hyph..(nstart+2*t+hgap)..tspace..lastpart
		if t == 1 then --test abbreviated range first, then full range
			if mw.title.new(num_con("bn", prevseason2), 'বিষয়শ্রেণী').exists or --use 'or', in case we're at the edge of the cat structure,
			   mw.title.new(num_con("bn", nextseason2), 'বিষয়শ্রেণী').exists or --or we hit a "–00"/"–2000" situation on one side
			   mw.title.new(num_con("bn", prevseason4), 'বিষয়শ্রেণী').exists or
			   mw.title.new(num_con("bn", nextseason4), 'বিষয়শ্রেণী').exists
			then
				hgap_success = true
				break
			end
		elseif t > 1 then --test full range first, then abbreviated range
			if mw.title.new(num_con("bn", prevseason4), 'বিষয়শ্রেণী').exists or --use 'or', in case we're at the edge of the cat structure,
			   mw.title.new(num_con("bn", nextseason4), 'বিষয়শ্রেণী').exists or --or we hit a "–00"/"–2000" situation on one side
			   mw.title.new(num_con("bn", prevseason2), 'বিষয়শ্রেণী').exists or 
			   mw.title.new(num_con("bn", nextseason2), 'বিষয়শ্রেণী').exists
			then
				hgap_success = true
				break
			end
		end
		hgap = hgap + 1
	end
	if hgap_success == false then
		hgap = tonumber(testgap) or hgap_default --tracked via defaultgapcat()
	end
	
	--preliminary scan to determine ir/regular spacing of nearby hgap=0 cats
	--to limit expensive function usage, MOS:DATERANGE alternation is not used
	--an irregular-term-length series should follow "YYYY..hyph..YYYY" throughout
	local iregs = 0
	local iirregs = 0
	local tirregs = {}
	if hgap <= hgap_limit then --keep this now-unnecessary if-block to isolate temp vars
		--find # of nav-visible regular-term-length cats
		local i = -3
		while i <= 3 do
			local from = nstart + i*(t+hgap)
			local to   = tostring(from+t)
			local full = firstpart..lspace..from..hyph..to..tspace..lastpart
			if i ~= 0 and mw.title.new(num_con("bn", full), 'বিষয়শ্রেণী' ).exists then
				iregs = iregs + 1
			end
			i = i + 1
		end
		
		--find # of nav-visible irregular-term-length cats
		local bwanchor = nstart       --backwards anchor/common year
		local fwanchor = bwanchor + t --forwards anchor/common year
		local endfound = false
		local j = -3
		while j <= 3 do
			
			if j < 0 then --search backwards
				local k = 1
				while k <= 10 do
					local from = bwanchor - k
					local to   = bwanchor
					local full = firstpart..lspace..from..hyph..to..tspace..lastpart
					if mw.title.new(num_con("bn", full), 'বিষয়শ্রেণী' ).exists then
						iirregs = iirregs + 1
						tirregs['از-'..iirregs] = from
						tirregs['تا-'..iirregs] = to
						bwanchor = from --ratchet down
						break
					end
					k = k + 1
				end
			end
			
			if j > 0 and endfound == false then --search forwards
				local k = 0
				while k <= 10 do
					local from = fwanchor
					local to   = fwanchor + k
					if k == 0 then to = '' end --see if end-cat exists
					local full = firstpart..lspace..from..hyph..to..tspace..lastpart
					if mw.title.new(num_con("bn", full), 'বিষয়শ্রেণী' ).exists then
						tirregs['از'..j] = from
						tirregs['تا'..j] = to
						iirregs = iirregs + 1
						if k == 0 then
							endfound = true --tentative
						else
							endfound = false
							fwanchor = to --ratchet up
							break --only break on k > 0; old end-cat #Rs still exist like "Members of the Scottish Parliament 2011–"
						end
					end
					k = k + 1
				end
			end
			
			j = j + 1
		end
		
		--rm irreg table if not useful
		if iregs >= iirregs then
			tirregs = {}
		elseif avoidself then
			misctrackingcats[7] = '[['..testcasecolon..'Category:Navseasoncats range irregular, no gaps]]'
		end
	end
	
	--begin navhyphen
	local navh = '{| class="toccolours hlist" style="text-align: center; margin: auto;"\n'..'|\n'
	
	local terminalcat = false
	local i = -3
	while i <= 3 do
		local from  = nstart + i*(t+hgap)
		if tirregs['from'..i] then from = tonumber(tirregs['from'..i]) end
		local from2 = mw.ustring.match(from, '%d?%d$')
		
		local to  = tostring(from+t)
		if tirregs['to'..i] then
			to = tirregs['to'..i]
		elseif regularparent == false and tirregs and i > 0 then
			to = tirregs['to-1'] --special treatment for parent terminal cats, since they have no natural 'to'
		end
		local to2 = mw.ustring.match(to, '%d?%d$')
		local tofinal = (to2 or '')    --assume t=1 and abbreviated 'to' (the most common case)
		if t > 1 or                    --per MOS:DATERANGE (e.g. 1999-2004)
		  (from2 - (to2 or from2)) > 0 --century transition exception (e.g. 1999–2000)
		then
			tofinal = (to or '')       --default to the MOS-correct format, in case no fallbacks found
		end
		
		--check existance of 4-digit, MOS-correct range, with abbreviation fallback
		if t > 1 and string.len(from) == 4 then --e.g. 1999-2004
			--determine which link exists (full or abbr)
			local full = firstpart..lspace..from..hyph..tofinal..tspace..lastpart
			if not mw.title.new(num_con("bn", full), 'বিষয়শ্রেণী' ).exists then
				local abbr = firstpart..lspace..from..hyph..to2..tspace..lastpart
				if mw.title.new(num_con("bn", abbr), 'বিষয়শ্রেণী' ).exists then
					tofinal = (to2 or '') --rv to MOS-incorrect format; if full AND abbr DNE, then tofinal is still in its MOS-correct format
				end
			end
		elseif t == 1 then --full-year consecutive ranges are also allowed
			local abbr = firstpart..lspace..from..hyph..tofinal..tspace..lastpart --assume tofinal is in abbr format
			if not mw.title.new(num_con("bn", abbr), 'বিষয়শ্রেণী' ).exists and tofinal ~= to then
				local full = firstpart..lspace..from..hyph..to..tspace..lastpart
				if mw.title.new(num_con("bn", full), 'বিষয়শ্রেণী' ).exists then
					tofinal = (to or '') --if abbr AND full DNE, then tofinal is still in its abbr format (unless it's a century transition)
				end
			end
		end
		
		--populate navh
		if i ~= 0 then --left/right navh
			local orig = firstpart..lspace..from..hyph..tofinal..tspace..lastpart
			local disp = from..hyph..tofinal
			local catlink = catlinkfollowr(orig, disp, true) --force terminal cat display
			
			if terminalcat == false then
				terminalcat = (mw.ustring.match(disp, '%d+[–-]$') or false)
			end
			if catlink[2] and avoidself then --a {{Category redirect}} was followed, figure out why
				local origbase = mw.ustring.gsub(orig,       '%d+[–-]%d+', '')
				local rtarbase = mw.ustring.gsub(catlink[2], '%d+[–-]%d+', '')
				if mw.ustring.match(orig, '%d+[–-]$') then
					origbase = mw.ustring.gsub(orig,       '%d+[–-]$', '')
				end
				if mw.ustring.match(catlink[2], '%d+[–-]$') then
					--finagle/overload terminalcat type to set tracking on 1st occurence only
					if terminalcat == false then terminalcat = 1 end
					rtarbase = mw.ustring.gsub(catlink[2], '%d+[–-]$', '')
				end
				origbase = mw.text.trim(origbase)
				rtarbase = mw.text.trim(rtarbase)
				if avoidself then
					if origbase ~= rtarbase then
						misctrackingcats[5] = '[['..testcasecolon..'Category:Navseasoncats range redirected (base change)]]'
					elseif terminalcat == 1 then
						misctrackingcats[8] = '[['..testcasecolon..'Category:Navseasoncats range redirected (end)]]'
					else
						local all4s = (mw.ustring.match(orig, '%d%d%d%d[–-]%d%d%d%d') and
									   mw.ustring.match(catlink[2], '%d%d%d%d[–-]%d%d%d%d'))
						if all4s then
							misctrackingcats[10] = '[['..testcasecolon..'Category:Navseasoncats range redirected (other)]]'
						else
							misctrackingcats[6] = '[['..testcasecolon..'Category:Navseasoncats range redirected (MOS)]]'
						end
					end
				end
			end
			
			if terminalcat then
				if type(terminalcat) ~= 'boolean' then nmaxseas = from end --only want to do this once
				if type(terminalcat) == 'string' and catlink[2] == nil and avoidself then
					misctrackingcats[9] = '[['..testcasecolon..'Category:Navseasoncats range unredirected (end)]]'
				end
				terminalcat = true --done finagling/overloading
			end
			if from >= 0 and nminseas <= from and from <= nmaxseas then
				navh = navh..'*'..catlink[1]..'\n'
				--in case irreg froms outpace reg froms, and are visible/grey after terminalcat instead of hidden
				if terminalcat then nmaxseas = nmaxseas - 50 end
			else
				local hidden = '<span style="visibility:hidden">'..disp..'</span>'
				navh = navh..'*'..hidden..'\n'
				if listall then
					listofalllinks[#listofalllinks] = listofalllinks[#listofalllinks]..' ('..hidden..')'
				end
			end
		else --center navh
			if finish == 0 then finish = '<span style="visibility:hidden">'..start..'</span>' end
			navh = navh..'*<b>'..start..hyph..finish..'</b>\n'
		end
		
		i = i + 1
	end
	
	isolatedcat()
	defaultgapcat(not hgap_success)
	if listall then
		return listalllinks()
	else
		return navh..'|}'
	end
end

--[[==========================={{  find_var  }}===============================]]
function p.find_var( pn )
	--Extracts the variable text (e.g. 2015–16, 3rd, 2000s, III, etc.) from a string
	local pagename = currtitle.baseText
	if pn and pn ~= '' then
		pagename = pn
	end
	
	local cpagename = 'বিষয়শ্রেণী:'..pagename--limited-Lua-regex workaround
	--local enpagename = num_con("en", pagename)
					
	local e_season = mw.ustring.match(cpagename, '%s(%d+[–-])$') --irreg; ending unknown, e.g. "Members of the Scottish Parliament 2021–"
	local season   = mw.ustring.match(cpagename, '[:%s%(](%d+[–-]%d+)[%)%s]') or --split in 2 b/c you can't frontier '$'/eos?
					 mw.ustring.match(cpagename, '[:%s](%d+[–-]%d+)$')
	local decade   = mw.ustring.match(cpagename, '[:%s]دهه%s(%d+)')
	local century  = mw.ustring.match(cpagename, '[:%s]سده%s(%d+)')
	local year     = mw.ustring.match(cpagename, '[:%s](%d%d%d%d)%s') or --prioritize 4-digit years first
					 mw.ustring.match(cpagename, '[:%s](%d%d%d%d)$') or
					 mw.ustring.match(cpagename, '[:%s](%d+)%s') or
					 mw.ustring.match(cpagename, '[:%s](%d+)$') or
					 mw.ustring.match(cpagename, '[:%s].-(%d+).-$')

	local found    = e_season or season or decade or century or year
	
	if found then
		if e_season then return { 'ending',   e_season } end
		if season   then return { 'season',   season   } end
		if decade then return { 'decade',   decade } end
		if century then return { 'century',  century } end
		if year then return { 'year', year } end
	end
	
	errors = p.errorclass('Function find_var can\'t find the variable x text in the category: "'..enpagename..'"')
	return { 'error', p.failedcat(errors, 'V') }
end

--[[==========================================================================]]
--[[                                  Main                                    ]]
--[[==========================================================================]]

function p.navseasoncats( frame )
	local args = frame:getParent().args
	local dby  = args['decade-below-year']    --used by {{Navseasoncats with decades below year}}
	local cbd  = args['century-below-decade'] --used by {{Navseasoncats with centuries below decade}}
	local cat  = args['cat']                  --'testcase' alias for mainspace
	local list = args['list-all-links']       --utility to output all links & followed #Rs instead of a navbar
	local follow = args['follow-redirects']   --default 'yes'
	local testcase    = args['testcase']
	local testcasegap = args['testcasegap']
	local minimum = args['min']
	local maximum = args['max']
	local skip_gaps = args['skip-gaps']
	
	if dby then
		navborder = false
		dby = string.gsub(dby, '&#32;', ' ') --unicodify forced whitespace
	end
	if cbd then
		navborder = false
		cbd = string.gsub(cbd, '&#32;', ' ') --unicodify forced whitespace
	end
	if follow and follow == 'no' then
		followRs = false
	end
	if list and list == 'yes' then
		listall = true
	end
	if skip_gaps and skip_gaps == 'yes' then
		skipgaps = true
		if avoidself then
			misctrackingcats[15] = '[['..testcasecolon..'বিষয়শ্রেণী:Navseasoncats using skip-gaps parameter]]'
		end
	end
	
	if currtitle.nsText == 'Category' then
		if cat      then misctrackingcats[1] = '[['..testcasecolon..'বিষয়শ্রেণী:Navseasoncats using cat parameter]]' end
		if testcase then misctrackingcats[2] = '[['..testcasecolon..'বিষয়শ্রেণী:Navseasoncats using testcase parameter]]' end
	end
	
	local pagename = testcase or cat or dby or cbd or currtitle.baseText
	
	local findvar = p.find_var(pagename)
	if findvar[1] == 'error' then return findvar[2]..table.concat(misctrackingcats) end --basic format error checking in find_var()
	
	local firstpart, lastpart = string.match(pagename, '^(.*)'..findvar[2]..'(.*)$')
	firstpart = mw.text.trim(firstpart or '')
	lastpart  = mw.text.trim(lastpart or '')
	
	local start = mw.ustring.match(findvar[2], '^%d+')
	
	--determine the appropriate nav function
	if findvar[1] == 'season' then       --e.g. "1–4", "1999–2000", "2001–02", "2001–2002", "2005–2010", etc.
		finish = start --در فارسی برعکس
		local hyphen, start = mw.ustring.match(findvar[2], '%d([–-])(%d+)') --ascii 150 & 45 (ndash & keyboard hyphen); mw req'd
		return nav_hyphen( start, hyphen, finish, firstpart, lastpart, minimum, maximum, testcasegap )..table.concat(misctrackingcats)
		
	elseif findvar[1] == 'ending' then   --e.g. "2021–" (irregular; ending unknown)
		finish = start --در فارسی برعکس
		local hyphen, start = mw.ustring.match(findvar[2], '%d([–-])$'), 0 --ascii 150 & 45 (ndash & keyboard hyphen); mw req'd
		return nav_hyphen( start, hyphen, finish, firstpart, lastpart, minimum, maximum, testcasegap )..table.concat(misctrackingcats)
		
	elseif findvar[1] == 'decade' then   --مثلاً دهه 1870 میلادی
		return nav_decade( firstpart, start, lastpart)..table.concat(misctrackingcats)
		
	elseif findvar[1] == 'century' then  --مثلاً سده 18 میلادی
		return nav_century( firstpart, start, lastpart)..table.concat(misctrackingcats)
		
	elseif findvar[1] == 'year' then --مثلاً سال 2005 میلادی
		return nav_year( firstpart, start, lastpart)..table.concat(misctrackingcats)
		
	else                                 --بدشکل
		errors = p.errorclass('Failed to determine the appropriate nav function from malformed season "'..findvar[2]..'". ')
		return p.failedcat(errors, 'N')..table.concat(misctrackingcats)
	end
end

return p