Module:Lingua Libre record

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Lua

CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules


This module powers {{Lingua Libre record}}. It exports two functions, both of which mainly read their parameters from the parent frame (i.e. the place that transcluded {{Lingua Libre record}}). The functions are:

main
Returns the wikitext content of the template, mainly an {{Information}} block. Takes the same parameters as the template, plus one additional argument: if |includeonly=y, then categories are included in the result (by default, they are not included). The template uses |includeonly=<includeonly>y</includeonly> to only set this parameter when the template is being transcluded; this effectively does the same thing that <includeonly>[[Category:...]]</includeonly> would do in a non-Lua-based template.
defaultsort
Returns the defaultsort string for the page. This has to be a separate function because the {{DEFAULTSORT:}} magic word can’t be in the wikitext returned by main. Takes the same parameters as the template (but doesn’t use all of them).

Code

local p = {}

local delink = nil -- lazy-loaded from require( 'Module:Delink' ).delink
local langSwitch = nil -- lazy-loaded from require( 'Module:LangSwitch' )._langSwitch
local getLabel = nil -- lazy-loaded from require( 'Module:Wikidata label' )._getLabel

local downloadLink = '[[File:Crystal Clear app download manager.svg|12px|link=lingualibre:Help:Download datasets]]'

local messages = mw.loadData( 'Module:Lingua Libre record/i18n' )
local _lang = nil -- lazy-loaded from {{int:lang}}

-- Get the user interface language code.
local function lang( frame )
	if _lang == nil then -- lazy-load
		_lang = frame:callParserFunction( 'int', 'lang' )
	end
	return _lang
end

-- Get the localized version of the message with the given key from the /i18n module.
local function message( frame, key )
	if langSwitch == nil then -- lazy-load
		langSwitch = require( 'Module:LangSwitch' )._langSwitch
	end

	local msg = langSwitch( messages[ key ], lang( frame ) )
	if msg == '' then
		msg = frame:expandTemplate{
			title = 'Please translate',
			args = {
				messages[ key ].en,
				'Module:Lingua Libre record',
				demo = 'yes',
			},
		}
	end

	return msg
end

-- Get the first best-rank data value (skipping somevalue/novalue)
-- for the given property ID from the given entity, or nil.
-- Used by bestStringValue, bestEntityIdValue, bestMonolingualTextValue.
local function bestDataValue( entity, propertyId )
	if not entity then return nil end
	local statements = entity:getBestStatements( propertyId )
	for i = 1, #statements do
		local snak = statements[ i ].mainsnak
		if snak.snaktype == 'value' then
			return snak.datavalue
		end
	end
	return nil
end

-- Get the first best-rank string value (skipping somevalue/novalue)
-- for the given property ID from the given entity, or nil.
local function bestStringValue( entity, propertyId )
	local value = bestDataValue( entity, propertyId )
	if not value then return nil end
	assert( value.type == 'string', 'data value type of ' .. propertyId .. ' must be string, not ' .. value.type )
	return value.value
end

-- Get the first best-rank entity ID value (skipping somevalue/novalue)
-- for the given property ID from the given entity, or nil.
local function bestEntityIdValue( entity, propertyId )
	local value = bestDataValue( entity, propertyId )
	if not value then return nil end
	assert( value.type == 'wikibase-entityid', 'data value type of ' .. propertyId .. ' must be wikibase-entityid, not ' .. value.type )
	return value.value.id
end

-- Get the first best-rank monolingual text value (skipping somevalue/novalue)
-- for the given property ID from the given entity, or nil.
-- Returns wikitext (string), the plain value (string), and its language (object).
local function bestMonolingualTextValue( entity, propertyId )
	local value = bestDataValue( entity, propertyId )
	if not value then return nil end
	assert( value.type == 'monolingualtext', 'data value type of ' .. propertyId .. ' must be monolingualtext, not ' .. value.type )
	local text = value.value.text
	local language = mw.language.new( value.value.language )
	local span = mw.html.create( 'span' )
	span
		:attr( 'lang', language:getCode() )
		:attr( 'dir', language:getDir() )
		:wikitext( mw.text.nowiki( text ) )
	return tostring( span ), text, language
end

-- Get the first best-rank time value (skipping somevalue/novalue)
-- for the given property ID from the given entity, or nil.
-- Requires proleptic Gregorian calendar,
-- and automatically removes irrelevant parts of the timestamp according to the precision.
local function bestTimeValue( entity, propertyId )
	local value = bestDataValue( entity, propertyId )
	if not value then return nil end
	assert( value.type == 'time', 'data value type of ' .. propertyId .. ' must be time, not ' .. value.type )

	if value.value.calendarmodel ~= 'http://www.wikidata.org/entity/Q1985727' then -- proleptic Gregorian calendar
		return nil
	end

	local timestamp = value.value.time
	local precision = value.value.precision
		local pattern = '^+?((((((-?%d*)-%d*)-%d*)T%d*):%d*):%d*)Z$'
	local replacement = '%1' -- second
	if precision == 13 then replacement = '%2' -- minute
	elseif precision == 12 then replacement = '%3' -- hour
	elseif precision == 11 then replacement = '%4' -- day
	elseif precision == 10 then replacement = '%5' -- month
	elseif precision <= 9 then replacement = '%6' -- year
	end
	return mw.ustring.gsub( timestamp, pattern, replacement )
