Zum Inhalt springen

„Modul:WikibaseLink“ – Versionsunterschied

aus AufEinWort, der freien Forschung
KKeine Bearbeitungszusammenfassung
KKeine Bearbeitungszusammenfassung
Markierung: Manuelle Zurücksetzung
 
(36 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
local p = {}
local p = {}


-- === KONFIG ===
------------------------------------------------------------------
local P_ITEM2LEX = 'P9'  -- Item (Q) -> Lexem (L)  „hat Wort“
-- Modul:WikibaseLink – überarbeitete Fassung (sichtbare Ausgabe)
local P_LEX2ITEM = 'P8'  -- Lexem (L) -> Item (Q)  „hat Eintrag“
-- Ziel:
local NS_CATEGORY_ID = 14  -- Einordnung aliasiert Kategorie (numerisch 14)
-- 1) Links zu Q / P / L ausgeben (Special:EntityPage/...)
-- 2) Für P und L passende Kurzbeschreibung ermitteln
--    - P: Wikibase-Beschreibung
--    - L: lexikalische Kategorie (Label)
-- 3) OPTIONAL: Kurzbeschreibung als {{SHORTDESC:...}} bereitstellen
--    (über p.shortdesc), aber NICHT mehr automatisch im sichtbaren
--    Seitentext ausgeben.
-- 4) Für P und L sichtbares Muster:
--      ID Label (Kurzbeschreibung)
--      z.B.  L2 Dingwort (Wortewort)
--
-- Nutzung (direkt per #invoke):
--  {{#invoke:WikibaseLink|link|entity=Q123}}
--  {{#invoke:WikibaseLink|link|P=P31}}
--  {{#invoke:WikibaseLink|link|L=L1}}
--  {{#invoke:WikibaseLink|shortdesc|P=P31|mode=P}}
--  {{#invoke:WikibaseLink|shortdesc|L=L1|mode=L}}
--  {{#invoke:WikibaseLink|linkAndShortdesc|L=L1|mode=L}}
--
-- Empfohlene Vorlage:WikibaseLink (Beispiel im Chat), die intern
--   {{#invoke:WikibaseLink|linkAndShortdesc|entity={{{1|}}}|mode={{{mode|auto}}}|label={{{label|}}}}}
-- aufruft.
--
-- Parameter (alle Funktionen):
--  |entity= Q123 / P31 / L1 (allgemein)
--  |Q=       explizit Item
--  |P=      explizit Property
--   |L=      explizit Lexem
--  |mode=    'auto' (Standard) / 'Q' / 'P' / 'L'
--             steuert v. a. die Kurzbeschreibung.
--  |label=   optionaler Basis-Linktext statt Wikibase-Label/Lemma
--
-- Fallback:
--  Wenn keine Entity per Parameter angegeben ist, versucht
--   das Modul mw.wikibase.getEntityIdForCurrentPage().
------------------------------------------------------------------


-- == Hilfsfunktionen ==
-- Hilfsfunktion: Trim
local function entityLink(id, label)
local function trim(s)
   if not id then return nil end
  if type(s) ~= 'string' then
   return string.format('[[Special:EntityPage/%s|%s]]', id, label or id)
    return s
  end
  return (s:gsub('^%s+', ''):gsub('%s+$', ''))
end
 
-- Hilfsfunktion: Argumente nur aus dem aktuellen frame holen
-- (keine Parent-Args, damit Aufrufe wie L=L2 zuverlässig wirken)
local function getArgs(frame)
  local args = {}
  if not frame then
    return args
  end
 
  for k, v in pairs(frame.args or {}) do
    if v ~= '' and v ~= nil then
      args[k] = v
    end
  end
 
  return args
end
 
-- Entity-Typ bestimmen
local function getEntityType(id)
   if type(id) ~= 'string' then
    return nil
  end
   if id:match('^Q%d+$') then
    return 'item'    -- Q
  elseif id:match('^P%d+$') then
    return 'property' -- P
  elseif id:match('^L%d+$') then
    return 'lexeme'  -- L
  end
  return nil
end
 
local function detectEntityId(args)
  -- Kandidaten aus diesem Aufruf in fester Priorität;
  -- wir bauen die Liste ohne Nil-Werte, damit ipairs
  -- nicht durch führende Nil-Einträge vorzeitig abbricht.
 
  -- 1) explizite Typen (L, P, Q)
  local rawL = args.L or args.lexeme or args.l
  local rawP = args.P or args.p
  local rawQ = args.Q or args.q
 
  -- 2) generische entity/id
  local rawEntity = args.entity or args.Entity
  local rawId = args.id or args.ID
 
  -- 3) Positionsparameter (1)
  local rawPos = args[1]
 
  local ordered = {}
  local function push(v)
    if v ~= nil and v ~= '' then
      table.insert(ordered, v)
    end
  end
 
  -- Reihenfolge der Priorität
  push(rawL)
  push(rawP)
  push(rawQ)
  push(rawEntity)
  push(rawId)
  push(rawPos)
 
for _, v in ipairs(ordered) do
  local s = trim(tostring(v))
  if s ~= '' then
    -- Direkte ID (Q123 / P31 / L2)
    local id = s:match('^[QPL]%d+$')
    -- Oder mit Namensraum-Präfix (z.B. "Lexeme:L2", "Property:P31", "Item:Q6")
    if not id then
      id = s:match('^.-:([QPL]%d+)$')
    end
    if id then
      return id
    end
  end
end
end


local function isQ(id) return type(id)=='string' and id:match('^Q%d+$') end
-- Fallback: Entity zur aktuellen Seite (z. B. Q bei Sitelink)
local function isL(id) return type(id)=='string' and id:match('^L%d+$') end
local ok, currentId = pcall(mw.wikibase.getEntityIdForCurrentPage)
if ok and type(currentId) == 'string' and currentId:match('^[QPL]%d+$') then
  return currentId
end


local function getCurrentTitleParts()
 
  local t = mw.title.getCurrentTitle()
   return nil
   return {
    nsId = t.namespace,
    nsText = t.nsText,          -- z. B. "Einordnung"
    text = t.text,              -- Seitentitel ohne Präfix (z. B. "Dingwort")
    prefixed = t.prefixedText,  -- mit Präfix
  }
end
end


-- Beste Statements einer Property (vom Item)
-- Label-/Lemma-Ermittlung für eine Entity (ohne Wikitext)
local function getBestValues(qid, pid)
local function buildEntityLabel(entityId)
   if not (qid and isQ(qid) and pid) then return {} end
   if not entityId then
   local vals = mw.wikibase.getBestStatements(qid, pid) or {}
    return nil
   local out = {}
  end
   for _, s in ipairs(vals) do
 
     local snak = s.mainsnak
   local etype = getEntityType(entityId)
     if snak and snak.datavalue and snak.datavalue.value then
 
       local v = snak.datavalue.value
   -- Spezieller Fall: Lexeme → Lemma in Inhalts-Sprache
      if type(v) == 'table' and v['id'] then
   if etype == 'lexeme' then
         table.insert(out, v.id)              -- Wikibase-EntityId (für entity-typed props)
     local ok, entity = pcall(mw.wikibase.getEntity, entityId)
     if ok and type(entity) == 'table' and type(entity.lemmas) == 'table' then
       local contentLang = 'de'
      if mw.getContentLanguage then
        local cl = mw.getContentLanguage()
        if cl and cl.getCode then
          contentLang = cl:getCode()
        end
      end
      local lemma
      if entity.lemmas[contentLang] and entity.lemmas[contentLang].value then
         lemma = entity.lemmas[contentLang].value
       else
       else
         table.insert(out, tostring(v))      -- sonst String/Identifier
         -- Fallback: irgendein Lemma
        for _, obj in pairs(entity.lemmas) do
          if type(obj) == 'table' and obj.value then
            lemma = obj.value
            break
          end
        end
      end
       if lemma and lemma ~= '' then
        return lemma
       end
       end
     end
     end
   end
   end
   return out
 
  -- Standard: Label über Wikibase
  local ok, fetched = pcall(mw.wikibase.getLabel, entityId)
  if ok and fetched and fetched ~= '' then
    return fetched
  end
 
   return entityId
end
end


-- Q im aktuellen Kontext ermitteln:
-- Link auf eine Entity bauen (mit optionalem Label)
-- 1) Direkter Sitelink (auf Artikelseiten etc.)
local function buildEntityLink(entityId, labelOverride)
-- 2) Auf Einordnung:-Seiten: versuche Sitelink für den zugrunde liegenden Artikelnamen
   if not entityId then
-- 3) Fallback: exakte Labelsuche (de)
    return ''
local function resolveQForContext()
  end
   local parts = getCurrentTitleParts()


   -- 1) Direkter Sitelink (nur wenn aktuelle Seite Sitelink-Ziel ist)
   local label = labelOverride
   local q = mw.wikibase.getEntityIdForCurrentPage()
  if not label or label == '' then
   if isQ(q) then return q end
    label = buildEntityLabel(entityId) or entityId
  end
 
  return string.format('[[Special:EntityPage/%s|%s]]', entityId, label)
end
 
 
-- Kurzbeschreibung für eine Entity bestimmen
-- mode:
--  'auto' / nil ->
--      Q (item)     → Wikibase-Beschreibung
--      P (property)  → Wikibase-Beschreibung
--      L (lexeme)   → Label der lexikalischen Kategorie
--   'Q' / 'item'      → Wikibase-Beschreibung
--  'P' / 'property'  → Wikibase-Beschreibung
--  'L' / 'lexeme'    → Label der lexikalischen Kategorie
local function buildShortdescText(entityId, mode)
   if not entityId then
    return nil
  end


   -- 2) Kategorie/Einordnung → nutze Artikeltitel ohne Präfix
   local etype = getEntityType(entityId)
   if parts.nsId == NS_CATEGORY_ID and parts.text and parts.text ~= '' then
   if not etype then
     local q2 = mw.wikibase.getEntityIdForTitle(parts.text)
    return nil
     if isQ(q2) then return q2 end
  end
 
  mode = mode and tostring(mode):lower() or 'auto'
 
  if mode == 'auto' then
    if etype == 'item' then
      mode = 'q'
     elseif etype == 'property' then
      mode = 'p'
     elseif etype == 'lexeme' then
      mode = 'l'
    end
   end
   end


   -- 3) Fallback exakte Labelsuche in de (nur wenn ein klarer Kandidat)
   -- Q und P: Wikibase-Beschreibung verwenden
   local title = parts.text or parts.prefixed
  if mode == 'q' or mode == 'item'
   if not title or title == '' then return nil end
    or mode == 'p' or mode == 'property'
   local ok, results = pcall(mw.wikibase.searchEntities, title, 'de')
  then
   if ok and type(results) == 'table' then
    local ok, desc = pcall( mw.wikibase.getDescription, entityId )
     local candidate
    if ok and type( desc ) == 'string' and desc ~= '' then
    for _, r in ipairs(results) do
      return desc
       if r and r.id and isQ(r.id) and r.display and r.display.label
    end
        and r.display.label.value == title then
    return nil
         if candidate then
 
          candidate = nil; break  -- Mehrdeutig → kein eindeutiger Treffer
   elseif mode == 'l' or mode == 'lexeme' then
         end
    -- Lexem: lexikalische Kategorie → Label dieser Kategorie
        candidate = r.id
    local ok, entity = pcall( mw.wikibase.getEntity, entityId )
    if not ok or type( entity ) ~= 'table' then
      return nil
    end
 
    local lc = entity.lexicalCategory
    if type( lc ) == 'table' then
      lc = lc.id or lc.value
    end
 
    if type( lc ) ~= 'string' or lc == '' then
      return nil
    end
 
    local okLabel, label = pcall( mw.wikibase.getLabel, lc )
    if okLabel and type( label ) == 'string' and label ~= '' then
      return label
    else
      return lc
    end
  end
 
  return nil
end
 
-- Gloss für einen Lexem-Sinn ermitteln (z.B. S=1, S='S1', S='L1-S1')
local function getLexemeSenseGloss(entityId, senseParam)
   if not entityId or not senseParam or senseParam == '' then
    return nil
  end
 
  local etype = getEntityType(entityId)
  if etype ~= 'lexeme' then
    return nil
  end
 
  -- Sense-ID kanonisieren
  local s = tostring(senseParam)
  local senseId
 
  -- Vollständige ID: L123-S4
  if s:match('^L%d+-S%d+$') then
    senseId = s
  else
    -- „S4“ oder „4“ → L123-S4
    local num = s:match('^S(%d+)$') or s:match('^(%d+)$')
    if not num then
      return nil
    end
    senseId = string.format('%s-S%s', entityId, num)
  end
 
  -- Lexem-Entity holen
   local ok, entity = pcall( mw.wikibase.getEntity, entityId )
   if not ok or type(entity) ~= 'table' or type(entity.senses) ~= 'table' then
     return nil
  end
 
  -- Ziel-Sense in entity.senses suchen
  local targetSense = nil
  for key, sense in pairs(entity.senses) do
    if type(sense) == 'table' then
       local sid = sense.id or key
      if sid == senseId then
        targetSense = sense
        break
      end
    end
  end
 
  if not targetSense or type(targetSense.glosses) ~= 'table' then
    return nil
  end
 
  -- Inhalts-Sprache bestimmen
  local contentLang = 'de'
  if mw.getContentLanguage then
    local cl = mw.getContentLanguage()
    if cl and cl.getCode then
      contentLang = cl:getCode()
    end
  end
 
  -- Gloss in Inhalts-Sprache bevorzugen, sonst irgendeinen
  local gloss
  local g = targetSense.glosses[contentLang]
  if g and type(g) == 'table' and g.value then
    gloss = g.value
  else
    for _, obj in pairs(targetSense.glosses) do
      if type(obj) == 'table' and obj.value then
         gloss = obj.value
         break
       end
       end
     end
     end
    if isQ(candidate) then return candidate end
   end
   end
  if gloss and gloss ~= '' then
    return gloss
  end
   return nil
   return nil
end
end


-- L im aktuellen Kontext ermitteln:
-- Form (Wortform) für ein Lexem ermitteln (z.B. F=1, F='F1', F='L2-F1')
-- (a) expliziter Parameter |L=, sonst
local function getLexemeFormRepresentation(entityId, formParam)
-- (b) über Item->Lexem (P_ITEM2LEX),
   if not entityId or not formParam or formParam == '' then
-- (c) Fallback exakte Labelsuche (de)
    return nil
local function resolveLsForContext(frame, qForContext)
   end
   -- (a) Override
 
   local args = frame and frame.args or {}
   local etype = getEntityType(entityId)
   local lexArg = args.L or args.lexeme or args[1]
   if etype ~= 'lexeme' then
   if lexArg and isL(tostring(lexArg)) then
     return nil
     return { tostring(lexArg) }
   end
   end


   -- (b) über Item-Property
   -- Form-ID kanonisieren
   if isQ(qForContext) then
  local s = tostring(formParam)
     local ls = getBestValues(qForContext, P_ITEM2LEX)
  local formId
     local out = {}
 
    for _, id in ipairs(ls) do
  -- Vollständige ID: L123-F4
      if isL(id) then table.insert(out, id) end
   if s:match('^L%d+-F%d+$') then
     formId = s
  else
    -- „F4“ oder „4“ → L123-F4
     local num = s:match('^F(%d+)$') or s:match('^(%d+)$')
    if not num then
      return nil
     end
     end
     if #out > 0 then return out end
     formId = string.format('%s-F%s', entityId, num)
  end
 
  -- Lexem-Entity holen
  local ok, entity = pcall( mw.wikibase.getEntity, entityId )
  if not ok or type(entity) ~= 'table' or type(entity.forms) ~= 'table' then
    return nil
   end
   end


   -- (c) Fallback exakte Labelsuche
   -- Ziel-Form in entity.forms suchen
   local parts = getCurrentTitleParts()
   local targetForm = nil
   local title = parts.text or parts.prefixed
   for key, form in pairs(entity.forms) do
  if not title or title == '' then return {} end
    if type(form) == 'table' then
  local ok, results = pcall(mw.wikibase.searchEntities, title, 'de')
      local fid = form.id or key
  if ok and type(results) == 'table' then
       if fid == formId then
    local out = {}
         targetForm = form
    for _, r in ipairs(results) do
        break
       if r and isL(r.id) and r.display and r.display.label
        and r.display.label.value == title then
         table.insert(out, r.id)
       end
       end
     end
     end
    return out
   end
   end
   return {}
 
   if not targetForm or type(targetForm.representations) ~= 'table' then
    return nil
  end
 
  -- Inhalts-Sprache bestimmen
  local contentLang = 'de'
  if mw.getContentLanguage then
    local cl = mw.getContentLanguage()
    if cl and cl.getCode then
      contentLang = cl:getCode()
    end
  end
 
  -- Wortform in Inhalts-Sprache bevorzugen, sonst irgendeine
  local formText
  local r = targetForm.representations[contentLang]
  if r and type(r) == 'table' and r.value then
    formText = r.value
  else
    for _, obj in pairs(targetForm.representations) do
      if type(obj) == 'table' and obj.value then
        formText = obj.value
        break
      end
    end
  end
 
  if formText and formText ~= '' then
    return formText
  end
 
  return nil
end
end


-- === Öffentliche Ausgaben ===


function p.itemLink(frame)
------------------------------------------------------------------
   local q = resolveQForContext()
-- Öffentliche Funktionen
   if not isQ(q) then return '' end
------------------------------------------------------------------
   return entityLink(q, 'Eintrag: ' .. q)
 
-- 1) Nur Link ausgeben (ohne sichtbaren Untertitel)
--    Q:  Label
--    P/L: ID Label
function p.link(frame)
   local args = getArgs(frame)
  local entityId = detectEntityId(args)
   if not entityId then
    return ''
  end
 
  local etype = getEntityType(entityId)
  local baseLabel = args.label or args.text or buildEntityLabel(entityId) or entityId
 
  local linkLabel
  if etype == 'item' then
    -- Q: nur das Label
    linkLabel = baseLabel
  else
    -- P/L: ID + Label
    linkLabel = string.format('%s %s', entityId, baseLabel)
  end
 
  -- Sicherheit: keine Zeilenumbrüche im Linktext
   linkLabel = trim(linkLabel):gsub('\n', ' ')
 
  return buildEntityLink(entityId, linkLabel)
end
end


function p.lexemeLink(frame)
-- 2) Nur SHORTDESC-Wikitext zurückgeben (falls sinnvoll)
   local q = resolveQForContext() -- ermöglicht (b)
--    Hinweis: Ob die ShortDescription-Extension {{SHORTDESC:...}}
   local ls = resolveLsForContext(frame, q)
--    aus Vorlagen heraus verarbeitet, hängt von der lokalen
   if not ls or #ls == 0 then return '' end
--    Konfiguration ab.
   -- mehrere Lexeme ggf. joinen
function p.shortdesc(frame)
   local links = {}
   local args = getArgs(frame)
   for _, lid in ipairs(ls) do
  local entityId = detectEntityId(args)
     table.insert(links, entityLink(lid, 'Wortlaut: ' .. lid))
  if not entityId then
    return ''
  end
 
   local mode = args.mode or args.typ or args.type
  local text = buildShortdescText(entityId, mode)
   if not text or text == '' then
    return ''
  end
 
  text = trim(text):gsub('\n', ' ')
 
   -- SHORTDESC als Parserfunktion aufrufen, so als stünde
  -- {{SHORTDESC:text}} im Quelltext.
   local ok, result = pcall(function()
    -- Entspricht {{SHORTDESC:text}}
    return frame:callParserFunction('SHORTDESC', text)
   end)
 
  if not ok then
     -- Falls die Parserfunktion nicht verfügbar ist, lieber
    -- still scheitern, damit Seiten nicht kaputtgehen.
    return ''
   end
   end
   return table.concat(links, ' · ')
 
   -- SHORTDESC gibt in der Regel nichts Sichtbares zurück.
  -- Wir geben das Ergebnis (typisch leer) zurück, so dass
  -- der #invoke-Aufruf im Seitentext nichts anzeigt.
  return result or ''
end
end


function p.both(frame)
 
   local qOut = p.itemLink(frame)
-- 3) Link mit Untertitel kombinieren
   local lOut = p.lexemeLink(frame)
--    Muster:
   if (qOut == '' or not qOut) and (lOut == '' or not lOut) then
--      Lexem ohne Sinn/Form:  [[.../L2|L2 Dingwort]] (Wortewort)
     return 'kein WikibaseLink'
--      Lexem mit Sinn (S):    [[.../L2|L2 Dingwort]] (Wortewort)
  elseif qOut ~= '' and lOut ~= '' then
--                                Ding benennendes Wort
     return qOut .. ' · ' .. lOut
--      Lexem mit Sinn + Form:  [[.../L2|L2 Dingwort]] (Wortewort)
--                                Ding benennendes Wort
--                                Ding benennende Wörter  (z.B. F1)
--      Item/Property:          [[.../Q6|Q6 Dingwort]] (…Beschreibung…)
function p.subtitle(frame)
   local args = getArgs(frame)
  local entityId = detectEntityId(args)
  if not entityId then
    return ''
  end
 
  local mode  = args.mode or args.typ or args.type
  local etype = getEntityType(entityId)
 
  -- Basislabel: explizit oder aus Wikibase/Lexem-Lemma
   local baseLabel = args.label or args.text or buildEntityLabel(entityId) or entityId
 
  -- Untertitel: Beschreibung (Q/P) bzw. Wortart (L)
  local desc = buildShortdescText(entityId, mode)
  if desc ~= nil then
    desc = trim(desc):gsub('\n', ' ')
  end
 
  ----------------------------------------------------------------
  -- Sinn (Sense) für Lexeme optional bestimmen (S=…)
  ----------------------------------------------------------------
  local senseParam = args.S or args.s or args.sense
  local senseGloss = nil
   if senseParam and etype == 'lexeme' then
    senseGloss = getLexemeSenseGloss(entityId, senseParam)
    if senseGloss ~= nil then
      senseGloss = trim(senseGloss):gsub('\n', ' ')
    end
  end
 
  ----------------------------------------------------------------
  -- Form (Wortform) für Lexeme optional bestimmen (F=…)
  ----------------------------------------------------------------
  local formParam = args.F or args.f or args.form or args.Form
  local formText = nil
  if formParam and etype == 'lexeme' then
    formText = getLexemeFormRepresentation(entityId, formParam)
    if formText ~= nil then
      formText = trim(formText):gsub('\n', ' ')
    end
  end
 
  ----------------------------------------------------------------
  -- Steuerung, ob der Untertitel im sichtbaren Text stehen soll
  -- subtitle: ja/1 (Standard) → sichtbar
  --          nein/0          → unsichtbar
  ----------------------------------------------------------------
  local subtitleParam = args.subtitle
  local showSubtitle  = true  -- Default: ja
 
  if subtitleParam ~= nil and subtitleParam ~= '' then
    local s = tostring(subtitleParam):lower()
    if s == 'nein' or s == '0' then
      showSubtitle = false
    elseif s == 'ja' or s == '1' then
      showSubtitle = true
    end
  end
 
  -- SHORTDESC als Seiteneigenschaft setzen (für Suche), falls vorhanden
  -- (weiterhin nur aus der Kurzbeschreibung, nicht aus Sinn/Form)
  if desc and desc ~= '' then
    pcall(function()
      frame:callParserFunction('SHORTDESC', desc)
    end)
  end
 
  -- Sichtbarer Link-Teil: für Q/P/L immer "ID Label"
  local visibleLabel = string.format('%s %s', entityId, baseLabel)
  visibleLabel = trim(visibleLabel):gsub('\n', ' ')
 
  -- Link nur um ID + Label
  local linkText = buildEntityLink(entityId, visibleLabel)
 
  -- Wenn keine sichtbaren Untertitel gewünscht → nur Link zurückgeben
  if not showSubtitle then
     return linkText
  end
 
  -- Spezielles Layout für Lexeme: Zeile 1 + optional Zeile 2/3
  if etype == 'lexeme' then
    local line1
    if desc and desc ~= '' then
      line1 = string.format('%s (%s)', linkText, desc)
    else
      line1 = linkText
    end
 
    local lines = { line1 }
 
    if senseGloss and senseGloss ~= '' then
      table.insert(lines, senseGloss)
    end
 
    if formText and formText ~= '' then
      table.insert(lines, formText)
    end
 
    -- Zeilen mit <br /> verbinden
     return table.concat(lines, '<br />')
  end
 
  -- Standardfall: Q / P → Link + (Beschreibung)
  if desc and desc ~= '' then
    return string.format('%s (%s)', linkText, desc)
   else
   else
     return (qOut ~= '' and qOut) or lOut
     return linkText
   end
   end
end
end


return p
return p

Aktuelle Version vom 14. Dezember 2025, 11:05 Uhr

Vorlagenprogrammierung Diskussionen Lua Unterseiten
Modul Deutsch

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus


local p = {}

------------------------------------------------------------------
-- Modul:WikibaseLink – überarbeitete Fassung (sichtbare Ausgabe)
-- Ziel:
-- 1) Links zu Q / P / L ausgeben (Special:EntityPage/...)
-- 2) Für P und L passende Kurzbeschreibung ermitteln
--    - P: Wikibase-Beschreibung
--    - L: lexikalische Kategorie (Label)
-- 3) OPTIONAL: Kurzbeschreibung als {{SHORTDESC:...}} bereitstellen
--    (über p.shortdesc), aber NICHT mehr automatisch im sichtbaren
--    Seitentext ausgeben.
-- 4) Für P und L sichtbares Muster:
--      ID Label (Kurzbeschreibung)
--      z.B.  L2 Dingwort (Wortewort)
--
-- Nutzung (direkt per #invoke):
--   {{#invoke:WikibaseLink|link|entity=Q123}}
--   {{#invoke:WikibaseLink|link|P=P31}}
--   {{#invoke:WikibaseLink|link|L=L1}}
--   {{#invoke:WikibaseLink|shortdesc|P=P31|mode=P}}
--   {{#invoke:WikibaseLink|shortdesc|L=L1|mode=L}}
--   {{#invoke:WikibaseLink|linkAndShortdesc|L=L1|mode=L}}
--
-- Empfohlene Vorlage:WikibaseLink (Beispiel im Chat), die intern
--   {{#invoke:WikibaseLink|linkAndShortdesc|entity={{{1|}}}|mode={{{mode|auto}}}|label={{{label|}}}}}
-- aufruft.
--
-- Parameter (alle Funktionen):
--   |entity=  Q123 / P31 / L1 (allgemein)
--   |Q=       explizit Item
--   |P=       explizit Property
--   |L=       explizit Lexem
--   |mode=    'auto' (Standard) / 'Q' / 'P' / 'L'
--             steuert v. a. die Kurzbeschreibung.
--   |label=   optionaler Basis-Linktext statt Wikibase-Label/Lemma
--
-- Fallback:
--   Wenn keine Entity per Parameter angegeben ist, versucht
--   das Modul mw.wikibase.getEntityIdForCurrentPage().
------------------------------------------------------------------

-- Hilfsfunktion: Trim
local function trim(s)
  if type(s) ~= 'string' then
    return s
  end
  return (s:gsub('^%s+', ''):gsub('%s+$', ''))
end

-- Hilfsfunktion: Argumente nur aus dem aktuellen frame holen
-- (keine Parent-Args, damit Aufrufe wie L=L2 zuverlässig wirken)
local function getArgs(frame)
  local args = {}
  if not frame then
    return args
  end

  for k, v in pairs(frame.args or {}) do
    if v ~= '' and v ~= nil then
      args[k] = v
    end
  end

  return args
end

-- Entity-Typ bestimmen
local function getEntityType(id)
  if type(id) ~= 'string' then
    return nil
  end
  if id:match('^Q%d+$') then
    return 'item'    -- Q
  elseif id:match('^P%d+$') then
    return 'property' -- P
  elseif id:match('^L%d+$') then
    return 'lexeme'  -- L
  end
  return nil
end

local function detectEntityId(args)
  -- Kandidaten aus diesem Aufruf in fester Priorität;
  -- wir bauen die Liste ohne Nil-Werte, damit ipairs
  -- nicht durch führende Nil-Einträge vorzeitig abbricht.

  -- 1) explizite Typen (L, P, Q)
  local rawL = args.L or args.lexeme or args.l
  local rawP = args.P or args.p
  local rawQ = args.Q or args.q

  -- 2) generische entity/id
  local rawEntity = args.entity or args.Entity
  local rawId = args.id or args.ID

  -- 3) Positionsparameter (1)
  local rawPos = args[1]

  local ordered = {}
  local function push(v)
    if v ~= nil and v ~= '' then
      table.insert(ordered, v)
    end
  end

  -- Reihenfolge der Priorität
  push(rawL)
  push(rawP)
  push(rawQ)
  push(rawEntity)
  push(rawId)
  push(rawPos)

for _, v in ipairs(ordered) do
  local s = trim(tostring(v))
  if s ~= '' then
    -- Direkte ID (Q123 / P31 / L2)
    local id = s:match('^[QPL]%d+$')
    -- Oder mit Namensraum-Präfix (z.B. "Lexeme:L2", "Property:P31", "Item:Q6")
    if not id then
      id = s:match('^.-:([QPL]%d+)$')
    end
    if id then
      return id
    end
  end
end

-- Fallback: Entity zur aktuellen Seite (z. B. Q bei Sitelink)
local ok, currentId = pcall(mw.wikibase.getEntityIdForCurrentPage)
if ok and type(currentId) == 'string' and currentId:match('^[QPL]%d+$') then
  return currentId
end


  return nil
end

-- Label-/Lemma-Ermittlung für eine Entity (ohne Wikitext)
local function buildEntityLabel(entityId)
  if not entityId then
    return nil
  end

  local etype = getEntityType(entityId)

  -- Spezieller Fall: Lexeme → Lemma in Inhalts-Sprache
  if etype == 'lexeme' then
    local ok, entity = pcall(mw.wikibase.getEntity, entityId)
    if ok and type(entity) == 'table' and type(entity.lemmas) == 'table' then
      local contentLang = 'de'
      if mw.getContentLanguage then
        local cl = mw.getContentLanguage()
        if cl and cl.getCode then
          contentLang = cl:getCode()
        end
      end
      local lemma
      if entity.lemmas[contentLang] and entity.lemmas[contentLang].value then
        lemma = entity.lemmas[contentLang].value
      else
        -- Fallback: irgendein Lemma
        for _, obj in pairs(entity.lemmas) do
          if type(obj) == 'table' and obj.value then
            lemma = obj.value
            break
          end
        end
      end
      if lemma and lemma ~= '' then
        return lemma
      end
    end
  end

  -- Standard: Label über Wikibase
  local ok, fetched = pcall(mw.wikibase.getLabel, entityId)
  if ok and fetched and fetched ~= '' then
    return fetched
  end

  return entityId
end

-- Link auf eine Entity bauen (mit optionalem Label)
local function buildEntityLink(entityId, labelOverride)
  if not entityId then
    return ''
  end

  local label = labelOverride
  if not label or label == '' then
    label = buildEntityLabel(entityId) or entityId
  end

  return string.format('[[Special:EntityPage/%s|%s]]', entityId, label)
end


-- Kurzbeschreibung für eine Entity bestimmen
-- mode:
--   'auto' / nil ->
--       Q (item)      → Wikibase-Beschreibung
--       P (property)  → Wikibase-Beschreibung
--       L (lexeme)    → Label der lexikalischen Kategorie
--   'Q' / 'item'      → Wikibase-Beschreibung
--   'P' / 'property'  → Wikibase-Beschreibung
--   'L' / 'lexeme'    → Label der lexikalischen Kategorie
local function buildShortdescText(entityId, mode)
  if not entityId then
    return nil
  end

  local etype = getEntityType(entityId)
  if not etype then
    return nil
  end

  mode = mode and tostring(mode):lower() or 'auto'

  if mode == 'auto' then
    if etype == 'item' then
      mode = 'q'
    elseif etype == 'property' then
      mode = 'p'
    elseif etype == 'lexeme' then
      mode = 'l'
    end
  end

  -- Q und P: Wikibase-Beschreibung verwenden
  if mode == 'q' or mode == 'item'
    or mode == 'p' or mode == 'property'
  then
    local ok, desc = pcall( mw.wikibase.getDescription, entityId )
    if ok and type( desc ) == 'string' and desc ~= '' then
      return desc
    end
    return nil

  elseif mode == 'l' or mode == 'lexeme' then
    -- Lexem: lexikalische Kategorie → Label dieser Kategorie
    local ok, entity = pcall( mw.wikibase.getEntity, entityId )
    if not ok or type( entity ) ~= 'table' then
      return nil
    end

    local lc = entity.lexicalCategory
    if type( lc ) == 'table' then
      lc = lc.id or lc.value
    end

    if type( lc ) ~= 'string' or lc == '' then
      return nil
    end

    local okLabel, label = pcall( mw.wikibase.getLabel, lc )
    if okLabel and type( label ) == 'string' and label ~= '' then
      return label
    else
      return lc
    end
  end

  return nil
end

-- Gloss für einen Lexem-Sinn ermitteln (z.B. S=1, S='S1', S='L1-S1')
local function getLexemeSenseGloss(entityId, senseParam)
  if not entityId or not senseParam or senseParam == '' then
    return nil
  end

  local etype = getEntityType(entityId)
  if etype ~= 'lexeme' then
    return nil
  end

  -- Sense-ID kanonisieren
  local s = tostring(senseParam)
  local senseId

  -- Vollständige ID: L123-S4
  if s:match('^L%d+-S%d+$') then
    senseId = s
  else
    -- „S4“ oder „4“ → L123-S4
    local num = s:match('^S(%d+)$') or s:match('^(%d+)$')
    if not num then
      return nil
    end
    senseId = string.format('%s-S%s', entityId, num)
  end

  -- Lexem-Entity holen
  local ok, entity = pcall( mw.wikibase.getEntity, entityId )
  if not ok or type(entity) ~= 'table' or type(entity.senses) ~= 'table' then
    return nil
  end

  -- Ziel-Sense in entity.senses suchen
  local targetSense = nil
  for key, sense in pairs(entity.senses) do
    if type(sense) == 'table' then
      local sid = sense.id or key
      if sid == senseId then
        targetSense = sense
        break
      end
    end
  end

  if not targetSense or type(targetSense.glosses) ~= 'table' then
    return nil
  end

  -- Inhalts-Sprache bestimmen
  local contentLang = 'de'
  if mw.getContentLanguage then
    local cl = mw.getContentLanguage()
    if cl and cl.getCode then
      contentLang = cl:getCode()
    end
  end

  -- Gloss in Inhalts-Sprache bevorzugen, sonst irgendeinen
  local gloss
  local g = targetSense.glosses[contentLang]
  if g and type(g) == 'table' and g.value then
    gloss = g.value
  else
    for _, obj in pairs(targetSense.glosses) do
      if type(obj) == 'table' and obj.value then
        gloss = obj.value
        break
      end
    end
  end

  if gloss and gloss ~= '' then
    return gloss
  end

  return nil
end

-- Form (Wortform) für ein Lexem ermitteln (z.B. F=1, F='F1', F='L2-F1')
local function getLexemeFormRepresentation(entityId, formParam)
  if not entityId or not formParam or formParam == '' then
    return nil
  end

  local etype = getEntityType(entityId)
  if etype ~= 'lexeme' then
    return nil
  end

  -- Form-ID kanonisieren
  local s = tostring(formParam)
  local formId

  -- Vollständige ID: L123-F4
  if s:match('^L%d+-F%d+$') then
    formId = s
  else
    -- „F4“ oder „4“ → L123-F4
    local num = s:match('^F(%d+)$') or s:match('^(%d+)$')
    if not num then
      return nil
    end
    formId = string.format('%s-F%s', entityId, num)
  end

  -- Lexem-Entity holen
  local ok, entity = pcall( mw.wikibase.getEntity, entityId )
  if not ok or type(entity) ~= 'table' or type(entity.forms) ~= 'table' then
    return nil
  end

  -- Ziel-Form in entity.forms suchen
  local targetForm = nil
  for key, form in pairs(entity.forms) do
    if type(form) == 'table' then
      local fid = form.id or key
      if fid == formId then
        targetForm = form
        break
      end
    end
  end

  if not targetForm or type(targetForm.representations) ~= 'table' then
    return nil
  end

  -- Inhalts-Sprache bestimmen
  local contentLang = 'de'
  if mw.getContentLanguage then
    local cl = mw.getContentLanguage()
    if cl and cl.getCode then
      contentLang = cl:getCode()
    end
  end

  -- Wortform in Inhalts-Sprache bevorzugen, sonst irgendeine
  local formText
  local r = targetForm.representations[contentLang]
  if r and type(r) == 'table' and r.value then
    formText = r.value
  else
    for _, obj in pairs(targetForm.representations) do
      if type(obj) == 'table' and obj.value then
        formText = obj.value
        break
      end
    end
  end

  if formText and formText ~= '' then
    return formText
  end

  return nil
end


------------------------------------------------------------------
-- Öffentliche Funktionen
------------------------------------------------------------------

-- 1) Nur Link ausgeben (ohne sichtbaren Untertitel)
--    Q:  Label
--    P/L: ID Label
function p.link(frame)
  local args = getArgs(frame)
  local entityId = detectEntityId(args)
  if not entityId then
    return ''
  end

  local etype = getEntityType(entityId)
  local baseLabel = args.label or args.text or buildEntityLabel(entityId) or entityId

  local linkLabel
  if etype == 'item' then
    -- Q: nur das Label
    linkLabel = baseLabel
  else
    -- P/L: ID + Label
    linkLabel = string.format('%s %s', entityId, baseLabel)
  end

  -- Sicherheit: keine Zeilenumbrüche im Linktext
  linkLabel = trim(linkLabel):gsub('\n', ' ')

  return buildEntityLink(entityId, linkLabel)
end

-- 2) Nur SHORTDESC-Wikitext zurückgeben (falls sinnvoll)
--    Hinweis: Ob die ShortDescription-Extension {{SHORTDESC:...}}
--    aus Vorlagen heraus verarbeitet, hängt von der lokalen
--    Konfiguration ab.
function p.shortdesc(frame)
  local args = getArgs(frame)
  local entityId = detectEntityId(args)
  if not entityId then
    return ''
  end

  local mode = args.mode or args.typ or args.type
  local text = buildShortdescText(entityId, mode)
  if not text or text == '' then
    return ''
  end

  text = trim(text):gsub('\n', ' ')

  -- SHORTDESC als Parserfunktion aufrufen, so als stünde
  -- {{SHORTDESC:text}} im Quelltext.
  local ok, result = pcall(function()
    -- Entspricht {{SHORTDESC:text}}
    return frame:callParserFunction('SHORTDESC', text)
  end)

  if not ok then
    -- Falls die Parserfunktion nicht verfügbar ist, lieber
    -- still scheitern, damit Seiten nicht kaputtgehen.
    return ''
  end

  -- SHORTDESC gibt in der Regel nichts Sichtbares zurück.
  -- Wir geben das Ergebnis (typisch leer) zurück, so dass
  -- der #invoke-Aufruf im Seitentext nichts anzeigt.
  return result or ''
end


-- 3) Link mit Untertitel kombinieren
--    Muster:
--       Lexem ohne Sinn/Form:   [[.../L2|L2 Dingwort]] (Wortewort)
--       Lexem mit Sinn (S):     [[.../L2|L2 Dingwort]] (Wortewort)
--                                Ding benennendes Wort
--       Lexem mit Sinn + Form:  [[.../L2|L2 Dingwort]] (Wortewort)
--                                Ding benennendes Wort
--                                Ding benennende Wörter  (z.B. F1)
--       Item/Property:          [[.../Q6|Q6 Dingwort]] (…Beschreibung…)
function p.subtitle(frame)
  local args = getArgs(frame)
  local entityId = detectEntityId(args)
  if not entityId then
    return ''
  end

  local mode  = args.mode or args.typ or args.type
  local etype = getEntityType(entityId)

  -- Basislabel: explizit oder aus Wikibase/Lexem-Lemma
  local baseLabel = args.label or args.text or buildEntityLabel(entityId) or entityId

  -- Untertitel: Beschreibung (Q/P) bzw. Wortart (L)
  local desc = buildShortdescText(entityId, mode)
  if desc ~= nil then
    desc = trim(desc):gsub('\n', ' ')
  end

  ----------------------------------------------------------------
  -- Sinn (Sense) für Lexeme optional bestimmen (S=…)
  ----------------------------------------------------------------
  local senseParam = args.S or args.s or args.sense
  local senseGloss = nil
  if senseParam and etype == 'lexeme' then
    senseGloss = getLexemeSenseGloss(entityId, senseParam)
    if senseGloss ~= nil then
      senseGloss = trim(senseGloss):gsub('\n', ' ')
    end
  end

  ----------------------------------------------------------------
  -- Form (Wortform) für Lexeme optional bestimmen (F=…)
  ----------------------------------------------------------------
  local formParam = args.F or args.f or args.form or args.Form
  local formText = nil
  if formParam and etype == 'lexeme' then
    formText = getLexemeFormRepresentation(entityId, formParam)
    if formText ~= nil then
      formText = trim(formText):gsub('\n', ' ')
    end
  end

  ----------------------------------------------------------------
  -- Steuerung, ob der Untertitel im sichtbaren Text stehen soll
  -- subtitle: ja/1 (Standard) → sichtbar
  --           nein/0          → unsichtbar
  ----------------------------------------------------------------
  local subtitleParam = args.subtitle
  local showSubtitle  = true  -- Default: ja

  if subtitleParam ~= nil and subtitleParam ~= '' then
    local s = tostring(subtitleParam):lower()
    if s == 'nein' or s == '0' then
      showSubtitle = false
    elseif s == 'ja' or s == '1' then
      showSubtitle = true
    end
  end

  -- SHORTDESC als Seiteneigenschaft setzen (für Suche), falls vorhanden
  -- (weiterhin nur aus der Kurzbeschreibung, nicht aus Sinn/Form)
  if desc and desc ~= '' then
    pcall(function()
      frame:callParserFunction('SHORTDESC', desc)
    end)
  end

  -- Sichtbarer Link-Teil: für Q/P/L immer "ID Label"
  local visibleLabel = string.format('%s %s', entityId, baseLabel)
  visibleLabel = trim(visibleLabel):gsub('\n', ' ')

  -- Link nur um ID + Label
  local linkText = buildEntityLink(entityId, visibleLabel)

  -- Wenn keine sichtbaren Untertitel gewünscht → nur Link zurückgeben
  if not showSubtitle then
    return linkText
  end

  -- Spezielles Layout für Lexeme: Zeile 1 + optional Zeile 2/3
  if etype == 'lexeme' then
    local line1
    if desc and desc ~= '' then
      line1 = string.format('%s (%s)', linkText, desc)
    else
      line1 = linkText
    end

    local lines = { line1 }

    if senseGloss and senseGloss ~= '' then
      table.insert(lines, senseGloss)
    end

    if formText and formText ~= '' then
      table.insert(lines, formText)
    end

    -- Zeilen mit <br /> verbinden
    return table.concat(lines, '<br />')
  end

  -- Standardfall: Q / P → Link + (Beschreibung)
  if desc and desc ~= '' then
    return string.format('%s (%s)', linkText, desc)
  else
    return linkText
  end
end


return p