Jump to content

Module:Wn/bn/উৎস

From Wikimedia Incubator
(Redirected from Module:Wn/bn/source)

local arguments = require('Module:Wn/bn/Arguments')

local Config = {
    Patterns = {
        BENGALI = "[\u{0980}-\u{09FF}]",
        URL = "^https?://[%w%-%._~:/%?#%[%]@!%$&'%(%)%*%+,;=%%\u{0980}-\u{09FF}]+$",
        ENGLISH = "^[A-Za-z0-9%s%p]+$",
        LATIN = "[A-Za-z]"
    },
    Messages = {
        NO_PUBLISHER = "ত্রুটি: প্রকাশক প্রদান করা হয়নি",
        NO_URL = "ত্রুটি: ইউআরএল প্রদান করা হয়নি",
        NO_TITLE = "ত্রুটি: শিরোনাম প্রদান করা হয়নি",
        INVALID_URL = "অবৈধ ইউআরএল",
        INVALID_ARCHIVE_URL = "অবৈধ সংরক্ষিত ইউআরএল",
        INVALID_ACCESS_DATE = "ত্রুটি: প্রাপ্তির তারিখ, প্রকাশনার তারিখের পূর্বে।",
        FORMAT_ERROR = "উৎস বিন্যাস ত্রুটি: "
    },
    Categories = {
        NON_BENGALI = "[[Category:Wn/bn/বাংলা ব্যতীত প্যারামিটারযুক্ত উৎস]]",
        HAS_ERROR = "[[Category:Wn/bn/ত্রুটিযুক্ত উৎস সহ পৃষ্ঠাসমূহ]]"
    },
    Mappings = {
        Digits = {
            ['০']='0', ['১']='1', ['২']='2', ['৩']='3', ['৪']='4',
            ['৫']='5', ['৬']='6', ['৭']='7', ['৮']='8', ['৯']='9'
        },
        Months = {
            ['জানুয়ারি']='January', ['জানুয়ারী']='January',
            ['ফেব্রুয়ারি']='February', ['ফেব্রুয়ারী']='February',
            ['মার্চ']='March', ['এপ্রিল']='April', ['মে']='May', 
            ['জুন']='June', ['জুলাই']='July', ['আগস্ট']='August', 
            ['সেপ্টেম্বর']='September', ['অক্টোবর']='October', 
            ['নভেম্বর']='November', ['ডিসেম্বর']='December'
        },
        MonthToNum = {
            January=1, February=2, March=3, April=4, May=5, June=6,
            July=7, August=8, September=9, October=10, November=11, December=12
        }
    }
}

-- Utility functions
local Util = {}

function Util.wrapError(text)
    return string.format('<span class="error">%s</span>', text)
end

function Util.trim(str)
    return str and str:match("^%s*(.-)%s*$")
end

function Util.decodePercent(str)
    return str:gsub("%%(%x%x)", function(h)
        return string.char(tonumber(h, 16))
    end)
end

-- Validation functions
local Validator = {}

function Validator.isValidUrl(url)
    if not url then return false end
    
    local cleanUrl = url:gsub("%s+", "")
                       :gsub("[\u{200B}-\u{200D}]", "")
    
    return cleanUrl:match(Config.Patterns.URL) ~= nil or
           Util.decodePercent(cleanUrl):match(Config.Patterns.URL) ~= nil
end

function Validator.containsBengali(text)
    return text and (text:match(Config.Patterns.BENGALI) ~= nil or text:match("[।,%d]") ~= nil)
end

function Validator.isEnglish(text)
    return text and text:match("[A-Za-z0-9]") ~= nil
end

function Validator.hasNonBengaliContent(text)
    return text and text:match(Config.Patterns.LATIN) ~= nil
end

-- Date handling
local DateHandler = {}

function DateHandler.convertToEnglish(str)
    local converted = str
    for bn, en in pairs(Config.Mappings.Digits) do 
        converted = converted:gsub(bn, en) 
    end
    for bn, en in pairs(Config.Mappings.Months) do 
        converted = converted:gsub(bn, en) 
    end
    return converted
end

function DateHandler.parseDate(str)
    if not str then return nil end
    
    local engStr = DateHandler.convertToEnglish(Util.trim(str))
    local day, month, year = engStr:match("^(%d+)%s+(%a+)[,%s]*(%d+)$")
    
    if not (day and month and year) then return nil end
    
    return {
        day = tonumber(day),
        month = Config.Mappings.MonthToNum[month] or 0,
        year = tonumber(year)
    }
end

