Модуль:Wikidata label

    Матеріал з Релігія в огні

    Документацію для цього модуля можна створити у Модуль:Wikidata label/документація

    --[[
      __  __           _       _      __        ___ _    _     _       _          _       _          _
     |  \/  | ___   __| |_   _| | ___ \ \      / (_) | _(_) __| | __ _| |_ __ _  | | __ _| |__   ___| |
     | |\/| |/ _ \ / _` | | | | |/ _ (_) \ /\ / /| | |/ / |/ _` |/ _` | __/ _` | | |/ _` | '_ \ / _ \ |
     | |  | | (_) | (_| | |_| | |  __/_ \ V  V / | |   <| | (_| | (_| | || (_| | | | (_| | |_) |  __/ |
     |_|  |_|\___/ \__,_|\__,_|_|\___(_) \_/\_/  |_|_|\_\_|\__,_|\__,_|\__\__,_| |_|\__,_|_.__/ \___|_|
    
    This module is intended to be the engine behind "Template:Label".
    This module was copied from Commons please ask for changes there.
    
    Please do not modify this code without applying the changes first at "Module:Wikidata label/sandbox" and testing
    at "Module:Wikidata label/testcases".
    
    Authors and maintainers:
    * User:Jarekt - original version
    
    ]]
    
    require('strict') -- used for debugging purposes as it detects cases of unintended global variables
    
    --=============================================
    --=== Internal functions ======================
    --=============================================
    
    ---------------------------------------------------------------------------
    -- Normalize input arguments by converting them all to lower case and
    -- replacing space with "_" in the argument name. Also empty strings are
    -- converted to nils. Arguments are collected from arguments passed to the
    -- module and if missing from the template that calls the module
    local function getArgs(frame)
    	local function normalize_input_args(input_args, output_args)
    		for name, value in pairs( input_args ) do
    			value = mw.text.trim(value) -- trim whitespaces from the beggining and the end of the string
    			if value ~= '' then -- nuke empty strings
    				if type(name)=='string' then
    					name = string.gsub( string.lower(name), ' ', '_')
    				end
    				output_args[name] = value
    			end
    		end
    		return output_args
    	end
    	local args = {}
    	args = normalize_input_args(frame:getParent().args, args)
    	args = normalize_input_args(frame.args, args)
    	return args
    end
    
    ---------------------------------------------------------------------------
    -- Function allowing for consistent treatment of boolean-like wikitext input.
    -- It works similarly to Module:Yesno but does not assume val is a string
    local function yesno(val, default)
    	if type(val) == 'boolean' then
    		return val
    	elseif type(val) == 'number' then
    		if val == 1 then
    			return true
    		elseif val == 0 then
    			return false
    		end
    	elseif type(val) == 'string' then
    		val = mw.ustring.lower(val)  -- put in lower case
    		if val == 'no'  or val == 'n' or val == 'false' or val == '0' then
    			return false
    		elseif val == 'yes' or val == 'y' or val == 'true' or val == '1' then
    			return true
    		end
    	end
    	return default
    end
    
    -------------------------------------------------------------------------
    -- get message in a given language
    -- INPUTS:
    -- * msg  - name of a message. For it to work [[MediaWiki:msg]] page need to be set up
    -- * lang - translate message to language "lang"
    -- * default - string to return in case this module is moved to a project where this message is not set
    -- OUTPUT:
    --  * translated message
    local function getMessage(msg, lang, default)
    	msg = mw.message.new(msg):inLanguage(lang):plain()
    	return (msg == nil and default) or msg
    end
    
    ---------------------------------------------------------------------------
    -- use different sitelink call depending if you already have an entity or not
    -- INPUTS:
    --  * item and entity - entity id and entity: if full entity already uploded than use that
    --                      otherwise use entity id to look up sitelink
    --  * lang - language of the project
    -- OUTPUT:
    --  * sitelink
    local function getSitelink(item, entity, lang)
    	if entity and entity.getSitelink then -- if we have entity then use it
    		return entity:getSitelink(lang .. 'wiki')
    	else -- if no entity then use different function
    		return mw.wikibase.getSitelink(item, lang .. 'wiki')
    	end
    end
    
    ---------------------------------------------------------------------------
    -- use different sitelink call depending if you already have an entity or not
    -- INPUTS:
    --  * item and entity - entity id and entity: if full entity already uploded than use that
    --                      otherwise use entity id to look up sitelink
    --  * prop - property for which to return the best statment
    -- OUTPUT:
    --  * value of the best statment (only from the first one)
    local function getBestStatementsValue(item, entity, prop)
    	local statments
    	if entity then
    		statments = entity:getBestStatements(prop)
    	else
    		statments = mw.wikibase.getBestStatements(item, prop)
    	end
    	for _, statment in ipairs(statments) do
    		if statment and statment.mainsnak.datavalue.value then
    			return statment.mainsnak.datavalue.value
    		end
    	end
    end
    
    ---------------------------------------------------------------------------
    -- change capitalization of the label
    -- INPUTS:
    --  * label - label string
    --  * capitalization - capitalization to be applied: allowed values are "tc", "lc",
    --      "uc", "lcfirst", and "ucfirst". Any other value will return original string
    --  * lang - language of the label
    -- OUTPUT:
    --  * value of the best statment (only from the first one)
    local function apply_capitalization(label, capitalization, lang)
    	capitalization = string.lower(capitalization or 'none')
    	if capitalization == 'none' then
    		return label
    	elseif capitalization == 'uc' then
    		return mw.language.new(lang):uc(label)
    	elseif capitalization == 'lc' then
    		return mw.language.new(lang):lc(label)
    	elseif capitalization == 'tc' then -- title case
    		local new_label = {}
    		for _, word in ipairs(mw.text.split(label, ' ')) do
    			table.insert(new_label, mw.language.new(lang):ucfirst(word))	
    		end
    		return table.concat(new_label, ' ')
    	elseif capitalization == 'ucfirst' then
    		return mw.language.new(lang):ucfirst(label)
    	elseif capitalization == 'lcfirst' then
    		return mw.language.new(lang):lcfirst(label)
    	end
    	return label
    end
    
    --[[-------------------------------------------------------------------------
    get link based on user preference
    INPUTS:
    * link_type - can be :
       * "wikidata" - link to wikidata
       * "wikipedia" - link to wikipedia (language dependent)
       * "wikidata talk" - link to wikidata talk page
       * "commons" - link to commons (try sitelink then commons category then commons gallery)
       * "commonscat" - link to commons (try commons category then commons gallery)
       * "-" - means no link
    * item   - entity ID (always provided)
    * entity - whole entity. It can be nil if whole entity is not loaded
    * langList - language fallback list for preferred language (required)
    OUTPUT:
    * link - link to the wikimedia page
    ]]
    local function getLink(link_type, item, entity, langList)
    	local link, eLink
    	link_type = mw.ustring.lower(link_type or '')
    	local item_type = mw.ustring.sub(item, 1, 1) -- first letter prefix of item entity ID: 'Q', 'P' or 'M'
    	if item_type == 'M' then
    		eLink='c:Special:EntityPage/'..item
    	elseif item_type == 'Q' then
    		eLink='d:'..item -- wikibase entity page link
    	elseif item_type == 'P' then
    		eLink='d:Property:'..item -- wikibase entity page link
    	else
    		eLink='d:Special:EntityPage/'..item
    	end
    	if link_type == '-' then -- allow different link formats
    		link = ''            -- no link
    	elseif link_type == 'wikidata' or item_type == 'M' then
    		link = eLink        -- link to wikibase entity page
    	elseif link_type == 'wikidata talk' and item_type == 'P' then
    		link = 'd:Property talk:'.. item        -- link to wikidata property talk page
    	elseif link_type == 'wikidata talk' then
    		link = 'd:Talk:'..item   -- link to wikidata talk page
    	elseif link_type == 'commons' or link_type == 'commonscat' then
    		--[[
    		When link_type == 'commons' we try the following links (in specified order):
    		  1) commons sitelink
    		  2) P373 "Commons Category" claims
    		  3) P935 "Commons Gallery"  claims
    		Since most items have a commons sitelink we never have to look for claims
    		When link_type == 'commonscat' we try to maximize chances of commons link being a category, so we
    		      try the following links (in specified order):
    		  1) commons sitelink, which is kept if it points to a category
    		  2) P373 "Commons Category" claims
    		  3) commons sitelink (which does not point to a category)
    		  4) P935 "Commons Gallery"  claims
    		Since most pages have a commons sitelink we never have to look for claims
    		]]
    		local sLink = getSitelink(item, entity, 'commons')  -- look for sitelink to commons
    		if sLink then
    			sLink = 'c:'..sLink
    			if (link_type == 'commons') or (link_type == 'commonscat' and mw.ustring.find(sLink, 'Category:')) then
    				link = sLink
    			end
    		end
    		if not link then -- try linking to P373 "Commons Category"
    			local cat = getBestStatementsValue(item, entity, 'P373')
    			link = (cat ~= nil and 'c:Category:' .. cat) or nil
    		end
    		link = link or sLink
    		if not link then -- try linking to P935 "Commons Gallery"
    			link = getBestStatementsValue(item, entity, 'P935')
    		end
    	end
    	if not link then -- apply default "Wikipedia" link type
    		for _, language in ipairs(langList) do
    			local sitelink = getSitelink(item, entity, language)
    			if sitelink then
    				link = 'w:'.. language ..':'.. sitelink
    				break
    			end
    		end
    	end
    	return link or eLink  -- no wiki sitelink, so link to wikidata
    end
    
    --=============================================
    --=== External functions ======================
    --=============================================
    local p = {}
    
    --======================================================================
    --=== API functions for use from other Scribunto modules ===============
    --======================================================================
    
    --[[
    _getLabel
    
    This function returns a label translated to desired language, created based on wikidata
    
    Inputs:
    	1: item - wikidata's item's q-id or entity class
    	2: lang - desired language of the label
    	3: link_type - link style. Possible values (case-insensitive): "wikipedia", "wikidata", "Commons", or "-" (no link)
    	4: capitalization - can be "uc" (upper case), "lc" (lower case), "ucfirst" (upper case for the first letter),
    			"lcfirst" (lower case for the first letter), or 'none' (default)
    
    Error Handling:
    	Bad q-id will result in displayed error
    ]]
    function p._getLabel(item, lang, link_type, capitalization, show_id)
    	local entity, s, link, label, language, desc
    
    	-- clean up the input parameters
    	if type(item) ~= 'string' then -- "item" is not a q-id
    		entity = item            -- "item" must be the entity
    		item   = entity.id       -- look-up q-id
    	elseif tonumber(item) then   -- if it is just the number then add "Q" in front
    		item = 'Q'..item
    	end
    	item = mw.ustring.gsub(mw.ustring.upper(item), 'PROPERTY:P', 'P') -- make all the properties the same and capitalize
    
    	if not lang then
    		label, lang = mw.wikibase.getLabelWithLang(item)
    	end
    	if not lang then -- if still no language
    		lang  = mw.getCurrentFrame():callParserFunction("int","lang")  -- get user's chosen language
    		label = nil
    	end
    
    	-- build language fallback list
    	lang = mw.ustring.lower(lang)
    	local langList = mw.language.getFallbacksFor(lang)
    	table.insert(langList, 1, lang)
    
    	-- get label (visible part of the link)
    	if not label then
    		for _, language in ipairs(langList) do  -- loop over language fallback list looking for label in the specific language
    			if entity then
    				label = entity:getLabel(language)
    			else
    				label = mw.wikibase.getLabelByLang(item, language)
    			end
    			if label then break end                    -- label found and we are done
    		end
    	end
    	if label then  -- wikitext-escape the label if we have one
    		label = mw.text.nowiki(label)
    	end
    	if not label then                              -- no labels found, so just show the q-id
    		label = item
    	elseif show_id then           -- add id
    		show_id = yesno(show_id,false)
    		if show_id then
    			local wordsep = getMessage('Word-separator', lang, ' ')
    			local id = mw.message.new('parentheses', item):inLanguage(lang):plain()
    			id = (id~=nil and id) or ('('..item..')') -- in case this module is moved to a project where {{int:parenthesis}} is not set
    			label = label .. wordsep .. "<small>" .. id .. "</small>"
    		end
    	end
    	label = apply_capitalization(label, capitalization, lang)
    	
    	-- look for description
    	if  entity and entity.descriptions and lang then 
    		for _, language in ipairs(langList) do
    			if entity.descriptions[language] then
    				desc = entity.descriptions[language].value 
    				break
    			end
    		end
    	else
    		desc = mw.wikibase.getDescription(item)
    	end
    	if desc and link_type ~= '-' then  -- wikitext-escape the description if we have one
    		desc = mw.text.nowiki(desc) -- add description as hover text
    		label = '<span title="' .. desc .. '">' .. label .. '</span>'
    	end
    
    	-- return the results
    	if link_type == '-' then
    		return label -- return just the label
    	else
    		link = getLink(link_type, item, entity, langList)
    		return '[[' .. link .. '|' .. label .. ']]' -- return link
    	end
    end
    
    --[[-------------------------------------------------------------------------------
    _sitelinks
    
    This function returns a table of sitelinks for a single project organized by language
    
    Inputs:
    	1: item - wikidata's item's q-id or entity class
    	2: project - (case-insensitive) one of: "wikipedia", "wikisource", "wikiquote", "wikibooks", "wikinews",
    			"wikiversity", "wikivoyage", "wiktionary", "commons", "mediawiki", "wikispecies", "wikidata", etc.
    
    Output:
    	Table of sitelinks with language fields
    
    Output:
    	Table of sitelinks with language fields
    See also
    * [https://foundation.wikimedia.org/wiki/Special:SiteMatrix] for the full list of supported interwikis.
    * [https://dumps.wikimedia.org/backup-index.html] for the full list of sitecodes (used in database dumps).
    ]]
    function p._sitelinks(item, project)
    	local entity, sitelink
    	-- get entity
    	if type(item) == 'string' then -- "item" is a q-id
    		entity = mw.wikibase.getEntity(item)
    	else
    		entity = item              -- "item" is the entity
    	end
    
    	-- convert from english project name  to proproject code
    	local projLUT = {
    		wikipedia   = 'wiki',           commons   = 'commonswiki',
    		foundation  = 'foundationwiki', mediawiki = 'mediawikiwiki',
    		wikispecies = 'specieswiki',    wikidata  = 'wikidatawiki',
    		incubator   = 'incubatorwiki',	oldwikisource = 'sourceswiki',
    	}
    	local langLUT = {
    		-- These are not language codes before the 'wiki' or 'wikiversity' suffix in a sitecode:
    		foundation = '~', commons = '~',	-- they will be skipped
    		incubator  = '~', meta    = '~',
    		mediawiki  = '~', sources = '~',
    		species    = '~', beta    = '~',
    		-- Legacy language codes used in sitecodes, remapped to standard Wikimedia language codes:
    		-- See https://meta.wikimedia.org/wiki/Special_language_codes for details
    		als     = 'gsw', bat_smg      = 'sgs',
    		fiu_vro = 'vro', be_x_old     = 'be-tarask',
    		roa_rup = 'rup', zh_classical = 'lzh',
    		zh_yue  = 'yue', zh_min_nan   = 'nan',
    		zh_wuu  = 'wuu', no           = 'nb',
    	}
    	project = project:lower()
    	project = projLUT[project] or project -- correct the project name
    	local n = project:len()
    	local linkTable = {}
    	if entity and entity.sitelinks then  -- See if entity exists, and that it has sitelinks
    		for _, sitelink in pairs(entity.sitelinks) do -- loop over all sitelinks
    			local site = sitelink.site
    			local m    = site:len() - n
    			local proj = site:sub(m +1)  -- project part of the siteID
    			if proj == project  then -- proj matches desired "project"
    				local lang = site:sub(1, m)  -- language part of the siteID
    				lang = langLUT[lang] or lang:gsub('_','-')
    				if lang ~= '~' then -- proj matches desired "project"
    					linkTable[lang] = sitelink.title
    				end
    			end
    		end
    	end
    	return linkTable
    end
    
    --[[----------------------------------------------------------------
    _aliases
    
    This function returns a table of aliases for a single language
    
    Inputs:
    	1: item - wikidata's item's q-id or entity class
    	2: lang - language code, like 'en' or 'de'
    
    Output:
    	Table of aliases with language fields
    ]]
    function p._aliases(item, lang)
    	local entity
    	if type(item) == 'string' then -- "item" is a q-id
    		entity = mw.wikibase.getEntity(item)
    	else
    		entity = item            -- "item" is the entity
    	end
    	local aliasTable = {}
    	if entity and entity.aliases then						-- See if there is an entity and that is has aliases
    		if entity.aliases[lang] then						-- See if it has English Aliases
    			for _, alias in pairs(entity.aliases[lang]) do  -- Make a loop around the English aliases
    				table.insert(aliasTable, alias.value)				-- Create a table of English aliases
    			end
    		end
    	end
    	return aliasTable
    end
    
    --======================================================================
    --=== Invoke functions for use from wikitext, e.g., templates ==========
    ---=====================================================================
    
    --[[
    getLabel
    
    This function returns a label translated to desired language, created based on wikidata
    
    Usage:
    {{#invoke:Wikidata label|getLabel|item=Q...|lang=..|link_style=..|capitalization=..}}
    
    Parameters
    	1: wikidata's item's q-id (required)
    	2: language (optional; default {{int:lang}})
    	3: link_style: "wikipedia" (default), "Wikidata", "Commons", or "-" (no link)
    	4: capitalization - can be "uc", "lc", "tc", "ucfirst", "lcfirst"
    
    Error Handling:
    	Bad q-id will result in displayed error
    ]]
    function p.getLabel(frame)
    	local args = getArgs(frame)
    	return p._getLabel(args.item, args.lang, args.link, args.capitalization, args.show_id)
    end
    
    --[[-------------------------------------------------------------------------------
    sitelinks
    
    This function returns a comma separated list of sitelinks for a single project organized by language
    Its main purpose is to help with testing of _sitelinks function.
    
    Usage:
    {{#invoke:Wikidata label|sitelinks|item=Q...|project=..}}
    
    Inputs:
    	1: item - wikidata's item's q-id or entity class
    	2: project - "wikipedia" (or "wiki"), "wikisource", "wikiquote", "wikibooks",
    	             "wikinews", "wikiversity", "wikivoyage", "wiktionary", etc.
    
    Output:
    	comma separated list
    ]]
    function p.sitelinks(frame)
    	local args = getArgs(frame)
    	local sitelinks = p._sitelinks(args.item, args.project)
    	local sitelinkList = {}
    	for lang, sitelink in pairs(sitelinks) do
    		table.insert(sitelinkList, (lang=='' and sitelink) or (lang .. ':' .. sitelink))
    	end
    	return table.concat(sitelinkList, ', ')
    end
    
    --[[----------------------------------------------------------------------------
    aliases
    
    This function returns a comma separated list of aliases for a single language
    Its main purpose is to help with testing of _aliases function.
    
    Usage:
    {{#invoke:Wikidata label|aliases|item=Q...|lang=..}}
    
    Inputs:
    	1: item - wikidata's item's q-id or entity class
    	2: lang - language code, like 'en' or 'de'
    
    Output:
    	Comma separated list of aliases
    ]]
    function p.aliases(frame)
    	local args = getArgs(frame)
    	return table.concat(p._aliases(args.item, args.lang), ', ')
    end
    
    return p