「モジュール:Wikidata label」の版間の差分
ナビゲーションに移動
検索に移動
bsd>Jarekt 細 (fix https://phabricator.wikimedia.org/T240563) |
細 (1版 をインポートしました) |
||
(4人の利用者による、間の4版が非表示) | |||
1行目: | 1行目: | ||
--[[ | --[[ | ||
__ __ _ _ __ ___ _ _ _ _ _ _ _ | __ __ _ _ __ ___ _ _ _ _ _ _ _ | ||
| \/ | ___ __| |_ _| | ___ \ \ / (_) | _(_) __| | __ _| |_ __ _ | | __ _| |__ ___| | | | \/ | ___ __| |_ _| | ___ \ \ / (_) | _(_) __| | __ _| |_ __ _ | | __ _| |__ ___| | | ||
| |\/| |/ _ \ / _` | | | | |/ _ (_) \ /\ / /| | |/ / |/ _` |/ _` | __/ _` | | |/ _` | '_ \ / _ \ | | | |\/| |/ _ \ / _` | | | | |/ _ (_) \ /\ / /| | |/ / |/ _` |/ _` | __/ _` | | |/ _` | '_ \ / _ \ | | ||
| | | | (_) | (_| | |_| | | __/_ \ V V / | | <| | (_| | (_| | || (_| | | | (_| | |_) | __/ | | | | | | (_) | (_| | |_| | | __/_ \ V V / | | <| | (_| | (_| | || (_| | | | (_| | |_) | __/ | | ||
|_| |_|\___/ \__,_|\__,_|_|\___(_) \_/\_/ |_|_|\_\_|\__,_|\__,_|\__\__,_| |_|\__,_|_.__/ \___|_| | |_| |_|\___/ \__,_|\__,_|_|\___(_) \_/\_/ |_|_|\_\_|\__,_|\__,_|\__\__,_| |_|\__,_|_.__/ \___|_| | ||
This module is intended to be the engine behind "Template:Label". | 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 | Please do not modify this code without applying the changes first at "Module:Wikidata label/sandbox" and testing | ||
at "Module:Wikidata label/testcases". | at "Module:Wikidata label/testcases". | ||
Authors and maintainers: | Authors and maintainers: | ||
* User:Jarekt - original version | * 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 | -- 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) | local function getSitelink(item, entity, lang) | ||
if entity and entity.getSitelink then -- if we have entity | if entity and entity.getSitelink then -- if we have entity then use it | ||
return entity:getSitelink(lang .. 'wiki') | return entity:getSitelink(lang .. 'wiki') | ||
else -- if no entity | else -- if no entity then use different function | ||
return mw.wikibase.sitelink( item, lang .. ' | 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 | 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 | end | ||
--============================================= | |||
--=== External functions ====================== | |||
--============================================= | |||
local p = {} | local p = {} | ||
--====================================================================== | |||
--=== API functions for use from other Scribunto modules =============== | |||
--====================================================================== | |||
--[[ | --[[ | ||
36行目: | 247行目: | ||
Inputs: | Inputs: | ||
1: item - wikidata's item's q- | 1: item - wikidata's item's q-id or entity class | ||
2: lang - desired language of the label | 2: lang - desired language of the label | ||
3: link_type - link style. Possible values: "wikipedia", " | 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), | 4: capitalization - can be "uc" (upper case), "lc" (lower case), "ucfirst" (upper case for the first letter), | ||
"lcfirst" (lower case for the first letter) | "lcfirst" (lower case for the first letter), or 'none' (default) | ||
Error Handling: | Error Handling: | ||
Bad q- | Bad q-id will result in displayed error | ||
]] | ]] | ||
function p._getLabel(item, lang, link_type, capitalization, show_id) | function p._getLabel(item, lang, link_type, capitalization, show_id) | ||
local entity, s, link, label, language | local entity, s, link, label, language, desc | ||
-- clean up the input parameters | -- clean up the input parameters | ||
if type(item)~='string' then -- "item" is not a q- | if type(item) ~= 'string' then -- "item" is not a q-id | ||
entity = item -- "item" must be the entity | entity = item -- "item" must be the entity | ||
item = entity.id -- look-up q- | item = entity.id -- look-up q-id | ||
elseif tonumber(item) then -- if it is just the number | elseif tonumber(item) then -- if it is just the number then add "Q" in front | ||
item = 'Q'..item | item = 'Q'..item | ||
end | end | ||
item = | 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 | -- build language fallback list | ||
lang = | lang = mw.ustring.lower(lang) | ||
local langList = mw.language.getFallbacksFor(lang) | local langList = mw.language.getFallbacksFor(lang) | ||
table.insert(langList, 1, lang) | table.insert(langList, 1, lang) | ||
-- get label (visible part of the link) | -- get label (visible part of the link) | ||
for _, language in ipairs(langList) do -- loop over language fallback list looking for label in the specific language | 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 | ||
end | end | ||
if not label then -- no labels found, so just show the q- | 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 | label = item | ||
elseif show_id then -- add id | elseif show_id then -- add id | ||
show_id = yesno(show_id,false) | show_id = yesno(show_id,false) | ||
if show_id then | if show_id then | ||
local | local wordsep = getMessage('Word-separator', lang, ' ') | ||
local | 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>" | label = label .. wordsep .. "<small>" .. id .. "</small>" | ||
end | end | ||
end | end | ||
label = apply_capitalization(label, capitalization, lang) | |||
-- | -- look for description | ||
if | 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 | |||
if | |||
end | end | ||
end | end | ||
else | |||
desc = mw.wikibase.getDescription(item) | |||
end | end | ||
if | 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 | end | ||
-- return the results | -- return the results | ||
if | if link_type == '-' then | ||
return | return label -- return just the label | ||
else | else | ||
return label -- return | link = getLink(link_type, item, entity, langList) | ||
return '[[' .. link .. '|' .. label .. ']]' -- return link | |||
end | end | ||
end | end | ||
--[[ | --[[------------------------------------------------------------------------------- | ||
_sitelinks | |||
This function returns a | 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. | function p._sitelinks(item, project) | ||
local | local entity, sitelink | ||
if | -- 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 | 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 | end | ||
return linkTable | |||
return | |||
end | end | ||
--[[ | --[[---------------------------------------------------------------- | ||
_aliases | |||
This function returns a table of | This function returns a table of aliases for a single language | ||
Inputs: | Inputs: | ||
1: item - wikidata's item's q- | 1: item - wikidata's item's q-id or entity class | ||
2: | 2: lang - language code, like 'en' or 'de' | ||
Output: | Output: | ||
Table with language fields | Table of aliases with language fields | ||
]] | ]] | ||
function p. | function p._aliases(item, lang) | ||
local | local entity | ||
if type(item) == 'string' then -- "item" is a q-id | |||
if type(item)=='string' then -- "item" is a q- | entity = mw.wikibase.getEntity(item) | ||
entity = mw.wikibase.getEntity(item) | |||
else | else | ||
entity = item -- "item" is the entity | entity = item -- "item" is the entity | ||
end | end | ||
local aliasTable = {} | |||
if entity and entity.aliases then -- See if there is an entity and that is has aliases | |||
local | 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 | end | ||
end | end | ||
return | return aliasTable | ||
end | 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 | sitelinks | ||
254行目: | 469行目: | ||
Inputs: | Inputs: | ||
1: item - wikidata's item's q- | 1: item - wikidata's item's q-id or entity class | ||
2: project - "wikipedia" (or "wiki"), "wikisource", "wikiquote", "wikibooks", "wikinews", "wikiversity", "wikivoyage", "wiktionary", etc. | 2: project - "wikipedia" (or "wiki"), "wikisource", "wikiquote", "wikibooks", | ||
"wikinews", "wikiversity", "wikivoyage", "wiktionary", etc. | |||
Output: | Output: | ||
261行目: | 477行目: | ||
]] | ]] | ||
function p.sitelinks(frame) | function p.sitelinks(frame) | ||
local sitelinks = p._sitelinks( | local args = getArgs(frame) | ||
local | local sitelinks = p._sitelinks(args.item, args.project) | ||
for | local sitelinkList = {} | ||
table.insert( | for lang, sitelink in pairs(sitelinks) do | ||
table.insert(sitelinkList, (lang=='' and sitelink) or (lang .. ':' .. sitelink)) | |||
end | end | ||
return table.concat(sitelinkList, ', ') | |||
end | end | ||
--[[ | --[[---------------------------------------------------------------------------- | ||
aliases | aliases | ||
310行目: | 496行目: | ||
Inputs: | Inputs: | ||
1: item - wikidata's item's q- | 1: item - wikidata's item's q-id or entity class | ||
2: lang - language code, like 'en' or 'de' | 2: lang - language code, like 'en' or 'de' | ||
317行目: | 503行目: | ||
]] | ]] | ||
function p.aliases(frame) | function p.aliases(frame) | ||
return table.concat(p._aliases( | local args = getArgs(frame) | ||
return table.concat(p._aliases(args.item, args.lang), ', ') | |||
end | end | ||
return p | return p |
2023年5月8日 (月) 09:09時点における最新版
このモジュールについての説明文ページを モジュール:Wikidata label/doc に作成できます
--[[
__ __ _ _ __ ___ _ _ _ _ _ _ _
| \/ | ___ __| |_ _| | ___ \ \ / (_) | _(_) __| | __ _| |_ __ _ | | __ _| |__ ___| |
| |\/| |/ _ \ / _` | | | | |/ _ (_) \ /\ / /| | |/ / |/ _` |/ _` | __/ _` | | |/ _` | '_ \ / _ \ |
| | | | (_) | (_| | |_| | | __/_ \ 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