लुआ त्रुटि मॉड्यूल:Documentation में पंक्ति 144 पर: message: type error in message cfg.container (string expected, got nil)।

require( "strict" )
-- Local variables.
local dateModule = require( "Module:Era" )
local tableToolsModule = require( "Module:TableTools" )
local categories = {} -- List of categories to add page to.

local PROP_FAMILY_NAME = 'P734'

--------------------------------------------------------------------------------
-- Get the actual parentheses-enclosed HTML string that shows the dates.
local function getFormattedDates( birthyear, deathyear )
	local dates = ''
	if birthyear ~= '' or deathyear ~= '' then
		dates = dates .. '<br />('
	end
	if birthyear ~= '' then
		dates = dates .. birthyear
	end
	if ( birthyear ~= '' or deathyear ~= '' ) and birthyear ~= deathyear then
		-- Add spaces if there are spaces in either of the dates.
		local spaces = ''
		if string.match( birthyear .. deathyear, ' ' ) then
			spaces = ' '
		end
		dates = dates .. spaces .. '–' .. spaces
	end
	if deathyear ~= '' and birthyear ~= deathyear then
		dates = dates .. deathyear
	end
	if birthyear ~= '' or deathyear ~= '' then
		dates = dates .. ')'
	end
	return dates
end

--------------------------------------------------------------------------------
-- Add a category to the current list of categories. Do not include the Category prefix.
local function addCategory( category )
	for _, cat in pairs( categories ) do
    	if cat == category then
    		-- Already present
			return
    	end
  	end
	table.insert( categories, category )
end

--------------------------------------------------------------------------------
-- Remove a category. Do not include the Category prefix.
local function removeCategory( category )
	for catPos, cat in pairs( categories ) do
    	if cat == category then
    		table.remove( categories, catPos )
    	end
  	end
end

--------------------------------------------------------------------------------
-- Get wikitext for all categories added using addCategory.
local function getCategories()
	table.sort( categories )
	local out = ''
	for _, cat in pairs( categories ) do
		out = out .. '[[श्रेणी:' .. cat .. ']]'
	end
	return out
end