end

-- Get the first Wikimedia username (P4174) qualifier
-- of the first best recordist (P10893) statement, or nil.
local function bestAuthor( entity )
	if not entity then return nil end
	local statements = entity:getBestStatements( 'P10893' )
	for i = 1, #statements do
		local qualifiers = statements[ i ].qualifiers or {}
		for j = 1, #( qualifiers.P4174 or {} ) do
			local qualifier = qualifiers.P4174[ j ]
			if qualifier.snaktype == 'value' then
				local userName = qualifier.datavalue.value
				return '[[User:' .. userName .. '|' .. userName .. ']]'
			end
		end
	end
	return nil
end

-- Get the first author name string (P2093) and Lingua Libre ID (P10369)
-- qualifiers of the first best spoken by (P10894) statement, or nil.
local function bestSpeaker( entity )
	if not entity then return nil end
	local statements = entity:getBestStatements( 'P10894' )
	local speaker, speakerId
	for i = 1, #statements do
		local qualifiers = statements[ i ].qualifiers or {}
		for j = 1, #( qualifiers.P2093 or {} ) do
			local qualifier = qualifiers.P2093[ j ]
			if qualifier.snaktype == 'value' then
				speaker = qualifier.datavalue.value
				break
			end
		end
		for j = 1, #( qualifiers.P10369 or {} ) do
			local qualifier = qualifiers.P10369[ j ]
			if qualifier.snaktype == 'value' then
				speakerId = qualifier.datavalue.value
				break
			end
		end
	end
	return speaker, speakerId
end

-- Construct the {{Information}} template for the given information.
-- The parameters are used as they are: fallback to entity data is up to the caller.
local function information(
	frame,
	languageId,
	transcriptionWikitext,
	qualifier,
	speaker,
	speakerId,
	author,
	linguaLibreId,
	date,
	otherVersions
)
	local wordSeparator = mw.message.new( 'word-separator' ):plain()

	local languageField = ''
	if languageId then
		if getLabel == nil then
			getLabel = require( 'Module:Wikidata label' )._getLabel
		end
		local languageLabel = getLabel( languageId, lang( frame ) )
		languageField = frame:expandTemplate{ title = 'Information field', args = {
			name = message( frame, 'language' ),
			value = languageLabel .. wordSeparator .. downloadLink,
		} }
	end

	local transcriptionField = ''
	if transcriptionWikitext then
		transcriptionField = frame:expandTemplate{ title = 'Information field', args = {
			name = message( frame, 'transcription' ),
			value = transcriptionWikitext,
		} }
	end

	local qualifierField = ''
	if qualifier and qualifier ~= '' then
		qualifierField = frame:expandTemplate{ title = 'Information field', args = {
			name = message( frame, 'qualifier' ),
			value = qualifier,
		} }
	end
	
	local speakerLink
	if speaker and speaker ~= '' and speakerId and speakerId ~= '' then
		speakerLink = '[[lingualibre:' .. speakerId .. '|' .. speaker .. ']]'
	elseif speaker and speaker ~= '' then
		speakerLink = speaker
	elseif speakerId and speakerId ~= '' then
		speakerLink = '[[lingualibre:' .. speakerId .. '|' .. speakerId .. ']]'
	else
		speakerLink = nil
	end
	local speakerItem = ''
	if speakerLink then
		speakerItem = '\n* ' .. message( frame, 'speaker' ) .. wordSeparator .. speakerLink
	end
	
	local recorderItem = ''
	if author and author ~= '' then
		recorderItem = '\n* ' .. message( frame, 'recorder' ) .. wordSeparator .. author
	end

	local source = frame:expandTemplate{ title = 'Own' }
	if linguaLibreId then
		local linguaLibreLabel = mw.wikibase.getLabel( 'Q60024037' )
		local linguaLibreLink = '[[lingualibre:' .. linguaLibreId .. '|' .. linguaLibreLabel .. ']]'
		source = source .. wordSeparator .. mw.message.new( 'parentheses', linguaLibreLink ):plain()
	end

	return frame:expandTemplate{ title = 'Information', args = {
		Description = message( frame, 'description' ),
		other_fields_1 = languageField .. transcriptionField .. qualifierField,
		Author = speakerItem .. recorderItem,
		Source = source,
		Date = date,
		[ 'Other versions' ] = otherVersions,
	} }
