Jump to content
Toggle menu
  • 8 articles
  • 75 files
  • 7 users
  • 37.8K edits
Neodyland Wiki
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Dir: Difference between revisions

From Neodyland Wiki
create replacement for Template:Dir
 
m 22 revisions imported
 
(21 intermediate revisions by 3 users not shown)
Line 1: Line 1:
-- This module implements [[Template:Dir]].
--[==[
This module implements [[Template:Dir]].


Enter this to run tests in the Lua console:
=getmetatable(p).quickTests()
--]==]
local rtlOverrides = require('Module:Dir/RTL overrides')
local p = {}
local p = {}


local function trim(s)
local function trim(s)
if s then
    if s and s ~= '' then s = tostring(s):match('^%s*(.-)%s*$') end
return s:match('^%s*(.-)%s*$')
    if s == '' then return nil end
else
    return s
return nil
end
end
end


local function trimAndRemoveBlanks(s)
function p.rtlLangs(isRTL)
if s then
    if isRTL == nil then isRTL = true end
s = trim(s)
    return rtlOverrides[isRTL] or {}
if s ~= '' then
return s
end
end
return nil
end
end


function p.isRTL(code)
function p.isRTL(code)
local lang = code and mw.language.new(code)
    if type(code) ~= 'string' then return nil end
return lang and lang:isRTL() or false
    local v = rtlOverrides[code] -- very fast and not limited in the number of supported languages
    if v ~= nil then return v end -- return it if mapped, otherwise use MediaWiki library:
    local success, ret = pcall(function ()
        return mw.language.new(code):isRTL() -- expensive and limited to 20 languages per MediaWiki instance
    end)
    return success and ret
end
end


function p.select(code, rtl, ltr)
    if p.isRTL(code) then
        return rtl
    else
        return ltr
    end
end
-- The 3 parameters are automatically trimmed:
-- Parameter 1 takes the default value from {{Int:Lang}} if it's empty or not specified.
-- Parameter 2 can be named rtl, may be explicitly empty, otherwise takes the default value 'rtl' only if it's not specified.
-- Parameter 3 can be named ltr, may be explicitly empty, otherwise takes the default value 'ltr' only if it's not specified.
function p.main(frame)
function p.main(frame)
local args = frame:getParent().args
    local args = frame:getParent().args -- Parameters used to transclude Template:Dir
local code = args[1]
    local code = trim(args[1]) or frame:callParserFunction('Int', 'Lang')
code = trimAndRemoveBlanks(code)
    local rtl = trim(args.rtl or args[2] or 'rtl')
if p.isRTL(code) then
    local ltr = trim(args.ltr or args[3] or 'ltr')
return trim(args[2]) or 'rtl'
    return p.select(code, rtl, ltr)