--------------------------------------------------------------------------------
-- Take a statement of a given property and make a human-readable year string
-- out of it, adding the relevant categories as we go.
-- @param table statement The statement.
-- @param string type One of 'birth' or 'death'.
local function getYearStringFromSingleStatement( statement, type )
-- Define here local name for 'birth' and 'death'
	local localtype = ''
	if type == 'birth' then
		localtype = 'जन्म'
	elseif type == 'death' then
		localtype = 'निधन'
	end
	local snak = statement.mainsnak
	-- We're not using mw.wikibase.formatValue because we only want years.

	-- No value. This is invalid for birth dates (should be 'somevalue'
	-- instead), and indicates 'still alive' for death dates.
	if snak.snaktype == 'novalue' and type == 'birth' then
		addCategory( 'अनुपलब्ध जन्म तिथि वाले लेखक' )
		return ''
	end
	if snak.snaktype == 'novalue' and type == 'death' then
		addCategory( 'जीवित लेखक' )
		return ''
	end

	-- Unknown value.
	if snak.snaktype == 'somevalue' then
		addCategory( 'अज्ञात ' .. localtype .. ' तिथि वाले लेखक' )
		return '?'
	end

	-- Extract year from the time value.
	local _,_, extractedYear = string.find( snak.datavalue.value.time, '([%+%-]%d%d%d+)%-' )
	local year = math.abs( tonumber( extractedYear ) )
	addCategory( dateModule.era( extractedYear ) .. ' युग के लेखक' )
	 -- Century & millennium precision.
	if snak.datavalue.value.precision == 6 or snak.datavalue.value.precision == 7 then
		local ceilfactor = 100
		local precisionName = 'सदी'
		if snak.datavalue.value.precision == 6 then
			ceilfactor = 1000
			precisionName = 'सहस्राब्दि'
		end
		local cent = math.max( math.ceil( year / ceilfactor ), 1 )
		-- @TODO: extract this to use something like [[en:wikipedia:Module:Ordinal]]
		local suffix = 'वीं'
		if cent == '1' then
			suffix = 'हली'
		elseif cent == '2' or cent == '3' then
			suffix = 'सरी'
		elseif cent == '4' then
			suffix = 'थी'
		end
		year = cent .. suffix .. ' ' .. precisionName
		addCategory( 'अनुमानित ' .. localtype .. ' तिथि वाले लेखक' )
	end
	if snak.datavalue.value.precision == 8 then -- decade precision
		year = math.floor( tonumber( year ) / 10 ) * 10 .. 's'
		addCategory( 'अनुमानित ' .. localtype .. ' तिथि वाले लेखक' )
	end
	if tonumber( extractedYear ) < 0 then
		year = year .. ' ईसा पूर्व'
	end

	-- Remove from 'Living authors' if that's not possible.
	if tonumber( extractedYear ) < tonumber( os.date( '%Y' ) - 110 ) then
		removeCategory( 'जीवित लेखक' )
	end

	-- Add to e.g. 'YYYY births' category (before we add 'c.' or 'fl.' prefixes).

	if type == 'birth' or type == 'death' then
		addCategory( year .. ' में ' .. localtype)
	end

	-- Extract circa (P1480 = sourcing circumstances, Q5727902 = circa)
	if statement.qualifiers ~= nil and statement.qualifiers.P1480 ~= nil then
		for _,qualifier in pairs(statement.qualifiers.P1480) do
			if qualifier.datavalue ~= nil and qualifier.datavalue.value.id == 'Q5727902' then
				addCategory( 'अनुमानित ' .. localtype .. ' तिथि वाले लेखक' )
				year = 'लग. ' .. year
			end
		end
	end

	-- Add floruit abbreviation.
	if type == 'floruit' then
		year = 'सक्रि. ' .. year
	end

	return year
end
--------------------------------------------------------------------------------
-- Get a given or family name property. This concatenates (with spaces) all
-- statements of the given property in order of the series ordinal (P1545)
-- qualifier. @TODO fix this.
local function getNameFromWikidata( item, property )
	local statements = item:getBestStatements( property )
	local out = {}
	if statements[1] ~= nil and statements[1].mainsnak.datavalue ~= nil then
		local itemId = statements[1].mainsnak.datavalue.value.id
		table.insert( out, mw.wikibase.label( itemId ) or '' )
	end
	return table.concat( out, ' ' )
end

--------------------------------------------------------------------------------
local function getPropertyValue( item, property )
	local statements = item:getBestStatements( property )
	if statements[1] ~= nil and statements[1].mainsnak.datavalue ~= nil then
		return statements[1].mainsnak.datavalue.value
	end	
end