end

-- Construct the categories for the given information.
local function categories( languageId, speaker, speakerId, author, date )
	local languageCategory
	if languageId and languageId ~= '' then
		local languageCode = bestStringValue( mw.wikibase.getEntity( languageId ), 'P220' )
		if languageCode then
			languageCategory = '[[Category:Lingua Libre pronunciation-' .. languageCode .. ']]'
		else
			languageCategory = '[[Category:Lingua Libre pronunciation-other (' .. languageId .. ')]]'
		end
	else
		languageCategory = '[[Category:Lingua Libre pronunciation with incomplete parameters]]'
	end
	local authorCategory = ''
	if author and author ~= '' then
		if delink == nil then -- lazy-load
			delink = require( 'Module:Delink' ).delink
		end
		local authorCategoryTitle = 'Category:Lingua Libre pronunciation by ' .. delink( { author } )
		if mw.title.new( authorCategoryTitle ).exists then
			authorCategory = '[[' .. authorCategoryTitle .. ']]'
		end
	else
		authorCategory = '[[Category:Lingua Libre pronunciation with incomplete parameters]]'
	end
	local speakerCategory = ''
	if not ( speaker and speaker ~= '' and speakerId and speakerId ~= '' ) then
		speakerCategory = '[[Category:Lingua Libre pronunciation with incomplete parameters]]'
	end
	local dateCategory = ''
	if not ( date and date ~= '' ) then
		dateCategory = '[[Category:Lingua Libre pronunciation with incomplete paremeters]]'
	end
	return languageCategory .. authorCategory .. speakerCategory
end

-- Parse the arguments out of the given frame,
-- including fallbacks to the parent frame and to entity data.
local function parseArgs( frame )
	local args = frame.args
	local pargs = frame:getParent().args
	local speaker = args.locutor or args.speaker or pargs.locutor or pargs.speaker
	local speakerId = args.locutorId or args.speakerId or pargs.locutorId or pargs.speakerId
	local author = args.author or pargs.author
	local languageId = args.languageId or pargs.languageId 
	local transcriptionWikitext = args.transcription or pargs.transcription
	local qualifier = args.qualifier or pargs.qualifier
	local date = args.date or pargs.date
	local otherVersions = args['other versions'] or pargs['other versions']
	local mid = args.mid or pargs.mid

	local entity
	if mid then
		entity = mw.wikibase.getEntity( mid )
	else
		entity = mw.wikibase.getEntity()
	end

	local linguaLibreId = bestStringValue( entity, 'P10369' )
	
	if ( not speaker or speaker == '' ) and ( not speakerId or speakerId == '' ) then
		speaker, speakerId = bestSpeaker( entity )
	end
	
	if not author or author == '' then
		author = bestAuthor( entity )
	end
	
	if not languageId or languageId == '' then
		languageId = bestEntityIdValue( entity, 'P407' )
	end
	
	local transcription = transcriptionWikitext
	if not transcriptionWikitext or transcriptionWikitext == '' then
		transcriptionWikitext, transcription = bestMonolingualTextValue( entity, 'P9533' )
	end
	
	if not date or date == '' then
		date = bestTimeValue( entity, 'P10135' )
	end
	
	return speaker, speakerId, author, languageId, transcriptionWikitext, transcription, qualifier, date, otherVersions, linguaLibreId
end

-- Main function, returning most of the relevant wikitext.
-- {{DEFAULTSORT:}} cannot be done here, callers must also invoke the defaultsort function below.
function p.main( frame )
	local speaker, speakerId, author, languageId, transcriptionWikitext, transcription, qualifier, date, otherVersions, linguaLibreId = parseArgs( frame )
	local includeonly = frame.args.includeonly == 'y'

	local info = information(
		frame,
		languageId,
		transcriptionWikitext,
		qualifier,
		speaker,
		speakerId,
		author,
		linguaLibreId,
		date,
		otherVersions
	)
	
	local cats = ''
	if includeonly then
		cats = categories( languageId, speaker, speakerId, author, date )
	end

	return '<div class="haudio">' .. info .. '</div>' .. cats
end

-- Return the {{DEFAULTSORT:}} string that should be used.
-- The {{DEFAULTSORT:}} magic word itself must be in the invoking wikitext.
function p.defaultsort( frame )
	local speaker, speakerId, author, languageId, transcriptionWikitext, transcription, qualifier, date, otherVersions, linguaLibreId = parseArgs( frame )

	if transcription and transcription ~= '' then
		local suffix = ''
		if qualifier and qualifier ~= '' then
			suffix = ' ' .. qualifier
		end
		return transcription .. suffix
	else
		return ''
	end
end

return p