Module:В чи у: Difference between revisions

From Test Wiki
Jump to navigation Jump to search
Content deleted Content added
test
(No difference)

Revision as of 17:25, 30 September 2025

Documentation for this module may be created at Module:В чи у/doc

local p = {}

-- Таблиця для перетворення чисел на слова (останнє слово числівника)
local numberWords = {
    -- Одиниці
    ["0"] = "нуль",
    ["1"] = "один",
    ["2"] = "два",
    ["3"] = "три",
    ["4"] = "чотири",
    ["5"] = "п'ять",
    ["6"] = "шість",
    ["7"] = "сім",
    ["8"] = "вісім",
    ["9"] = "дев'ять",
    ["10"] = "десять",
    ["11"] = "одинадцять",
    ["12"] = "дванадцять",
    ["13"] = "тринадцять",
    ["14"] = "чотирнадцять",
    ["15"] = "п'ятнадцять",
    ["16"] = "шістнадцять",
    ["17"] = "сімнадцять",
    ["18"] = "вісімнадцять",
    ["19"] = "дев'ятнадцять",
    ["20"] = "двадцять",
    ["30"] = "тридцять",
    ["40"] = "сорок",
    ["50"] = "п'ятдесят",
    ["60"] = "шістдесят",
    ["70"] = "сімдесят",
    ["80"] = "вісімдесят",
    ["90"] = "дев'яносто",
    ["100"] = "сто",
    ["200"] = "двісті",
    ["300"] = "триста",
    ["400"] = "чотириста",
    ["500"] = "п'ятсот",
    ["600"] = "шістсот",
    ["700"] = "сімсот",
    ["800"] = "вісімсот",
    ["900"] = "дев'ятсот",
    ["1000"] = "тисяча",
    ["2000"] = "дві тисячі",
    ["1000000"] = "мільйон",
    ["1000000000"] = "мільярд",
    ["1000000000000"] = "трильйон"
}

-- Функція для визначення, чи є символ голосним
local function isVowel(char)
    local vowels = "аеєиіїоуюяАЕЄИІЇОУЮЯ"
    return vowels:find(char, 1, true) ~= nil
end

-- Функція для визначення, чи є символ приголосним
local function isConsonant(char)
    local consonants = "бвгґджзклмнпрстфхцчшщйБВГҐДЖЗКЛМНПРСТФХЦЧШЩЙ"
    return consonants:find(char, 1, true) ~= nil
end

-- Функція для визначення останнього слова складеного числівника
local function getLastWordOfNumber(num)
    -- Видаляємо пробіли та коми
    num = tostring(num):gsub("[%s,]", "")
    
    -- Перевіряємо, чи це число
    if not num:match("^%d+$") then
        return nil
    end
    
    local n = tonumber(num)
    if not n then
        return nil
    end
    
    -- Для великих чисел визначаємо порядок
    if n >= 1000000000000 then
        local remainder = n % 1000000000000
        if remainder == 0 then
            return "трильйон"
        else
            return getLastWordOfNumber(remainder)
        end
    elseif n >= 1000000000 then
        local remainder = n % 1000000000
        if remainder == 0 then
            return "мільярд"
        else
            return getLastWordOfNumber(remainder)
        end
    elseif n >= 1000000 then
        local remainder = n % 1000000
        if remainder == 0 then
            return "мільйон"
        else
            return getLastWordOfNumber(remainder)
        end
    elseif n >= 1000 then
        local remainder = n % 1000
        if remainder == 0 then
            local thousands = math.floor(n / 1000)
            if thousands == 1 then
                return "тисяча"
            elseif thousands == 2 then
                return "тисячі"
            else
                return "тисяч"
            end
        else
            return getLastWordOfNumber(remainder)
        end
    elseif n >= 100 then
        local remainder = n % 100
        if remainder == 0 then
            return numberWords[tostring(n)]
        else
            return getLastWordOfNumber(remainder)
        end
    elseif n >= 20 then
        local remainder = n % 10
        if remainder == 0 then
            return numberWords[tostring(n)]
        else
            return numberWords[tostring(remainder)]
        end
    else
        return numberWords[tostring(n)]
    end
end

-- Функція для отримання першого слова числівника
local function getFirstWordOfNumber(num)
    num = tostring(num):gsub("[%s,]", "")
    
    if not num:match("^%d+$") then
        return nil
    end
    
    local n = tonumber(num)
    if not n then
        return nil
    end
    
    -- Визначаємо перше слово
    if n >= 1000000000000 then
        local trillions = math.floor(n / 1000000000000)
        return getLastWordOfNumber(trillions)
    elseif n >= 1000000000 then
        local billions = math.floor(n / 1000000000)
        return getLastWordOfNumber(billions)
    elseif n >= 1000000 then
        local millions = math.floor(n / 1000000)
        return getLastWordOfNumber(millions)
    elseif n >= 1000 then
        local thousands = math.floor(n / 1000)
        return getLastWordOfNumber(thousands)
    else
        return numberWords[tostring(n)]
    end