--------------------------------------------------------------------------------
-- The 'Wikisource' format for a birth or death year is as follows:
--     "?" or empty for unknown (or still alive)
--     Use BCE for years before year 1
--     Approximate dates:
--         Decades or centuries: "1930s" or "20th century"
--         Circa: "c/1930" or "c. 1930" or "ca 1930" or "circa 1930"
--         Tenuous year: "1932/?"
--         Choice of two or more years: "1932/1933"
-- This is a slightly overly-complicated function, but one day will be able to be deleted.
-- @param string type Either 'birth' or 'death'
-- @return string The year to display
local function formatWikisourceYear( year, type )
	if year == nil or year == '' then
		return ''
	end
	local yearParts = mw.text.split( year, '/', true )
	-- Ends in a question mark.
	if yearParts[2] == '?' then
		addCategory( 'अज्ञात ' .. localtype .. ' तिथि वाले लेखक' )
		if tonumber( yearParts[1] ) == nil then
			addCategory( 'गैर-संख्यात्मक ' .. localtype .. ' तिथि वाले लेखक' )
		else
			addCategory( dateModule.era( yearParts[1] ) .. ' लेखक' )
			addCategory( yearParts[1] .. ' ' .. localtype .. ' के दशक के' )
		end
		return yearParts[1] .. '?'
	end
	-- Starts with one of the 'circa' abbreviations
	local circaNames = { 'c', 'c.', 'ca', 'ca.', 'circa', 'लगभग', 'ल.', 'लग.' }
	for _, circaName in pairs( circaNames ) do
		if yearParts[1] == circaName then
			addCategory( 'अनुमानित ' .. localtype .. ' तिथि वाले लेखक' )
			local out = 'लग. ' .. yearParts[2]
			if tonumber( yearParts[2] ) == nil then
				addCategory( 'गैर-संख्यात्मक ' .. localtype .. ' तिथि वाले लेखक' )
			else
				addCategory( dateModule.era( yearParts[2] ) .. ' युग के लेखक' )
				addCategory( yearParts[2] .. ' में ' .. localtype )
			end
			return out
		end
	end
	-- If there is more than one year part, and they're all numbers, add categories.
	local allPartsAreNumeric = true
	if #yearParts > 1 then
		for _, yearPart in pairs( yearParts ) do
			if tonumber( yearPart ) ~= nil then
				addCategory( yearPart .. ' में ' .. localtype )
				addCategory( dateModule.era( yearPart ) .. ' युग के लेखक' )
			else
				allPartsAreNumeric = false
			end
		end
		if allPartsAreNumeric then
			addCategory( 'अनुमानित जन्म तिथि वाले लेखक' )
		end
	end
	-- Otherwise, just use whatever's been given
	if #yearParts == 1 and tonumber( year ) == nil then
		addCategory( 'गैर-संख्यात्मक ' .. localtype .. ' तिथि वाले लेखक' )
	end
	if #yearParts == 1 or allPartsAreNumeric == false then
		addCategory( year .. ' में ' .. localtype )
	end
	return year
end

--------------------------------------------------------------------------------
-- Get a formatted year of the given property and add to the relevant categories.
--   P569   date of birth
--   P570   date of death
--   P1317  floruit
local function formatWikidataYear( item, property )
	-- Check sanity of inputs.
	if item == nil or string.sub( property, 1, 1 ) ~= 'P' then
		return ''
	end
	local type = 'birth'
	if property == 'P570' then
		type = 'death'
	end
	-- Get this property's statements.
	local statements = item:getBestStatements( property )
	if #statements == 0 then
		-- If there are no statements of this type, add to 'missing' category.
		local localtype = ''
			if type == 'birth' then
				localtype = 'जन्म'
			elseif type == 'death' then
				localtype = 'निधन'
			end
		if type == 'birth' or type == 'death' then
			addCategory( 'अनुपलब्ध ' .. localtype .. ' तिथि वाले लेखक' )
		end
		local isHuman = item:formatPropertyValues( 'P31' ).value == 'human'
		if type == 'death' and isHuman then
			-- If no statements about death, assume to be alive.
			addCategory( 'जीवित लेखक' )
		end
	end

	-- Compile a list of years, one from each statement.
	local years = {}
	for _, statement in pairs( statements ) do
		local year = getYearStringFromSingleStatement( statement, type )
		table.insert( years, year )
	end
	years = tableToolsModule.removeDuplicates( tableToolsModule.compressSparseArray( years ) )

	-- If no year found yet, try for a floruit date.
	if #years == 0 or table.concat( years, '/' ) == '?' then
		local floruitStatements = item:getBestStatements( 'P1317' )
		for _, statement in pairs( floruitStatements ) do
			-- If all we've got so far is 'unknown', replace it.
			if table.concat( years, '/' ) == '?' then
				years = {}
			end
			addCategory( 'सक्रियता तिथियों वाले लेखक' )
			local year = getYearStringFromSingleStatement( statement, 'floruit' )
			table.insert( years, year )
		end
	end
	years = tableToolsModule.removeDuplicates( tableToolsModule.compressSparseArray( years ) )

	-- table.sort( years );
	return table.concat( years, '/' )
