Module:LangSwitcher: Difference between revisions

From Heroes of Might and Magic: Olden Era Official Wiki
Created page with "-- Module:LangSwitcher -- Language subpage switcher for self-hosted MediaWiki -- Usage: -- {{#invoke:LangSwitcher|render -- | langs = en,sv,da,fi -- | labels = en=English,sv=Svenska,da=Dansk,fi=Suomi -- | icon = 🌐 -- | class = hh-lang-switcher -- }} -- -- Behavior: -- * Determines base page and current language from title/subpage -- * Scans configured langs and checks if base/xx exists -- * Includes the base page itself as the "default" languag..."
 
mNo edit summary
Line 28: Line 28:
     end
     end
     for part in s:gmatch("([^,]+)") do
     for part in s:gmatch("([^,]+)") do
         table.insert(t, trim(part))
         local v = trim(part)
        if v ~= "" then
            table.insert(t, v) -- append
        end
     end
     end
     return t
     return t
Line 52: Line 55:
     local full = title.fullText
     local full = title.fullText
     local base, lang = full, nil
     local base, lang = full, nil
     -- Detect language subpage pattern: Base/xx (two-letter or hyphenated codes like pt-br)
     -- Accept language subpages like Base/xx or Base/pt-br (2–10 letters/hyphens)
    -- We allow 2–10 chars: letters and hyphen
     local b, l = full:match("^(.-)/([A-Za-z%-][A-Za-z%-][A-Za-z%-]*)$")
     local b, l = full:match("^(.-)/([A-Za-z%-][A-Za-z%-][A-Za-z%-]*)$")
     if b and l then
     if b and l then
Line 83: Line 85:
     local icon = args.icon or ""
     local icon = args.icon or ""


    -- Detect base/current
     local base, currentLang = getTitleParts()
     local base, currentLang, currentTitle = getTitleParts()


    -- Determine canonical default language page (base without suffix is treated as default language)
    -- The first code in langs is assumed default if not specified; fallback to 'en'
     local defaultLang = trim(args.default or (langs[1] or "en")):lower()
     local defaultLang = trim(args.default or (langs[1] or "en")):lower()


    -- Build items
    local items = {}
    -- Helper to label a language
     local function labelFor(code)
     local function labelFor(code)
         return labels[code] or code
         return labels[code] or code
     end
     end


     -- Ensure default language entry exists in the list order
     -- Build ordered list, starting with defaultLang
     local seen = {}
     local seen = {}
     local ordered = {}
     local ordered = {}


     -- Always start with defaultLang at the front
     local function add(code)
    table.insert(ordered, defaultLang)
        if not code or code == "" then
    seen[defaultLang] = true
            return
    -- Append the rest in provided order
        end
    for _, code in ipairs(langs) do
         local lc = code:lower()
         local lc = code:lower()
         if not seen[lc] then
         if not seen[lc] then
Line 114: Line 108:
     end
     end


     -- Render each language as link or text
     add(defaultLang)
    for _, code in ipairs(langs) do
        add(code)
    end
 
    local items = {}
 
     for _, code in ipairs(ordered) do
     for _, code in ipairs(ordered) do
         local target = (code == defaultLang) and base or (base .. "/" .. code)
         local target = (code == defaultLang) and base or (base .. "/" .. code)
Line 124: Line 124:
                 isActive = (currentLang == code)
                 isActive = (currentLang == code)
             else
             else
                -- On base page (no language suffix), consider defaultLang active
                 isActive = (code == defaultLang)
                 isActive = (code == defaultLang)
             end
             end
Line 138: Line 137:
                 content = makeLink(target, label)
                 content = makeLink(target, label)
             else
             else
                -- Missing page: display as redlink or plain text
                 content = makeLink(target, label) -- redlink
                 content = makeLink(target, label) -- redlink
                -- content = string.format('<span class="missing">%s</span>', escapeHtml(label)) -- plain text alternative
             end
             end



Revision as of 13:02, 30 October 2025

Documentation for this module may be created at Module:LangSwitcher/doc

