Module: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 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

-- Used via a template -- which just calls, the 3 parameters are automatically trimmed: -- Parameter 1 takes the default value from 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