else
return trim(args[3]) or 'ltr'
end
end
end
setmetatable(p, { quickTests = function ()
    local rtlLangs = p.rtlLangs(true)
    local ltrLangs = p.rtlLangs(false)
    -- Basic check of data format
    local function checkLangs(name, langs)
        for k, lang in pairs(langs) do
            assert(type(k) == 'number' and k == math.floor(k)
                and type(lang) == 'string' and #lang >= 2 and #lang <= 16
                and lang:find('^[a-z][%-0-9a-z]*[0-9a-z]$') == 1,
                ": Invalid sequence of language codes, " .. name .. "['" .. k .. "'] = '" .. lang .. "'")
        end
        return true
    end
    local ok, msg
    ok, msg = pcall(checkLangs, 'rtlLangs', rtlLangs)
    if not ok then return false, msg end
    ok, msg = pcall(checkLangs, 'ltrLangs', ltrLangs)
    if not ok then return false, msg end
    -- Build inverse maps of languages having each direction
    local isrtl, isltr = {}, {}
    for _, lang in ipairs(rtlLangs) do isrtl[lang] = true end
    for _, lang in ipairs(ltrLangs) do isltr[lang] = true end
    -- Check conflicts using the two inverse maps
    for _, lang in ipairs(rtlLangs) do
        if isltr[lang] then return false, ": Direction conflict for '" .. lang .. "'" end
    end
    for _, lang in ipairs(ltrLangs) do
        if isrtl[lang] then return false, ": Direction conflict for '" .. lang .. "'" end
    end
    -- Log missing languages (allows filling the tables above) according to MediaWiki internal data
    local knownLangs, isKnownLang = mw.language.fetchLanguageNames(), {}
    for lang, _ in pairs(knownLangs) do
        isKnownLang[lang] = true
        if rtlOverrides[lang] == nil then -- only if we still don't have data for this language
            -- Note: we cannot check more than 20 languages at once, then MediaWiki raises an error.
            -- So this test only runs on the Lua console, where you can update the tables at top.
            -- This also means we cannot compare what MediaWiki returns with the direction we map here for all languages.
            ok, value = pcall(function() return tostring(mw.language.new(lang):isRTL()) end)
            mw.log("Warning: missing direction for language '" .. lang .. "', MediaWiki returns '" .. value .. "'")
        end
    end
    -- utility: reverse order iterator on sequences
    local function revipairs(t)
        return function(t, i)
            i = i - 1
            local v = t[i]
            if v == nil then return nil end
            return i, v
        end, t, #t + 1
    end
    -- Sort and deduplicate language code values (by scanning backward) for data cleanup
    -- Also log languages having a direction mapping in this module but still not known by MediaWiki
    table.sort(rtlLangs)
    table.sort(ltrLangs)
    for i, lang in revipairs(rtlLangs) do
        if rtlLangs[i - 1] == rtlLangs[i] then table.remove(rtlLangs, i) end
        if isKnownLang[lang] == nil then
            mw.log("RTL language '" .. lang .. "' still not known by Mediawiki (could be a missing alias or variant)")
        end
    end
    for i, lang in revipairs(ltrLangs) do
        if ltrLangs[i - 1] == ltrLangs[i] then table.remove(ltrLangs, i) end
        if isKnownLang[lang] == nil then
            mw.log("LTR language '" .. lang .. "' still not known by Mediawiki (could be a missing alias or variant)")
        end
    end
    -- Final presentation of current lists, sorted and deduplicated
    mw.log("----")
    mw.log("local rtlLangs = { '" .. table.concat(rtlLangs, "', '") .. "' }")
    mw.log("local ltrLangs = { '" .. table.concat(ltrLangs, "', '") .. "' }")
    return true
end })


return p
return p

Latest revision as of 06:04, 9 August 2026

Module documentation[ create · purge ]
This documentation is transcluded from Module:Dir/doc.
--[==[
This module implements [[Template:Dir]].

Enter this to run tests in the Lua console:
=getmetatable(p).quickTests()

--]==]
local rtlOverrides = require('Module:Dir/RTL overrides')
local p = {}

local function trim(s)
    if s and s ~= '' then s = tostring(s):match('^%s*(.-)%s*$') end
    if s == '' then return nil end
    return s
end

function p.rtlLangs(isRTL)
    if isRTL == nil then isRTL = true end
    return rtlOverrides[isRTL] or {}
end

function p.isRTL(code)
    if type(code) ~= 'string' then return nil end
    local v = rtlOverrides[code] -- very fast and not limited in the number of supported languages
    if v ~= nil then return v end -- return it if mapped, otherwise use MediaWiki library:
    local success, ret = pcall(function ()
        return mw.language.new(code):isRTL() -- expensive and limited to 20 languages per MediaWiki instance
    end)
    return success and ret
end

function p.select(code, rtl, ltr)
    if p.isRTL(code) then
        return rtl
    else
        return ltr
    end
end

-- The 3 parameters are automatically trimmed:
-- Parameter 1 takes the default value from {{Int:Lang}} if it's empty or not specified.
-- Parameter 2 can be named rtl, may be explicitly empty, otherwise takes the default value 'rtl' only if it's not specified.
-- Parameter 3 can be named ltr, may be explicitly empty, otherwise takes the default value 'ltr' only if it's not specified.
function p.main(frame)
    local args = frame:getParent().args -- Parameters used to transclude Template:Dir
    local code = trim(args[1]) or frame:callParserFunction('Int', 'Lang')
    local rtl = trim(args.rtl or args[2] or 'rtl')
    local ltr = trim(args.ltr or args[3] or 'ltr')
    return p.select(code, rtl, ltr)