end

--[=[
Get categories for nationality, occupations, etc.

Returns a list of category names
]=]
local function categoriesList( entity )
	-- bail out on junk inputs
	if entity == nil or entity == "" then
		return {}	
	end

	local DATA = mw.loadData( 'Module:Author/data' )

	local function addCategoriesFromClaims( item, cats, pId, knownCategories )
		-- the item is completely empty (?), maybe this could happen
		if not item.claims then
			return
		end

		-- should never happen, but makes development easier as it won't break as
		-- easily if the /data module is out of sync
		if not knownCategories then
			return
		end

		local statements = item.claims[ pId ]

		-- there aren't any
		if not statements then
			return
		end

		-- for each statement in the item
		for _, v in pairs( statements ) do
			-- Sometimes the property exists on item but has no value, or it has an
			-- unknown value, so in the output from mw.wikibase.getEntity()
			-- .mainsnak's .datavalue will be nil.		
			if v.mainsnak.snaktype == "value" then
				local valueId = v.mainsnak.datavalue.value.id
				-- add the category if we know it
				local knownCat = knownCategories[ valueId ]
				if knownCat then
					table.insert( cats, knownCat )
				end
			end
		end
	end

	local cats = {}
	
	-- add known categories from certain properties
	addCategoriesFromClaims( entity, cats, 'P6886', DATA.categories.languages ) -- P6886 is for languages spoken or written
	addCategoriesFromClaims( entity, cats, 'P27', DATA.categories.nationalities )
	addCategoriesFromClaims( entity, cats, 'P106', DATA.categories.occupations )
	addCategoriesFromClaims( entity, cats, 'P140', DATA.categories.religions ) -- note P8929 is listed for deletion
	addCategoriesFromClaims( entity, cats, 'P135', DATA.categories.movements )
	addCategoriesFromClaims( entity, cats, 'P1142', DATA.categories.ideologies )
	addCategoriesFromClaims( entity, cats, 'P108', DATA.categories.employer )
	addCategoriesFromClaims( entity, cats, 'P39', DATA.categories.positionheld )
	addCategoriesFromClaims( entity, cats, 'P166', DATA.categories.awardreceived )
	addCategoriesFromClaims( entity, cats, 'P463', DATA.categories.memberof )
	addCategoriesFromClaims( entity, cats, 'P411', DATA.categories.canonizationstatus )
	addCategoriesFromClaims( entity, cats, 'P3919', DATA.categories.contributedto )
	addCategoriesFromClaims( entity, cats, 'P3716', DATA.categories.socialclassification )
	addCategoriesFromClaims( entity, cats, 'P1303', DATA.categories.instrument )
	
	return cats
end

--------------------------------------------------------------------------------
-- Get a single formatted date, with no categories.
-- args.year, args.type, args.wikidata_id
local function date( args )
	if args.type == nil or args.type == '' then
		args.type = 'birth'
	end
	if args.year == nil or args.year == '' then
		local item = nil
		if args.wikidata_id ~= nil and args.wikidata_id ~= '' then
			item = mw.wikibase.getEntity( args.wikidata_id )
		else
			item = mw.wikibase.getEntity()
		end
		local property = 'P570' -- P570 Date of death
		if args.type == 'birth' then
			property = 'P569' -- P569 Date of birth
		end
		return formatWikidataYear( item, property )
	else
		return formatWikisourceYear( args.year, args.type )
	end
end