end

-- Функція для отримання останнього символу слова або числа
local function getLastChar(word)
    if not word or word == "" then
        return nil
    end
    
    word = word:match("^%s*(.-)%s*$")
    
    -- Якщо це число
    if word:match("^%d+$") then
        local lastWord = getLastWordOfNumber(word)
        if lastWord then
            return mw.ustring.sub(lastWord, -1)
        end
    end
    
    return mw.ustring.sub(word, -1)
end

-- Функція для отримання першого символу слова або числа
local function getFirstChar(word)
    if not word or word == "" then
        return nil
    end
    
    word = word:match("^%s*(.-)%s*$")
    
    -- Якщо це число
    if word:match("^%d+$") then
        local firstWord = getFirstWordOfNumber(word)
        if firstWord then
            return mw.ustring.sub(firstWord, 1, 1)
        end
    end
    
    return mw.ustring.sub(word, 1, 1)
end

-- Перевірка на спеціальні буквосполучення (в, ф, льв, зв, св, дв, тв, гв, хв)
local function startsWithSpecialCombination(word)
    if not word or word == "" then
        return false
    end
    
    word = word:match("^%s*(.-)%s*$")
    
    -- Якщо це число, перевіряємо його текстове представлення
    if word:match("^%d+$") then
        local firstWord = getFirstWordOfNumber(word)
        if firstWord then
            word = firstWord
        end
    end
    
    local lower = mw.ustring.lower(word)
    
    -- Перевірка на в або ф
    if lower:sub(1, 1) == "в" or lower:sub(1, 1) == "ф" then
        return true
    end
    
    -- Перевірка на буквосполучення льв, зв, св, дв, тв, гв, хв
    local specialCombinations = {"льв", "зв", "св", "дв", "тв", "гв", "хв"}
    for _, combo in ipairs(specialCombinations) do
        if mw.ustring.sub(lower, 1, mw.ustring.len(combo)) == combo then
            return true
        end
    end
    
    return false
end

-- Перевірка, чи слово починається з кількох приголосних
local function startsWithMultipleConsonants(word)
    if not word or word == "" then
        return false
    end
    
    word = word:match("^%s*(.-)%s*$")
    
    -- Якщо це число, перевіряємо його текстове представлення
    if word:match("^%d+$") then
        local firstWord = getFirstWordOfNumber(word)
        if firstWord then
            word = firstWord
        end
    end
    
    if mw.ustring.len(word) < 2 then
        return false
    end
    
    local firstChar = mw.ustring.sub(word, 1, 1)
    local secondChar = mw.ustring.sub(word, 2, 2)
    
    return isConsonant(firstChar) and isConsonant(secondChar)
end

-- Основна функція для визначення прийменника
function p.determine(frame)
    local args = frame.args
    if not args[1] or args[1] == "" then
        args = frame:getParent().args
    end
    
    local previousWord = args[1] or ""
    local nextWord = args[2] or ""
    
    -- Видаляємо пробіли
    previousWord = previousWord:match("^%s*(.-)%s*$")
    nextWord = nextWord:match("^%s*(.-)%s*$")
    
    -- Якщо немає наступного слова, повертаємо порожній рядок
    if nextWord == "" then
        return ""
    end
    
    local lastChar = getLastChar(previousWord)
    local firstChar = getFirstChar(nextWord)
    
    -- Правило 1.4: перед в, ф та спеціальними буквосполученнями завжди "у"
    if startsWithSpecialCombination(nextWord) then
        return "у"
    end
    
    -- Правило 1.3: якщо наступне слово починається з кількох приголосних
    if startsWithMultipleConsonants(nextWord) then
        return "у"
    end
    
    -- Якщо попереднє слово порожнє або його немає
    if previousWord == "" or not lastChar then
        -- На початку речення
        if isVowel(firstChar) then
            return "в" -- Правило 2.2
        else
            return "у" -- Правило 1.2
        end
    end
    
    -- Якщо попереднє слово закінчується на голосний
    if isVowel(lastChar) then
        if isVowel(firstChar) then
            return "в" -- Правило 2.1
        elseif startsWithSpecialCombination(nextWord) then
            return "у" -- Правило 1.4
        else
            return "в" -- Правило 2.6 (за замовчуванням після голосного перед більшістю приголосних)
        end
    end
    
    -- Якщо попереднє слово закінчується на приголосний
    if isConsonant(lastChar) then
        if isVowel(firstChar) then
            return "в" -- Правило 2.3
        else
            return "у" -- Правило 1.1
        end
    end
    
    -- За замовчуванням
    return "у"
end

-- Функція для використання в шаблоні з автоматичним додаванням наступного слова
function p.full(frame)
    local args = frame.args
    if not args[1] or args[1] == "" then
        args = frame:getParent().args
    end
    
    local previousWord = args[1] or ""
    local nextWord = args[2] or ""
    
    local preposition = p.determine(frame)
    
    return preposition .. " " .. nextWord
end

return p