Module:Keywords: Difference between revisions
Render infobox keywords: link only curated concepts, no redlinks |
Strip wiki markup before splitting: whole-list links were rejoining into one redlink |
||
| Line 28: | Line 28: | ||
-- Strip any stored wiki markup, keeping the display text of piped links. | -- Strip any stored wiki markup, keeping the display text of piped links. | ||
-- | |||
-- This must run over the WHOLE field before it is split on commas. Some legacy | |||
-- rows wrap the entire list in a single link -- [[Sw Canis Majoris, Binary | |||
-- stars, Lacy, motion]] -- so splitting first would leave unbalanced fragments | |||
-- ("[[Sw Canis Majoris" ... "motion]]") that rejoin into one giant redlink. | |||
local function plainText(s) | local function plainText(s) | ||
s = s:gsub('%[%[[^%]|]*|([^%]]*)%]%]', '%1') -- [[Target|shown]] -> shown | s = s:gsub('%[%[[^%]|]*|([^%]]*)%]%]', '%1') -- [[Target|shown]] -> shown | ||
s = s:gsub('%[%[([^%]]*)%]%]', '%1') -- [[Target]] -> Target | s = s:gsub('%[%[([^%]]*)%]%]', '%1') -- [[Target]] -> Target | ||
s = s:gsub("'''", ''):gsub("''", '') -- stray bold/italic | s = s:gsub("'''", ''):gsub("''", '') -- stray bold/italic | ||
s = s:gsub('%[%[', ''):gsub('%]%]', '') -- any unbalanced leftovers | |||
return trim(s) | return trim(s) | ||
end | end | ||
| Line 59: | Line 65: | ||
local ok, data = pcall(mw.loadData, 'Module:Keywords/data') | local ok, data = pcall(mw.loadData, 'Module:Keywords/data') | ||
local map = (ok and type(data) == 'table' and data.map) or {} | local map = (ok and type(data) == 'table' and data.map) or {} | ||
-- Remove markup first, then split -- see plainText above for why the order | |||
-- matters. | |||
raw = plainText(raw) | |||
local out, seen = {}, {} | local out, seen = {}, {} | ||
for chunk in mw.text.gsplit(raw, '%s*[,;]%s*') do | for chunk in mw.text.gsplit(raw, '%s*[,;]%s*') do | ||
local display = | local display = trim(chunk) | ||
if display ~= '' then | if display ~= '' then | ||
local k = key(display) | local k = key(display) | ||
Latest revision as of 07:59, 21 July 2026
This module renders the keywords field of {{Infobox paper}}. Recognised concepts
become links; everything else stays plain text.
Why it exists
The keyword fields came from a legacy database in which the terms were already wrapped in
[[...]]. Most of those targets had never been written, so about
8,900 keyword links across the paper pages rendered red. A second problem was subtler: some
targets that did exist were the wrong kind of page. This wiki has papers titled
Gravity, Time, Ether and Cosmology, so the keyword "gravity" pointed at one paper
rather than at the subject. A further eleven terms — Cold Fusion, Anti-Gravity,
Thermodynamics, Black Holes and others — were redirects to
Index of Papers Without Abstracts.
So "link it if the page exists" is not a safe rule on this wiki. The module ignores whatever markup is stored on the page and decides the link itself, against a curated allowlist.
How it behaves
- The whole field has its markup stripped before it is split on commas. Some rows wrap the
entire list in one link ([[Sw Canis Majoris, Binary stars, Lacy, motion]]);
splitting first would leave unbalanced fragments that rejoin into a single giant redlink.
- Matching is case-insensitive and ignores trailing punctuation, so
Aether,
aetherandaether.all resolve alike.
- Duplicate terms within one field are dropped.
- A term links only if it appears in Module:Keywords/data. Creating an article is
therefore not enough to produce a link — the mapping must be added deliberately. That is what keeps papers and books out of the keyword links.
- If the data page is missing or broken the keywords still render, just unlinked. The module is
transcluded on thousands of pages and must not fail loudly.
Adding a keyword
Edit Module:Keywords/data and add a line to the map table:
['relativity principle'] = 'Principle of relativity',
The key must be lower case. The value must be the exact title of a topic, theory or person page. Before adding one, open the target and check it is not a paper or a book — that is the mistake this module exists to prevent.
See also
- Module:Keywords/data — the map itself
- Template:Infobox paper — where it is called
-- Module:Keywords
--
-- Renders the |keywords= field of the infobox templates as a comma-separated
-- list in which recognised concepts link to their article and everything else
-- stays plain text.
--
-- WHY THIS EXISTS
-- The keyword fields were populated from a legacy database in which the terms
-- were already wrapped in [[...]]. Most of those targets have never existed, so
-- roughly 8,900 keyword links across the paper pages rendered red. Worse, a
-- number of the targets that DO exist are papers rather than concepts -- the
-- wiki has papers titled "Gravity", "Time", "Ether" and "Cosmology" -- so the
-- keyword "gravity" pointed at a single paper instead of the subject.
--
-- The fix is to ignore the markup stored on the page and decide the link here,
-- against the curated allowlist in [[Module:Keywords/data]]. A term links only
-- if it is in that list; otherwise it renders as plain text. Adding a page to
-- the wiki is therefore not enough to create a link -- the mapping has to be
-- added deliberately, which is what keeps bad targets out.
--
-- Usage: {{#invoke:Keywords|render|{{{keywords|}}}}}
local p = {}
local function trim(s)
return (s:gsub('^%s*(.-)%s*$', '%1'))
end
-- Strip any stored wiki markup, keeping the display text of piped links.
--
-- This must run over the WHOLE field before it is split on commas. Some legacy
-- rows wrap the entire list in a single link -- [[Sw Canis Majoris, Binary
-- stars, Lacy, motion]] -- so splitting first would leave unbalanced fragments
-- ("[[Sw Canis Majoris" ... "motion]]") that rejoin into one giant redlink.
local function plainText(s)
s = s:gsub('%[%[[^%]|]*|([^%]]*)%]%]', '%1') -- [[Target|shown]] -> shown
s = s:gsub('%[%[([^%]]*)%]%]', '%1') -- [[Target]] -> Target
s = s:gsub("'''", ''):gsub("''", '') -- stray bold/italic
s = s:gsub('%[%[', ''):gsub('%]%]', '') -- any unbalanced leftovers
return trim(s)
end
-- Lookup key: lower case, collapsed whitespace, no trailing punctuation.
local function key(s)
s = trim(s):lower()
s = s:gsub('%s+', ' ')
s = s:gsub('[%.%s]+$', '')
return s
end
function p.render(frame)
local raw = frame.args[1]
if raw == nil or trim(raw) == '' then
local parent = frame:getParent()
raw = parent and parent.args and parent.args.keywords or ''
end
raw = trim(raw or '')
if raw == '' then
return ''
end
-- Load the map defensively: if the data page is missing or broken the
-- keywords must still render, just without links. This template is
-- transcluded on thousands of pages and must not fail loudly.
local ok, data = pcall(mw.loadData, 'Module:Keywords/data')
local map = (ok and type(data) == 'table' and data.map) or {}
-- Remove markup first, then split -- see plainText above for why the order
-- matters.
raw = plainText(raw)
local out, seen = {}, {}
for chunk in mw.text.gsplit(raw, '%s*[,;]%s*') do
local display = trim(chunk)
if display ~= '' then
local k = key(display)
-- Drop duplicates left over from the legacy data (e.g. a term
-- appearing both linked and unlinked in the same field).
if not seen[k] then
seen[k] = true
local target = map[k]
if target and target ~= '' then
if target == display then
table.insert(out, '[[' .. target .. ']]')
else
table.insert(out, '[[' .. target .. '|' .. display .. ']]')
end
else
table.insert(out, display)
end
end
end
end
return table.concat(out, ', ')
end
return p