--------------------------------------------------------------------------------
-- Get a formatted string of the years that this author lived,
-- and categorise in the appropriate categories.
-- The returned string starts with a line break (<br />).
local function dates( args )
	local item = mw.wikibase.getEntity()
	if args.wikidata_id ~= nil and args.wikidata_id ~= '' then
		-- This check required because getEntity can't copy with empty strings.
		item = mw.wikibase.getEntity( args.wikidata_id )
	end
	local outHtml = mw.html.create()

	--------------------------------------------------------------------------------
	-- Check a given title as having the appropriate dates as a disambiguating suffix.
	local function checkTitleDatesAgainstWikidata( title, wikidata_id )
		-- All disambiguated author pages have parentheses in their titles.
		local titleHasParentheses = string.find( tostring( title ), '%d%)' )
		if titleHasParentheses == nil then
			return
		end

		-- The title should end with years in the same format as is used in the page
		-- header but with a normal hyphen instead of an en dash.
		local birthYear = date( { type = 'birth'; wikidata_id = wikidata_id } )
		local deathYear = date( { type = 'death'; wikidata_id = wikidata_id } )
		local dates = '(' .. birthYear .. '-' .. deathYear .. ')'
		if string.sub( tostring( title ), -string.len( dates ) ) ~= dates then 
			addCategory( 'बिना शीर्षक-तिथि सुमेल वाले लेखक' )
		end
	end

	-- Check disambiguated page titles for accuracy.
	checkTitleDatesAgainstWikidata( args.pagetitle or mw.title.getCurrentTitle(), args.wikidata_id )

	-- Get the dates (do death first, so birth can override categories if required):
	-- Death.
	local wikidataDeathyear = formatWikidataYear( item, 'P570' ) -- P570 Date of death
	local wikisourceDeathyear = formatWikisourceYear( args.deathyear, 'death' )
	if args.deathyear == nil or args.deathyear == '' then
		args.deathyear = wikidataDeathyear
	else
		-- For Wikisource-supplied death dates.
		args.deathyear = wikisourceDeathyear
		addCategory( 'ओवरराइड निधन तिथि वाले लेखक' )
		if item ~= nil and wikisourceDeathyear ~= wikidataDeathyear then
			addCategory( 'विकिडेटा से भिन्न निधन  तिथि वाले लेखक' )
		end
		if tonumber( args.deathyear ) ~= nil then
			addCategory( dateModule.era( args.deathyear ) .. ' युग के लेखक' )
		end
	end
	if args.deathyear == '' and item == nil then
		addCategory( 'अनुपलब्ध निधन तिथि वाले लेखक' )
	end
	-- Birth.
	local wikidataBirthyear = formatWikidataYear( item, 'P569' ) -- P569 Date of birth
	local wikisourceBirthyear = formatWikisourceYear( args.birthyear, 'birth' )
	if args.birthyear == nil or args.birthyear == '' then
		args.birthyear = wikidataBirthyear
	else
		-- For Wikisource-supplied birth dates.
		args.birthyear = wikisourceBirthyear
		addCategory( 'ओवरराइड जन्म तिथि वाले लेखक' )
		if item ~= nil and wikisourceBirthyear ~= wikidataBirthyear then
			addCategory( 'विकिडेटा से भिन्न जन्म तिथि वाले लेखक' )
		end
		if tonumber( args.birthyear ) ~= nil then
			addCategory( dateModule.era( args.birthyear ) .. ' युग के लेखक' )
		end
	end
	if args.birthyear == '' then
		addCategory( 'अनुपलब्ध जन्म तिथि वाले लेखक' )
	end

	-- Put all the output together, including manual override of the dates.
	local dates = ''
	if args.dates ~= nil and args.dates ~= '' then
		-- The parentheses are repeated here and in getFormattedDates()
		addCategory( 'ओवरराइड तिथियों वाले लेखक' )
		dates = '<br />(' .. args.dates .. ')'
	else
		dates = getFormattedDates( args.birthyear, args.deathyear )
	end
	outHtml:wikitext( dates .. getCategories() )
	return tostring( outHtml )
end