-- Module:LangSwitcher
-- Language subpage switcher for self-hosted MediaWiki
-- Usage:
--   {{#invoke:LangSwitcher|render
--      | langs = en,sv,da,fi
--      | labels = en=English,sv=Svenska,da=Dansk,fi=Suomi
--      | icon = 🌐
--      | class = hh-lang-switcher
--   }}
--
-- Behavior:
-- * Determines base page and current language from title/subpage
-- * Scans configured langs and checks if base/xx exists
-- * Includes the base page itself as the "default" language (first in langs or 'en')
-- * Marks current selection with class "active"
-- * Skips links for languages without existing pages (optional flag to show “redlinks”)

local p = {}

local function trim(s)
    return (s or ""):gsub("^%s+", ""):gsub("%s+$", "")
end

local function splitCSV(s)
    local t = {}
    if not s or s == "" then
        return t
    end
    for part in s:gmatch("([^,]+)") do
        local v = trim(part)
        if v ~= "" then
            table.insert(t, v)  -- append
        end
    end
    return t
end

local function parseKVList(s)
    -- Parse "en=English,sv=Svenska"
    local out = {}
    if not s or s == "" then
        return out
    end
    for pair in s:gmatch("([^,]+)") do
        local k, v = pair:match("^%s*([^=]+)%s*=%s*(.-)%s*$")
        if k and v then
            out[trim(k)] = trim(v)
        end
    end
    return out
end

local function getTitleParts()
    local title = mw.title.getCurrentTitle()
    local full = title.fullText
    local base, lang = full, nil
    -- Accept language subpages like Base/xx or Base/pt-br (2–10 letters/hyphens)
    local b, l = full:match("^(.-)/([A-Za-z%-][A-Za-z%-][A-Za-z%-]*)$")
    if b and l then
        base, lang = b, l:lower()
    end
    return base, lang, title
end

local function exists(page)
    local t = mw.title.new(page)
    return t and t.exists
end

local function makeLink(page, label)
    return string.format('[[%s|%s]]', page, label)
end

local function escapeHtml(s)
    return mw.text.nowiki(s or "")
end

function p.render(frame)
    local args = frame:getParent() and frame:getParent().args or frame.args

    local langs = splitCSV(args.langs or args.languages or "en")
    local labels = parseKVList(args.labels or "")
    local showMissing = (args.show_missing or ""):lower() == "yes"
    local wrapperClass = trim(args.class or "lang-switcher")
    local icon = args.icon or ""

    local base, currentLang = getTitleParts()

    local defaultLang = trim(args.default or (langs[1] or "en")):lower()

    local function labelFor(code)
        return labels[code] or code
    end

    -- Build ordered list, starting with defaultLang
    local seen = {}
    local ordered = {}

    local function add(code)
        if not code or code == "" then
            return
        end
        local lc = code:lower()
        if not seen[lc] then
            table.insert(ordered, lc)
            seen[lc] = true
        end
    end

    add(defaultLang)
    for _, code in ipairs(langs) do
        add(code)
    end

    local items = {}

    for _, code in ipairs(ordered) do
        local target = (code == defaultLang) and base or (base .. "/" .. code)
        local pageExists = exists(target)

        if pageExists or showMissing then
            local isActive
            if currentLang then
                isActive = (currentLang == code)
            else
                isActive = (code == defaultLang)
            end

            local liClass = "lang-item lang-" .. code
            if isActive then
                liClass = liClass .. " active"
            end

            local label = labelFor(code)
            local content
            if pageExists then
                content = makeLink(target, label)
            else
                content = makeLink(target, label) -- redlink
            end

            table.insert(items, string.format('<li class="%s">%s</li>', liClass, content))
        end
    end

    if #items == 0 then
        return ""
    end

    local iconHtml = icon ~= "" and string.format('<span class="lang-icon">%s</span>', escapeHtml(icon)) or ""
    local html = {}
    table.insert(html, string.format('<nav class="%s" role="navigation" aria-label="Languages">', wrapperClass))
    table.insert(html, iconHtml)
    table.insert(html, '<ul class="lang-list">')
    table.insert(html, table.concat(items))
    table.insert(html, '</ul>')
    table.insert(html, '</nav>')

    return table.concat(html)
end

return p