end

setmetatable(p, { quickTests = function ()
    local rtlLangs = p.rtlLangs(true)
    local ltrLangs = p.rtlLangs(false)

    -- Basic check of data format
    local function checkLangs(name, langs)
        for k, lang in pairs(langs) do
            assert(type(k) == 'number' and k == math.floor(k)
                and type(lang) == 'string' and #lang >= 2 and #lang <= 16
                and lang:find('^[a-z][%-0-9a-z]*[0-9a-z]$') == 1,
                ": Invalid sequence of language codes, " .. name .. "['" .. k .. "'] = '" .. lang .. "'")
        end
        return true
    end
    local ok, msg
    ok, msg = pcall(checkLangs, 'rtlLangs', rtlLangs)
    if not ok then return false, msg end
    ok, msg = pcall(checkLangs, 'ltrLangs', ltrLangs)
    if not ok then return false, msg end

    -- Build inverse maps of languages having each direction
    local isrtl, isltr = {}, {}
    for _, lang in ipairs(rtlLangs) do isrtl[lang] = true end
    for _, lang in ipairs(ltrLangs) do isltr[lang] = true end
    -- Check conflicts using the two inverse maps
    for _, lang in ipairs(rtlLangs) do
        if isltr[lang] then return false, ": Direction conflict for '" .. lang .. "'" end
    end
    for _, lang in ipairs(ltrLangs) do
        if isrtl[lang] then return false, ": Direction conflict for '" .. lang .. "'" end
    end

    -- Log missing languages (allows filling the tables above) according to MediaWiki internal data
    local knownLangs, isKnownLang = mw.language.fetchLanguageNames(), {}
    for lang, _ in pairs(knownLangs) do
         isKnownLang[lang] = true
         if rtlOverrides[lang] == nil then -- only if we still don't have data for this language
             -- Note: we cannot check more than 20 languages at once, then MediaWiki raises an error.
             -- So this test only runs on the Lua console, where you can update the tables at top.
             -- This also means we cannot compare what MediaWiki returns with the direction we map here for all languages.
             ok, value = pcall(function() return tostring(mw.language.new(lang):isRTL()) end)
             mw.log("Warning: missing direction for language '" .. lang .. "', MediaWiki returns '" .. value .. "'")
         end
    end

    -- utility: reverse order iterator on sequences
    local function revipairs(t)
        return function(t, i)
            i = i - 1
            local v = t[i]
            if v == nil then return nil end
            return i, v
        end, t, #t + 1
    end
    -- Sort and deduplicate language code values (by scanning backward) for data cleanup
    -- Also log languages having a direction mapping in this module but still not known by MediaWiki
    table.sort(rtlLangs)
    table.sort(ltrLangs)
    for i, lang in revipairs(rtlLangs) do
        if rtlLangs[i - 1] == rtlLangs[i] then table.remove(rtlLangs, i) end
        if isKnownLang[lang] == nil then
             mw.log("RTL language '" .. lang .. "' still not known by Mediawiki (could be a missing alias or variant)")
        end
    end
    for i, lang in revipairs(ltrLangs) do
        if ltrLangs[i - 1] == ltrLangs[i] then table.remove(ltrLangs, i) end
        if isKnownLang[lang] == nil then
             mw.log("LTR language '" .. lang .. "' still not known by Mediawiki (could be a missing alias or variant)")
        end
    end

    -- Final presentation of current lists, sorted and deduplicated
    mw.log("----")
    mw.log("local rtlLangs = { '" .. table.concat(rtlLangs, "', '") .. "' }")
    mw.log("local ltrLangs = { '" .. table.concat(ltrLangs, "', '") .. "' }")
    return true
end })

return p
Cookies help us deliver our services. By using our services, you agree to our use of cookies.