Modul:AufEinWortSeiteKennung
Erscheinungsbild
-- Modul:AufEinWortSeiteKennung (Erweiterung UD, bereinigt)
-- Zweck: kompakte, numerische und chronologisch sortierbare Kennung (YIDXMMDDNN)
-- YIDX = 5000 + Jahr (n. Chr.) bzw. 5000 - Jahr (v. Chr.), Jahr ∈ [1..4999]
-- MM/DD = zweistellig oder "00" bei Unbekannt; NN = zweistellige Nummer
local p = {}
-- =========================
-- Konfiguration
-- =========================
-- "prefix": ED:7020112000 / BD:7020112000 / UD:7020112000
-- "postfix": 7020112000 ED / 7020112000 BD / 7020112000 UD (empfohlen)
-- "both" : beide Varianten
local CATEGORY_STYLE = "postfix"
-- Zusätzlich die reine numerische Kategorie setzen (Übergangsphase)
local LEGACY_NUMERIC = false
-- =========================
-- Hilfsfunktionen
-- =========================
local function normalize_whitespace(s)
if not s then return s end
s = mw.ustring.gsub(s, "%s+", " ")
s = mw.ustring.match(s, "^%s*(.-)%s*$") or s
return s
end
-- Ära (CE/BCE) erkennen und Eingabe bereinigen
-- Rückgabe: era ("1"=n. Chr., "0"=v. Chr.), cleaned string
local function detectEraAndClean(s)
local era = "1" -- Standard: n. Chr.
if not s or s == "" then return era, s end
local sl = mw.ustring.lower(s)
sl = mw.ustring.gsub(sl, "%.", " ") -- Punkte entfernen
sl = normalize_whitespace(sl)
-- BCE-Marker erkennen (minus vorne, v. Chr., BCE, BC)
if mw.ustring.match(sl, "^%-")
or mw.ustring.find(sl, " v chr")
or mw.ustring.find(sl, " vor christus")
or mw.ustring.find(sl, " bce")
or mw.ustring.find(sl, " bc") then
era = "0"
end
-- Marker entfernen (wo auch immer sie stehen)
sl = mw.ustring.gsub(sl, "^%-", "")
sl = mw.ustring.gsub(sl, " v chr", "")
sl = mw.ustring.gsub(sl, " vor christus", "")
sl = mw.ustring.gsub(sl, " bce", "")
sl = mw.ustring.gsub(sl, " bc", "")
sl = normalize_whitespace(sl)
return era, sl
end
local function replace_umlauts(s)
local m = { ["Ä"] = "Ae", ["ä"] = "ae", ["Ö"] = "Oe", ["ö"] = "oe", ["Ü"] = "Ue", ["ü"] = "ue", ["ß"] = "ss" }
for k, v in pairs(m) do s = mw.ustring.gsub(s, k, v) end
return s
end
local monate = {
januar = "01", februar = "02",
maerz = "03", ["märz"] = "03",
april = "04", mai = "05",
juni = "06", juli = "07",
august = "08", september = "09",
oktober = "10", november = "11",
dezember = "12"
}
local function month_to_num(name)
if not name or name == "" then return "00" end
local asNum = tonumber(name)
if asNum and asNum >= 1 and asNum <= 12 then
return string.format("%02d", asNum)
end
local key = mw.ustring.lower(name)
if key == "märz" then key = "maerz" end
return monate[key] or "00"
end
-- Flexibles Parsen: liefert (tag, monat_name, jahr) oder nils
local function parse_date_flexible(s)
-- dd.mm.yyyy (auch 1- oder 2-stellige d/m)
local d, m, y = mw.ustring.match(s, "^(%d%d?)%s*%.%s*(%d%d?)%s*%.%s*(%d+)$")
if d and m and y then return d, m, y end
-- yyyy-mm-dd
local y2, m2, d2 = mw.ustring.match(s, "^(%d+)%-(%d%d?)%-(%d%d?)$")
if y2 and m2 and d2 then return d2, m2, y2 end
-- Tag[.] Monat Jahr (z. B. "12. Mai 425" oder "12 Mai 425")
local tag, monat_name, jahr = mw.ustring.match(s, "^(%d+)%.?%s*(%a+)%s*(%d+)$")
if tag and monat_name and jahr then return tag, monat_name, jahr end
-- Monat Jahr (z. B. "Mai 425")
monat_name, jahr = mw.ustring.match(s, "^(%a+)%s*(%d+)$")
if monat_name and jahr then return nil, monat_name, jahr end
-- Nur Jahr (z. B. "425")
jahr = mw.ustring.match(s, "^(%d+)$")
if jahr then return nil, nil, jahr end
return nil, nil, nil
end
-- PIVOT-Kodierung des Jahres: 5000 ± Jahr → 4-stellig
local function yearIndex(era, y)
if y < 1 then y = 1 end -- Jahr 0 vermeiden
if era == "0" then
local idx = 5000 - y
if idx < 0 then idx = 0 end
return idx
else
local idx = 5000 + y
if idx > 9999 then
return nil, "Fehler: Jahreszahl außerhalb des Bereichs (1–4999)"
end
return idx
end
end
-- Kern: Kennung erzeugen (YIDXMMDDNN)
local function makeKennung(datum, nummer)
local num = string.format("%02d", tonumber(nummer) or 0)
if not datum or datum == "" then
return "00000000" .. num, nil
end
local era, cleaned = detectEraAndClean(datum)
local normalized = replace_umlauts(cleaned)
local tag, monat_name, jahr = parse_date_flexible(normalized)
if not jahr then
return "Fehler: Ungültiges Datumsformat", { badFormat = true }
end
local y = tonumber(jahr)
if not y then
return "Fehler: Ungültige Jahreszahl", { badYear = true }
end
local yidx, err = yearIndex(era, y)
if not yidx then
return (err or "Fehler bei Jahresindex"), { badYear = true }
end
local Y = string.format("%04d", yidx)
local M = month_to_num(monat_name)
local warn = nil
if monat_name and M == "00" then
warn = warn or {}
warn.badMonth = true
end
local D = tag and string.format("%02d", tonumber(tag) or 0) or "00"
return Y .. M .. D .. num, warn
end
-- =========================
-- Öffentliche API
-- =========================
-- Lesbare Anzeige (z. B. für Listen oder DISPLAYTITLE)
local monatsNamen = {"Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"}
function p.anzeige(frame)
local a_self = frame.args or {}
local a_parent = (frame.getParent and frame:getParent().args) or {}
-- Priorität: ED → BD → UD → Datum
local datum = a_self.Erstelldatum or a_self.Bearbeitetdatum or a_self.Urwerkdatum or a_self.Datum
if not datum or datum == "" then
datum = a_parent.Erstelldatum or a_parent.Bearbeitetdatum or a_parent.Urwerkdatum or a_parent.Datum or ""
end
if not datum or datum == "" then return "unbekannt" end
local era, cleaned = detectEraAndClean(datum)
local normalized = replace_umlauts(cleaned)
local tag, monat_name, jahr = parse_date_flexible(normalized)
if not jahr then return "unbekannt" end
local y = tonumber(jahr) or 1
local eraLabel = (era == "0") and " v. Chr." or " n. Chr."
local mtxt = ""
if monat_name then
local mnum = month_to_num(monat_name)
local mn = tonumber(mnum)
if mn and mn >= 1 and mn <= 12 then mtxt = monatsNamen[mn] end
end
if tag and mtxt ~= "" then
return string.format("%d. %s %d%s", tonumber(tag) or 0, mtxt, y, eraLabel)
elseif mtxt ~= "" then
return string.format("%s %d%s", mtxt, y, eraLabel)
else
return string.format("%d%s", y, eraLabel)
end
end
-- Kennung (YIDXMMDD[NN]) → lesbarer Titel (für Kategorieseiten)
function p.decodeKennung(frame)
local code = (frame.args and frame.args.Kennung) or nil
if not code or code == "" then code = mw.title.getCurrentTitle().text or "" end
local digits = code:match("^(%d+)$")
if not digits then digits = mw.ustring.gsub(code, "[^%d]", "") end
local Y, M, D = digits:match("^(%d%d%d%d)(%d%d)(%d%d)")
if not (Y and M and D) then return code end
local yidx = tonumber(Y); if not yidx then return code end
local era = " n. Chr."; local year
if yidx < 5000 then era = " v. Chr."; year = 5000 - yidx else year = yidx - 5000 end
local mnum = tonumber(M) or 0
local dnum = tonumber(D) or 0
if mnum == 0 and dnum >= 1 and dnum <= 12 then mnum, dnum = dnum, 0 end -- Alt-/Fremdcodes
local mn_ok = (mnum >= 1 and mnum <= 12)
if dnum > 0 and mn_ok then
return string.format("%d. %s %d%s", dnum, monatsNamen[mnum], year, era)
elseif mn_ok then
return string.format("%s %d%s", monatsNamen[mnum], year, era)
else
return string.format("%d%s", year, era)
end
end
-- Decode + Suffix anhand Kategorientitel (ED/BD/UD)
function p.decodeKennungMitSuffix(frame)
local args = frame.args or {}
local title = mw.title.getCurrentTitle().text or ""
local base = p.decodeKennung({ args = { Kennung = title } })
if not base or base == "" then return base end
local isED = mw.ustring.match(title, "%sED$") or mw.ustring.match(title, "^ED:")
local isBD = mw.ustring.match(title, "%sBD$") or mw.ustring.match(title, "^BD:")
local isUD = mw.ustring.match(title, "%sUD$") or mw.ustring.match(title, "^UD:")
local token = ""
if isED then token = args.ED or "erst."
elseif isBD then token = args.BD or "bear."
elseif isUD then token = args.UD or "hrsg." end
if token == "" then return base end
local sep = args.sep or " "
local pos = args.pos or "after"
if pos == "before" then return token .. sep .. base else return base .. sep .. token end
end
-- Interne Helfer: Kategorieausgabe mit Prä-/Postfix
local function makeCatOutput(datum, nummer, token, opts)
local code, warn = makeKennung(datum, nummer)
local out = ""
if mw.ustring.match(code, "^%d%d%d%d%d%d%d%d%d%d") then
local style = (opts and opts.style) or CATEGORY_STYLE
if style == "prefix" or style == "both" then
out = out .. '<span style="display:none">[[Kategorie:' .. (token .. ":" .. code) .. ']]</span>'
end
if style == "postfix" or style == "both" then
out = out .. '<span style="display:none">[[Kategorie:' .. (code .. " " .. token) .. ']]</span>'
end
if LEGACY_NUMERIC then
out = out .. '<span style="display:none">[[Kategorie:' .. code .. ']]</span>'
end
end
if warn and warn.badMonth then
out = out .. '<span style="display:none">[[Kategorie:AufEinWort-Datum-fehlerhaft]]</span>'
end
return out
end
-- Jahrhundert-Kategorie aus Datum (auf Werkseiten)
local function makeCenturyCategoryFromDate(datum, opts)
if not datum or datum == "" then return "" end
local era, cleaned = detectEraAndClean(datum)
local normalized = replace_umlauts(cleaned)
local _t, _m, jahr = parse_date_flexible(normalized)
if not jahr then return "" end
local y = tonumber(jahr); if not y then return "" end
if y < 1 then y = 1 end
local eraCE = (era ~= "0")
local century = math.floor((y - 1) / 100) + 1
local form = (opts and opts.form) or "kurz"
local term = (form == "lang") and "Jahrhundert" or "Jh."
local nchr = (opts and opts.nchr) or "n. Chr."
local vchr = (opts and opts.vchr) or "v. Chr."
local label = tostring(century) .. ". " .. term .. " " .. (eraCE and nchr or vchr)
return "[[Kategorie:" .. label .. "]]"
end
-- Kategorien auf Werkseiten
function p.catErstellung(frame)
local args = frame.args or frame:getParent().args or {}
local datum = args.Erstelldatum or ""
local nummer = args.Nummer or "00"
local style = args.style
local out = makeCatOutput(datum, nummer, "ED", { style = style })
out = out .. makeCenturyCategoryFromDate(datum, { form = args.form, nchr = args.nchr, vchr = args.vchr })
return out
end
function p.catBearbeitung(frame)
local args = frame.args or frame:getParent().args or {}
local datum = args.Bearbeitetdatum or ""
local nummer = args.Nummer or "00"
local style = args.style
local out = makeCatOutput(datum, nummer, "BD", { style = style })
out = out .. makeCenturyCategoryFromDate(datum, { form = args.form, nchr = args.nchr, vchr = args.vchr })
return out
end
function p.catUrwerk(frame)
local args = frame.args or frame:getParent().args or {}
local datum = args.Urwerkdatum or ""
local nummer = args.Nummer or "00"
local style = args.style
local out = makeCatOutput(datum, nummer, "UD", { style = style })
out = out .. makeCenturyCategoryFromDate(datum, { form = args.form, nchr = args.nchr, vchr = args.vchr })
return out
end
-- Kennungen einzeln
function p.kennungE(frame)
local args = frame.args or (frame.getParent and frame:getParent().args) or {}
local datum = args.Erstelldatum or ""
local nummer = args.Nummer or "00"
return makeKennung(datum, nummer)
end
function p.kennungB(frame)
local args = frame.args or (frame.getParent and frame:getParent().args) or {}
local datum = args.Bearbeitetdatum or ""
local nummer = args.Nummer or "00"
return makeKennung(datum, nummer)
end
function p.kennungU(frame)
local args = frame.args or (frame.getParent and frame:getParent().args) or {}
local datum = args.Urwerkdatum or ""
local nummer = args.Nummer or "00"
return makeKennung(datum, nummer)
end
-- Meta-Kategorien für Kategorieseiten (ED/BD/UD)
function p.katMeta(frame)
local args = frame.args or {}
local title = mw.title.getCurrentTitle().text or ""
local isED = mw.ustring.match(title, "%sED$") or mw.ustring.match(title, "^ED:")
local isBD = mw.ustring.match(title, "%sBD$") or mw.ustring.match(title, "^BD:")
local isUD = mw.ustring.match(title, "%sUD$") or mw.ustring.match(title, "^UD:")
local digits = mw.ustring.gsub(title, "[^%d]", "") or ""
local Y, M, D = digits:match("^(%d%d%d%d)(%d%d)(%d%d)")
local gran = "year"
if Y and M and D then
if D ~= "00" then gran = "day"
elseif M ~= "00" then gran = "month"
else gran = "year" end
end
local edCat = args.ED or "Kategorie:Werk Erstelldatum"
local bdCat = args.BD or "Kategorie:Werk Bearbeitetdatum"
local udCat = args.UD or "Kategorie:Werk Urwerkdatum"
local edTagCat = args.ED_Tag or args.EDTag or "Kategorie:Werk Erstellungskennung Tag"
local edMonCat = args.ED_Monat or args.EDMonat or "Kategorie:Werk Erstellungskennung Monat"
local edJahrCat = args.ED_Jahr or args.EDJahr or "Kategorie:Werk Erstellungskennung Jahr"
local bdTagCat = args.BD_Tag or args.BDTag or "Kategorie:Werk Bearbeitungskennung Tag"
local bdMonCat = args.BD_Monat or args.BDMonat or "Kategorie:Werk Bearbeitungskennung Monat"
local bdJahrCat = args.BD_Jahr or args.BDJahr or "Kategorie:Werk Bearbeitungskennung Jahr"
local udTagCat = args.UD_Tag or args.UDTag or "Kategorie:Werk Urwerkkennung Tag"
local udMonCat = args.UD_Monat or args.UDMonat or "Kategorie:Werk Urwerkkennung Monat"
local udJahrCat = args.UD_Jahr or args.UDJahr or "Kategorie:Werk Urwerkkennung Jahr"
if isED then
if gran == "day" then return "[[" .. edTagCat .. "]]" end
if gran == "month" then return "[[" .. edMonCat .. "]]" end
if gran == "year" then return "[[" .. edJahrCat .. "]]" end
return "[[" .. edCat .. "]]"
elseif isBD then
if gran == "day" then return "[[" .. bdTagCat .. "]]" end
if gran == "month" then return "[[" .. bdMonCat .. "]]" end
if gran == "year" then return "[[" .. bdJahrCat .. "]]" end
return "[[" .. bdCat .. "]]"
elseif isUD then
if gran == "day" then return "[[" .. udTagCat .. "]]" end
if gran == "month" then return "[[" .. udMonCat .. "]]" end
if gran == "year" then return "[[" .. udJahrCat .. "]]" end
return "[[" .. udCat .. "]]"
end
return ""
end
-- Jahrhundert-Metakategorie je nach Datum (für Kategorieseiten)
-- Fügt automatisch eine Jahrhundert-Kategorie hinzu, z. B. "21. Jh. n. Chr." / "1. Jh. v. Chr."
-- Erkennung basiert auf dem Kategorietitel (Post-/Prefix mit ED/BD/UD), Jahreszahl aus YIDXMMDD
-- Optional: split=1 → eigene Jahrhundert-Kategorien für ED/BD/UD (Suffix " ED"/" BD"/" UD"), sonst gemeinsam
function p.katJahrhundert(frame)
local args = frame.args or {}
local title = mw.title.getCurrentTitle().text or ""
local isED = mw.ustring.match(title, "%sED$") or mw.ustring.match(title, "^ED:")
local isBD = mw.ustring.match(title, "%sBD$") or mw.ustring.match(title, "^BD:")
local isUD = mw.ustring.match(title, "%sUD$") or mw.ustring.match(title, "^UD:")
local digits = mw.ustring.gsub(title, "[^%d]", "") or ""
local Y, M, D = digits:match("^(%d%d%d%d)(%d%d)(%d%d)")
if not (Y and M and D) then return "" end
local yidx = tonumber(Y); if not yidx then return "" end
local eraCE = true; local year
if yidx < 5000 then eraCE = false; year = 5000 - yidx else year = yidx - 5000 end
if year < 1 then year = 1 end
local century = math.floor((year - 1) / 100) + 1
local form = args.form or "kurz"
local term = (form == "lang") and "Jahrhundert" or "Jh."
local nchr = args.nchr or "n. Chr."
local vchr = args.vchr or "v. Chr."
local label = tostring(century) .. ". " .. term .. " " .. (eraCE and nchr or vchr)
local split = tostring(args.split or "0")
if split == "1" then
if isED then label = label .. " ED"
elseif isBD then label = label .. " BD"
elseif isUD then label = label .. " UD" end
end
return "[[Kategorie:" .. label .. "]]"
end
return p