Модуль:CenturyMetaCat

Материал из Народные Сказки

Модуль используется для навигиции и категоризации категорий по векам (для категорий с заголовком, включающим «<римские цифры> век/века/веке»).

  • Определяет век и эру (до н.э / н.э.).
  • Создаёт навигационную линейку по векам.
  • Корректно работает с веками до нашей эры.
  • Добавляет категории.

Использование[править код]

{{#invoke:CenturyMetaCat|main
|Мир в <век> веке
|Мир <тысячелетие>-го тысячелетия! <ключ>
|Мир по векам! <ключ>
}}
  • <век> — век римскими цифрами без слова «век»
  • <тысячелетие> — тысячелетие числом (без окончания -е/-м/-го)
  • <ключ> — ключ сортировки, н. э. — номер века числом, до н. э. — отрицательное число начиная с -99 (-99 == I век до н. э. -98 == II век до н. э. и т. д.); нужен для корректной сортировки в категориях

Полная версия

{{#invoke:CenturyMetaCat|main
|Категория 1![ключ сортировки]![диапазон веков от]![диапазон веков до]
...
|Категория N[...]
|min = до какого века рисовать линейку слева, по умолчанию -39 (0 — рисовать только века нашей эры)
|max = до какого века рисовать линейку справа, по умолчанию 21
|range = сколько веков в линейке слева и справа, по умолчанию 5
|title = заголовок страницы, используемый вместо текущего (для тестов)
}}

Категории[править код]

Категория состоит из 4-х полей, разделенных ! (восклицательным знаком):

  • первое — название категории
  • второе — ключ сортировки (необязательно)
  • третье — начиная с какого века добавлять категорию (необязательно)
  • четвертое — каким веком заканчивать добавление категории (необязательно)

Примеры:

  • |Мир по векам! <ключ> — добавлять категорию «Мир по векам» с ключом сортировки <пробел><ключ>
  • |Графы Средних веков!<ключ>!5!15 — добавлять категорию «Графы Средних веков» в категории с V по XV века
  • |Книги в общественном достоянии!<ключ>!!19 — добавлять категорию «Книги в общественном достоянии» во все категории до XIX века включительно
Шаблон:NB

Есть эмпирическое правило: в категории обязательно должен быть или <век> (в названии), или <ключ> (в ключе сортировки), но не оба.

Другие опции[править код]

|min = до какого века рисовать линейку слева, по умолчанию -39 (0 — рисовать только века нашей эры)
|max = до какого века рисовать линейку справа, по умолчанию 21
|range = сколько веков в линейке слева и справа, по умолчанию 5
|title = заголовок страницы, используемый вместо текущего (для тестов)

Дополнительные функции[править код]

expand[править код]

  • заменяет <век> на текущий, по необходимости добавив «до н. э.»
  • заменяет <тысячелетие> на текущее, по необходимости добавив «до н. э.»
  • заменяет <ключ> на ключ сортировки

Пример:

{{#invoke:CenturyMetaCat|expand|Мир в <век> веке}}

на странице «К:Земля I века до н. э.» вернёт:

Мир в I веке до н. э.

century_from_title[править код]

Возвращает век из заголовка числом, для веков до н.э. с минусом.

См. также[править код]


---*- mode: lua; coding: utf-8; -*-

local p = {}

-- константы
local ROMAN = {
    I = 1,
    IV = 4,
    V = 5,
    IX = 9,
    X = 10,
    --XL = 40,
    --L = 50,
}

-- переменные
local cent -- век, положительное число
local roman_cent -- век римскими цифрами
local BC   -- 0 == н.э. 1 == до н.э.
local templ -- строка-шаблон вида 'Мир в %s веке%s'
local title = mw.title.getCurrentTitle().text

-- опции
local cent_min -- число, по умолчанию -39, 0 == только н.э.
local cent_max -- число, по умолчанию 21 (XXI век)
local range = 5

-- экспортируемые функции
local getArgs = require('Module:Arguments').getArgs
local sparseIpairs = require('Module:TableTools').sparseIpairs
local toroman = require('Module:Roman').convert
local gsub = mw.ustring.gsub

function roman_to_int(s)
    local i = 1
    local num = 0
    while i <= s:len() do
        local c
        if i < s:len() then
            c = ROMAN[s:sub(i, i+1)]
        end
        if c then
            num = num + c
            i = i + 2
        else
            num = num + ROMAN[s:sub(i, i)]
            i = i + 1
        end
    end
    return num
end

local function get_templ(s)
    -- формируем строку-шаблон вида:
    -- 'Мир в XI веке до н. э.' -> 'Мир в %s веке%s'
    -- определяем BC 
    local t
    t, BC = gsub(s, '[IVX]+ (век[еа]?) до н%. э%.', '%%s %1%%s')
    local n = BC
    if BC ~= 1 then
        t, n = gsub(s, '[IVX]+ (век[еа]?)', '%%s %1%%s')
    end
    if n ~= 1 then
        -- не найдено или найдено больше одного
        error('Век не найден')
    end
    -- в/во, о/об
    t = gsub(t, 'во %%s веке', 'в %%s веке')
    templ = gsub(t, 'об %%s веке', 'о %%s веке')
end

local function get_cent(t)
    _, _, roman_cent = mw.ustring.find(t, '([IVX]+) век')
    if not roman_cent then error('век не найден') end
    cent = roman_to_int(roman_cent)
end

local function format(c, wiki)
    local bcs, t
    if c < 1 then
        c = 1 - c
        bcs = ' до н. э.'
        --t = toroman(c)..' до н. э.'
        t = '-'..toroman(c)
    else
        bcs = ''
        t = toroman(c)
    end
    local s
    if wiki then
        -- в/во, о/об
        local tt = templ
        if c == 2 then
            tt = gsub(tt, 'в %%s веке', 'во %%s веке')
        end
        if c == 11 then
            tt = gsub(tt, 'о %%s веке', 'об %%s веке')
        end
        s = string.format(tt, toroman(c), bcs)
        s = string.format('[[:Категория:%s|%s]]', s, t)
    else
        s = t
    end
    return s
end

local function navbox()
    local c
    c = cent
    if BC == 1 then
        -- пропускаем 0
        -- I до н.э. c == 0, II до н.э. c == -1 и т.д.
        c = 1 - cent
    end
    local wt = mw.html.create('table'):addClass('standard'):attr('align', 'center')
    local row = wt:tag('tr')
    local cstart
    if cent_min < 1 then
        cstart = math.max(cent_min+1, c - range)
    else
        cstart = math.max(cent_min, c - range)
    end
    local cend = math.min(cent_max, c + range) -- FIXME: до н.э.
    for i = cstart, cend do
        if i == 1 and i ~= cstart then row:tag('th'):wikitext('') end -- разд. до н.э./н.э.
        if i == c then
            row:tag('th'):wikitext(format(i, false))
        else
            row:tag('td'):wikitext(format(i, true))
        end
    end
    return tostring(wt)
end

local function do_expand(s)
-- <век> - век римскими цифрами без слова "век" (XVII)
-- <тысячелетие> - тысячелетие числом (без добавления -е/-м/-го)
-- <ключ> - ключ сортировки, н.э. - номер века числом,
-- до н.э. - отрицательное число начиная с -99 (-99 == I век до н.э. -98 == II век до н.э. и т.д.)
    local mil = math.floor((cent-1)/10)+1 -- тысячелетие
    -- в/во, о/об ?
    if mil == 2 then
        s = gsub(s, ' в <тысячелетие>', ' во <тысячелетие>')
    end
    if BC == 1 then
        s = gsub(s, '<век> (век[еа]?)', roman_cent .. ' %1 до н. э.')
        s = gsub(s, '<тысячелетие>(-[емг][о]? тысячелети[еия])', mil..'%1 до н. э.') -- 2-е/2-м/2-го
        s = gsub(s, '<ключ>', cent-100)
    else
        s = gsub(s, '<век>', roman_cent)
        s = gsub(s, '<тысячелетие>', mil)
        s = gsub(s, '<ключ>', cent)
    end
    return s
end

local function cats(args)
    local c, t
    local ret = ''
    for _, c in sparseIpairs(args) do
        t = mw.text.split(c, '!', true)
        -- диапазон веков для кат.
        local cmin = -99
        if t[3] and t[3] ~= '' then
            cmin = tonumber(t[3])
        end
        local cmax = 99
        if t[4] and t[4] ~= '' then
            cmax = tonumber(t[4])
        end
        local cc = cent
        if BC == 1 then cc = -cent end
        if cc >= cmin and cc <= cmax then
            -- в/во, о/об
            local tt = t[1]
            if cent == 2 then
                tt = gsub(tt, 'в <век> веке', 'во <век> веке')
            end
            if cent == 11 then
                tt = gsub(tt, 'о <век> веке', 'об <век> веке')
            end
            if t[2] and t[2] ~= '' then
                ret = ret .. string.format('[[Категория:%s|%s]]', tt, t[2])
            else
                ret = ret .. string.format('[[Категория:%s]]', tt)
            end
        end
    end
    return do_expand(ret)
end

function p.main(frame)
    local args = getArgs(frame)
    title = args['title'] or title
    -- разбор аргументов
    range = tonumber(args['range'] or 5)
    cent_min = tonumber(args['min'] or -39)
    cent_max = tonumber(args['max'] or 21)
    -- нахождение текушего века
    get_cent(title)
    -- создание шаблона-строки
    get_templ(title)
    -- создание навбокса и категорий
    return navbox(title) .. cats(args)
end

function p.expand(frame)
    local args = getArgs(frame)
    title = args['title'] or title
    get_cent(title)
    BC = mw.ustring.find(title, '[IVX]+ век[еа]? до н%. э%.')
    if BC then
        BC = 1
    else
        BC = 0
    end
    -- в/во, о/об
    local tt = args[1]
    if cent == 2 then
        tt = gsub(args[1], 'в <век> веке', 'во <век> веке')
    end
    if cent == 11 then
        tt = gsub(tt, 'о <век> веке', 'об <век> веке')
    end
    return do_expand(tt)
end

function p.century_from_title(frame)
    local args = getArgs(frame)
    title = args['title'] or title
    BC = mw.ustring.find(title, '[IVX]+ век[еа]? до н%. э%.')
    get_cent(title)
    if BC then
        return -cent
    end
    return cent
end

return p