Module:Spoken Wikipedia

Revision as of 04:31, 15 September 2022 by Thewolf12 (talk | contribs) (1 revision imported)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Documentation for this module may be created at Module:Spoken Wikipedia/doc

local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local Date = require('Module:Date')._Date
local lang = mw.language.new('en')
local cfg = mw.loadData('Module:Spoken Wikipedia/configuration')

p = {}

local function wikiError(message)
	local ret = mw.html.create('div')
		:addClass(cfg.i18n.class.err)
		:wikitext(message)
		:done()
	return tostring(ret)
end

local function category(c, nocat)
	if nocat then
		return ''
	else
		return c
	end
end

local function formatPageText(page)
	if page then
		return '<span class="fn">[[' .. page .. ']]</span>'
	end
	if mw.title.getCurrentTitle().namespace == 0 then
		return cfg.i18n.this_article
	else
		return cfg.i18n.this_page
	end
end

local function formatFileLength(filenames)
	local length = 0
	for _, filename in ipairs(filenames) do
		local fileTitle = mw.title.new(filename, 'Media')
		if fileTitle and fileTitle.file and fileTitle.file.exists then
			length = length + fileTitle.file.length
		end
	end

	-- Add 30 to offset the rounding down
	local intervals = lang:getDurationIntervals(length + 30, { 'hours', 'minutes' })
	local ret = string.format(
		'<span class="min">%s</span> %s',
		intervals.minutes or 0,
		intervals.minutes == 1 and cfg.i18n.minute or cfg.i18n.minutes
	)
	if intervals.hours then
		ret = string.format(
			'<span class="h">%s</span> %s and %s',
			intervals.hours,
			intervals.hours == 1 and cfg.i18n.hour or cfg.i18n.hours,
			ret
		)
	end
	return '<span class="duration">' .. ret .. '</span>'
end

local function formatHeader(filenames, page)
	local listento = mw.html.create('span')
		:addClass(cfg.i18n.class.listento)
		:wikitext(string.format(
			cfg.i18n.listento,
			formatPageText(page)
		))
		:done()
	
	local file_length
	if #filenames > 1 then
		file_length = string.format(
			cfg.i18n.n_files_length,
			tostring(#filenames),
			formatFileLength(filenames)
		)
	else
		file_length = string.format(
			cfg.i18n.one_file_length,
			formatFileLength(filenames)
		)
	end
	return mw.html.create('div')
		:addClass(cfg.i18n.class.header)
		:node(listento)
		:wikitext(file_length)
		:done()
end

local function formatIcon()
	return mw.html.create('div')
		:addClass(cfg.i18n.class.icon)
		:wikitext(cfg.i18n.icon)
		:done()
end

local function formatFiles(filenames, nocat)
	if #filenames == 0 then
		return wikiError(cfg.i18n.err.no_filename) ..
			category(cfg.i18n.cat.no_filename, nocat)
	end
	
	-- TODO: the else branch really wants to be a mw.html <ol> object rather than wikitext
	-- version of the same, so that we can style the numbers nicer
	local files = {}
	if #filenames == 1 then
		table.insert(files, string.format(cfg.i18n.one_file, filenames[1]))
	else
		for i, filename in ipairs(filenames) do
			table.insert(files, string.format(cfg.i18n.n_files, filename, i))
		end
	end

	return mw.html.create('div')
		:addClass(cfg.i18n.class.files)
		:wikitext(table.concat(files))
		:done()
		:newline()
end

local function formatDateText(frame, dateArg, nocat)
	local d = dateArg and Date(dateArg) or nil
	return d and frame:expandTemplate{
		title = 'Start date', args = {
			d.year,
			d.month,
			d.day,
			df='y'
		}
	} or (wikiError(cfg.i18n.err.no_date) .. category(cfg.i18n.cat.no_date, nocat))
end

local function formatDisclaimer(frame, filenames, page, dateArg, nocat)
	local thisFileText = ''
	local disclaimer
	if #filenames == 1 then
		thisFileText = filenames[1]
		disclaimer = cfg.i18n.one_file_disclaimer
	else
		disclaimer = cfg.i18n.n_files_disclaimer
	end
	return mw.html.create('div')
		:addClass(cfg.i18n.class.disclaimer)
		:wikitext(string.format(
			disclaimer,
			thisFileText,
			formatPageText(page),
			formatDateText(frame, dateArg, nocat)
		))
		:done()
end

local function formatFooter()
	return mw.html.create('div')
		:addClass(cfg.i18n.class.footer)
		:wikitext(cfg.i18n.footer)
		:done()
end

local function formatTopicon(frame, filenames)
	local wikilink
	if #filenames > 0 then
		wikilink = 'File:' .. filenames[1]
	else
		wikilink = cfg.i18n.topicon_multiwikilink
	end
	return frame:expandTemplate{
		title = "Top icon",
		args = {
			imagename = 'Sound-icon.svg',
			wikilink = wikilink,
			text = 'Listen to this article',
			id = 'spoken-icon'
		}
	}
end

local function extractFilenames(args)
	local filenames = {}
	for key, rawValue in ipairs(args) do
		local value = mw.text.trim(rawValue)
		if type(key) == "number" and value ~= '' then
			table.insert(filenames, value)
		end
	end
	return filenames
end

local function sidebox(nodes)
	root = mw.html.create('div')
		:addClass(cfg.i18n.class.box)
	for _, node in ipairs(nodes) do
		root:node(node)
	end
	return root
end

function main(frame)
	local args = getArgs(frame)

	-- Mandatory parameters
	local filenames = extractFilenames(args)
	local dateArg = args['date']
	-- Optional parameters
	local page = args['page']
	local nocat = yesno(args['nocat'], false) or false

	local root = sidebox({
		formatHeader(filenames, page),
		formatFiles(filenames, nocat),
		formatIcon(),
		formatDisclaimer(frame, filenames, page, dateArg, nocat),
		formatFooter()
	})

	if mw.title.getCurrentTitle().namespace == 0 then
		root:wikitext(formatTopicon(frame, filenames))
		root:wikitext(category(cfg.i18n.cat.articles, nocat))
	end

	return frame:extensionTag{
		name = 'templatestyles', args = { src = cfg.templatestyles }
	} .. tostring(root)
end

p.main = main

return p