--[=[
Get categories for nationality, occupations, etc.

Returns a string of wikicode
]=]
local function constructCategories( args )
	local item
	if args.wikidata_id ~= nil and args.wikidata_id ~= '' then
		-- This check required because getEntity can't cope with empty strings.
		item = mw.wikibase.getEntity( args.wikidata_id )
	else
		item = mw.wikibase.getEntity()
	end
	
	if item == nil or item == "" then
		-- .getEntity() failed, possibly because the page is not connected to
		-- Wikidata (the author is unknown and with only a partial name, e.g.)
		return
	end
	
	-- defer to the core function
	local cats = categoriesList( item )
	
	local out = ''
	-- and construct a list of wikitext categories
	for _, cat in pairs( cats ) do
		out = out .. '[[श्रेणी:' .. cat .. ']]\n'
	end
	return out
end

--------------------------------------------------------------------------------
-- Output link and category for initial letters of given name.
--
-- Debugging 1: =p.firstInitial({args={first_initial='Qx'}})
-- Debugging 2: =p.firstInitial({args={wikidata_id='Q1107985'}})
-- Debugging 1: =p.firstInitial({args={firstname='Qqxxx'}})
-- Debugging 3: =p.firstInitial({args={first_initial='Qx', wikidata_id='Q1107985'}})
local function firstInitial( args )
	local initials = nil
	
	-- Allow manual override of initials.
	if args.first_initial ~= nil and args.first_initial ~= '' then
		initials = args.first_initial
	end

	-- Handle special override, used by the {{disambiguation}} template.
	if initials == '!NO_INITIALS' then
		return ''
	end

	-- If a firstname is provided, get the initials from that.
	if initials == nil and args.lastname ~= nil and args.lastname ~= '' then
		initials =  mw.ustring.sub( args.lastname, 1, 2 )
	end

	-- Fetch from Wikidata. यह फिलहाल lastname के लिए छोड़ दिया गया है, बाद में इसे सुधार लेंगे।
	if initials == nil then
		local item = nil
		if args.wikidata_id ~= nil and args.wikidata_id ~= '' then
			-- Make it possible to pass a Wikidata ID, for easier testing.
			item = mw.wikibase.getEntity( args.wikidata_id )
		else
			item = mw.wikibase.getEntity()
		end
		if item then
			-- Get the first family name statement.
			local familyNames = item:getBestStatements( PROP_FAMILY_NAME )
			if #familyNames > 0 then
				local familyNameId = familyNames[1].mainsnak.datavalue.value.id
				local familyName = mw.wikibase.getEntity( familyNameId )
				if familyName.labels ~= nil and familyName.labels.en ~= nil then
					-- Take the first two characters of the English label
					-- (this avoids issues with 'navive label P1705' and is fine for English Wikisource).
					initials = mw.ustring.sub( familyName.labels.en.value, 1, 2 ) 
				end
			end
		end
	end

	-- Put it all together and output.
	local out = ''
	if initials ~= nil then
		local authorIndex = '[[विकिस्रोत:लेखक-' .. initials .. '|अनुक्रमणिका सूची: ' .. initials .. ']]'
		local authorCategory = mw.title.new('लेखक-' .. initials, 'श्रेणी')
		out = authorIndex .. '[[' .. authorCategory.prefixedText .. ']]'
		if authorCategory.exists ~= true then
			local missingAuthorCat = mw.title.new('अनुपलब्ध आद्याक्षर श्रेणी वाले लेखक पृष्ठ', 'श्रेणी')
			out = out .. '[[' .. missingAuthorCat.prefixedText .. ']]'
		end
	else
		out = '[[:श्रेणी:बिना आद्याक्षर वाले लेखक|बिना आद्याक्षर वाले लेखक]][[श्रेणी:बिना आद्याक्षर वाले लेखक]]'
	end

	return out
end

--------------------------------------------------------------------------------
-- Export all public functions.
return {
	header = function( frame ) return header( frame.args ) end;
	dates = function( frame ) return dates( frame.args ) end;
	date = function( frame ) return date( frame.args ) end;
	categories = function( frame ) return constructCategories( frame.args ) end;
	categoriesList = categoriesList;
	firstInitial = function( frame ) return firstInitial( frame.args ) end;
}