function DateHandler.compareDates(date1, date2)
    if not date1 or not date2 then return true end
    
    local d1 = DateHandler.parseDate(date1)
    local d2 = DateHandler.parseDate(date2)
    
    if not d1 or not d2 then return true end
    
    if d1.year ~= d2.year then return d1.year <= d2.year end
    if d1.month ~= d2.month then return d1.month <= d2.month end
    return d1.day <= d2.day
end

-- Formatter functions
local Formatter = {}

function Formatter.detectLanguage(text, frame)
    if not text then return nil end
    text = Util.trim(text)
    if Validator.isEnglish(text) then
        return frame:expandTemplate{title = 'Wn/bn/ইংরেজি'}
    end
    return nil
end

function Formatter.formatPublisher(frame, publisher)
    if not publisher then
        return " — " .. Util.wrapError(Config.Messages.NO_PUBLISHER), true
    end
    
    local baseText = " — "
    if frame:callParserFunction{name = '#ifexist', args = {'Category:Wn/bn/' .. publisher, 'yes', 'no'}} == 'yes' then
        return baseText .. string.format('[[:%s|%s]]', 'Category:Wn/bn/' .. publisher, publisher), false
    end
    return baseText .. string.format('[[w:bn:%s|%s]]', publisher, publisher), false
end

function Formatter.formatUrl(url, archivedUrl, title)
    if not url then return Util.wrapError(Config.Messages.NO_URL), true end
    if not title then return Util.wrapError(Config.Messages.NO_TITLE), true end
    if not Validator.isValidUrl(url) then
        return string.format('[%s %s <%s>]', url, title, Config.Messages.INVALID_URL), true
    end
    
    if archivedUrl then
        if not Validator.isValidUrl(archivedUrl) then
            return Util.wrapError(Config.Messages.INVALID_ARCHIVE_URL), true
        end
        return string.format('[%s %s]', archivedUrl, title), false
    end
    
    return string.format('[%s %s]', url, title), false
end

-- Main module
local p = {}

function p.formatSource(frame)
    local args = require('Module:Wn/bn/Arguments').getArgs(frame)
    local source = {}
    local state = {
        hasError = false,
        hasNonBengaliParams = false
    }
    
    -- Check for non-Bengali content
    if (args.author and Validator.hasNonBengaliContent(args.author)) or
       (args.publisher and Validator.hasNonBengaliContent(args.publisher)) then
        state.hasNonBengaliParams = true
    end
    
    -- Build source components
    if args.author then
        table.insert(source, args.author .. "। ")
    end
    
    local urlText, urlError = Formatter.formatUrl(args.url, args.archived_url, args.title)
    state.hasError = state.hasError or urlError
    table.insert(source, '"' .. urlText .. '"')
    
    local publisherText, pubError = Formatter.formatPublisher(frame, args.publisher)
    state.hasError = state.hasError or pubError
    local separator = (args.archived_url or (args.access_date and not args.date)) and "। " or ", "
    table.insert(source, publisherText .. separator)
    
    if args.archived_url and args.url and Validator.isValidUrl(args.url) then
        table.insert(source, string.format('মূল [%s সংস্করণ] থেকে সংরক্ষিত। ', args.url))
    end
    
    if args.date then
        table.insert(source, args.date)
    end
    
    if args.access_date then
        if args.date and not DateHandler.compareDates(args.date, args.access_date) then
            table.insert(source, Util.wrapError(Config.Messages.INVALID_ACCESS_DATE))
            state.hasError = true
        else
            table.insert(source, args.date and "। " or "")
            table.insert(source, "সংগ্রহের তারিখ: " .. args.access_date)
        end
    end
    
    -- Handle language
    local languageText
    if args.language and args.language:match("%S") then
        languageText = frame:expandTemplate{title = 'Wn/bn/Languageicon', args = {args.language}}
    else
        languageText = args.title and Formatter.detectLanguage(args.title, frame)
    end
    
    if languageText then
        table.insert(source, "। ")
        table.insert(source, languageText)
    end
    
    -- Add tracking categories
    if state.hasNonBengaliParams then
        table.insert(source, "\n" .. Config.Categories.NON_BENGALI)
    end
    if state.hasError then
        table.insert(source, "\n" .. Config.Categories.HAS_ERROR)
    end
    
    return table.concat(source, "")
end

function p.formatSourceWithCheck(frame)
    local success, result = pcall(p.formatSource, frame)
    return success and result or Util.wrapError(Config.Messages.FORMAT_ERROR .. result)
end

return p