Модуль:YearMetaCat2 (Bk;rl,&YearMetaCat2)
Модуль используется для полосы навигации и автокатегоризации категорий по годам (для категорий с заголовком, включающим «<число> год/года/годе»).
Возможности
- Определяет год, десятилетие, век и эру (до н. э. / н. э.).
- Обработка стран:
- Определяет страну из заголовка в любом падеже.
- Позволяет изменить падеж страны для категорий.
- Определяет, в каких частях света расположена страна и публикует их в выбранном падеже.
- Определяет, в какое государство входит (входила) страна, исходя из текущего года, позволяя опубликовать категории для любых государств в одном формате, либо указывать отдельные категории для выбранных государств.
- Позволяет проверить существование категории и опубликовать одну или несколько замен для неё.
- Добавляет {{автоиндекс}} (появляется от 200 статей, расширенный индекс от 1200 статей).
- Создаёт навигационную линейку по годам, с возможностями:
- Задавать min/max год в линейке.
- Автоматически отслеживает min/max год существования отдельных стран и выдаёт ошибку при выходе за предел.
- Позволяет указывать количество элементов в линейке.
- Добавляет категории.
Используемые списки данных для стран:
- падежные формы стран с предлогом
- разделение стран по частям света
- вхождение стран в государства по годам
- годы существования стран
Использование
{{#invoke:YearMetaCat2|main |Мир <век> века <в стране>!<ключ> |Мир <десятилетие>-х годов по странам!<ключ> |Мир <год> года <страны> |Мир по годам <в части света>!<ключ> }}
Категория состоит из 2-х частей, разделенных !
(восклицательным знаком). Первая часть — название категории, вторая часть — ключ сортировки (необязателен).
Переменные
<век>
— век римскими цифрами без слова «век»<ключ>
— ключ сортировки, н. э. — год, до н. э. — отрицательное число начиная с -10000 (-9999 == 1 год до н. э. -9998 == 2 год до н. э. и т. д.); для корректной сортировки у годов до н. э. перед минусом добавляется 0<страна>
,<страны>
,<в стране>
— страна в необходимом падеже<часть света>
,<части света>
,<в части света>
— часть света в необходимом падеже<государство>
,<государства>
,<в государстве>
— государство в необходимом падеже<государство:Название>
,<государства:Название>
,<в государстве:Название>
— дополнительная проверка, позволяющая публиковать категорию только для стран, входящих в конкретное государство. Использование символа^
перед названием государства, наоборот, исключает его из публикации среди всех остальных государств. Можно исключать сразу несколько государств, отделяя каждое из них символом^
. Для установки сложных условий отображения см. Модуль:CountryMetaCat/State.
Именительный, родительный и предложный падежи для стран, частей света и государств подставляются автоматически, соответственно указанным переменным. Вариант предложного падежа у стран и государств автоматически выводится с нужным предлогом «в/во/на». Для частей света в предложном падеже автоматически ставится предлог «в».
Следующие символы, указанные перед названием категории, осуществляют механизм проверки на существование категорий:
?
— категория публикуется только если она существует.~
— является заменой для несуществующей категории?
. Обязательно должна следовать сразу за ней на следующей строке, иначе игнорируется. Замены публикуются без проверок на существование. Для одной проверяемой категории может указываться несколько замен подряд.
Для отдельных стран, расположенных на двух частях света или входящих в два государства, выполняется механизм раздваивания категорий с соответствующими переменными. Если переменная указана лишь в качестве ключа сортировки, то категория публикуется только один раз. Проверка на существование категорий осуществляется для каждой из частей света или государства в названии. Если одна из категорий не существует, то будет опубликована замена для соответствующей переменной.
Полная версия
{{#invoke:YearMetaCat2|main |Категория 1![ключ сортировки] |?Категория 2![ключ сортировки] |~Категория 3![ключ сортировки] ... |Категория N[...] |min = до какого года рисовать линейку слева, по умолчанию -40000 (0 — рисовать только года нашей эры) |max = до какого года рисовать линейку справа, по умолчанию 2100 |range = сколько годов в линейке слева и справа, по умолчанию 5 }}
Дополнительные параметры:
|title = заголовок страницы, используемый вместо текущего |noindex = 1 (указывается, если необходимо отключить добавления шаблона индекса) |nonav = 1 (указывается, если необходимо отключить добавления навигационной линейки)
Дополнительные функции
expand
- заменяет
<год>
на текущий, по необходимости добавив «до н. э.» - заменяет
<десятилетие>
на текущее, по необходимости добавив «до н. э.» - заменяет
<век>
на текущий, по необходимости добавив «до н. э.» - заменяет
<ключ>
на ключ сортировки
Например, {{#invoke:YearMetaCat2|expand|Мир <год> года}}
на странице Категория:Земля в 100 году до н. э.
вернёт Мир 100 года до н. э.
.
Страны и части света функция не обрабатывает.
Категории отслеживания
- Википедия:Страницы с некорректным использованием модуля YearMetaCat2 (18) — отслеживание использований с несуществующими странами или частями света, а также с нарушениями диапазонов в навигационной линейке.
- Шаблоны, использующие модуль YearMetaCat2 (429) — в модуль встроено автодобавление в эту категорию страниц, на которых он используется, при условии что страница является шаблоном. Однако, так как проверка пространства страницы и размещение происходит через код модуля, то необходимо избегать помещения модуля в тег
<includeonly></includeonly>
на странице шаблона. Модуль нужно размещать вне любых подобных тэгов. - Категория:Шаблоны, использующие индекс категории (автоматический)
См. также
- Модуль:DecadeMetaCat — аналог для десятилетий
- Модуль:CenturyMetaCat — аналог для веков
- Модуль:CountryMetaCat — аналог для стран
- Модуль:YearMetaCat — аналог для годов и десятилетий
- Модуль:MetaCatDoc — для документирования шаблонов, использующих этот модуль
---*- mode: lua; coding: utf-8; -*-
local p = {}
-- Переменные
local year -- год, положительное число
local BC -- 0 == н.э. 1 == до н.э.
local templ -- строка-шаблон вида 'Мир в %s году%s'
local title = mw.title.getCurrentTitle().text
-- Опции
local year_min = -40000 -- 0 == только н.э.
local year_max = 2100 -- XXI
local range = 5
-- Импортируемые функции
local getArgs = require('Module:Arguments').getArgs
local sparseIpairs = require('Module:TableTools').sparseIpairs
local toroman = require('Module:Roman').convert
local getStyles = require('Модуль:Индекс категории').getStyles
local gsub = mw.ustring.gsub
local findCountry = require('Модуль:Find country')
local countryModule = require('Модуль:CountryMetaCat')
-- Инициализация трекера для ошибок
local error_list = {}
local year_range_error = nil
local country_error_flag = false
local unique_errors = {}
--------------- Ошибки ---------------
-- Сбор и обработка ошибок
local function add_error(error_code, additional_info)
local error_specific = {
[1] = 'Ошибка: год не найден.',
[2] = 'Минимальный год, ограниченный шаблоном: ' .. (additional_info or "") .. '.',
[3] = 'Максимальный год, ограниченный шаблоном: ' .. (additional_info or "") .. '.',
[4] = 'Минимальный год для ' .. (additional_info or "") .. '.',
[5] = 'Максимальный год для ' .. (additional_info or "") .. '.',
[6] = 'Ошибка: страна не найдена.',
[7] = 'Ошибка: часть света для страны не найдена.',
[8] = 'Ошибка: обнаружено два года.'
}
-- Для ошибок диапазона
if error_code >= 2 and error_code <= 5 then
if not year_range_error then
year_range_error = {message = 'Ошибка: год не попадает в заданный диапазон.', details = {}}
table.insert(error_list, year_range_error)
end
table.insert(year_range_error.details, error_specific[error_code])
else
-- Для остальных ошибок проверяем уникальность
local error_message = '<span class="error">' .. error_specific[error_code] .. '</span>'
if not unique_errors[error_message] then
unique_errors[error_message] = true
table.insert(error_list, {message = error_message})
end
end
end
-- Публикация всех ошибок в едином блоке
local function publish_errors()
local error_category = '[[Категория:Википедия:Страницы с некорректным использованием модуля YearMetaCat2]]'
if #error_list == 0 then
return ''
end
local result = '<div class="error-list">'
for _, err in ipairs(error_list) do
if err.details then
result = result .. '<span class="error">' .. err.message
for _, detail in ipairs(err.details) do
result = result .. ' ' .. detail
end
result = result .. '</span>'
else
result = result .. err.message
end
end
result = result .. '</div>'
result = result .. error_category
return result
end
--------------- Считывание и обработка годов ---------------
-- Считывание года из строки
local function get_year(t)
local years = {}
for year in mw.ustring.gmatch(t, '([0-9]+)%s*год') do
table.insert(years, tonumber(year)) -- Преобразование строки в число
end
if #years == 0 then
add_error(1) -- Ошибка "не найден"
return nil
elseif #years > 1 then
add_error(8) -- Ошибка "обнаружено два"
return nil
end
return years[1] -- Возврат единственного найденнего значения
end
-- Замена плейсхолдеров (год, десятилетие, век, ключ) на реальные значения
local function do_expand(s)
-- <год> - год без слова "год"
-- <ключ> - ключ сортировки, н.э. - номер года,
-- до н.э. - отрицательное число начиная с -99 (-99 == 1 год до н.э. -98 == 2 год до н.э. и т.д.)
-- <десятилетие> - десятилетие числом (без окончания -е/-х)
-- <век> - век римскими цифрами
local d = math.floor(year/10)*10 -- Определение десятилетия
local c = toroman(math.floor((year-1)/100)+1) -- Преобразование века в римские цифры
-- Обработка для II века (в/во)
if c == 'II' then
s = gsub(s, ' в <век>', ' во <век>')
end
-- Обработка для 2 года (в/во)
if year == 2 then
s = gsub(s, 'в <год> году', 'во <год> году')
end
if BC == 1 then
s = gsub(s, '<год> (год[ау]?)', year..' %1 до н. э.')
s = gsub(s, '<ключ>', '0' .. (year - 10000)) -- Преобразование ключа для до н.э.
s = gsub(s, '<десятилетие>(-[ех] год[ыоа][вх]?)', d..'%1 до н. э.') -- годы/годов/годах
s = gsub(s, '<век> (век[еа]?)', c..' %1 до н. э.')
else
s = gsub(s, '<год>', year)
s = gsub(s, '<ключ>', year)
s = gsub(s, '<десятилетие>', d)
s = gsub(s, '<век>', c)
end
return s
end
--------------- Обработка min/max ---------------
-- Поиск данных о стране в JSON-файле по названию или алиасу
local function find_country_in_json(country_name)
local country_data = mw.loadJsonData('Модуль:YearMetaCat2/country-years.json')
for _, country in ipairs(country_data.countries) do
if country.name == country_name then
return country
end
if country.aliases then
for _, alias in ipairs(country.aliases) do
if alias == country_name then
return country
end
end
end
end
return nil
end
-- Проверка, попадает ли год в диапазон страны или вручную заданные значения
local function check_year_in_bounds(args)
args = args or {}
local country_name = findCountry.findcountryinstring(title)
local country_data = find_country_in_json(country_name)
-- Корректировка для до н.э.
local year_adjusted = (BC == 1) and -year or year
-- Ручные ограничения min и max
local manual_min = tonumber(args['min'])
local manual_max = tonumber(args['max'])
-- Определение активных границ
local effective_min = manual_min or (country_data and country_data.min)
local effective_max = manual_max or (country_data and country_data.max)
-- Проверка минимального значения
if effective_min and year_adjusted < effective_min then
if manual_min then
-- Если задано вручную
add_error(2, tostring(manual_min))
elseif country_data then
-- Если данные из страны
add_error(4, string.format("%s: %d", country_name, country_data.min))
end
end
-- Проверка максимального значения
if effective_max and year_adjusted > effective_max then
if manual_max then
-- Если задано вручную
add_error(3, tostring(manual_max))
elseif country_data then
-- Если данные из страны
add_error(5, string.format("%s: %d", country_name, country_data.max))
end
end
end
--------------- Считывание и обработка стран ---------------
-- Проверка на наличие плейсхолдеров, связанных со странами
local function has_country_placeholders(s)
local placeholders = {
'<страна>', '<страны>', '<в стране>',
'<часть света>', '<части света>', '<в части света>',
'<государство>', '<государства>', '<в государстве>'
}
-- Проверка на стандартные плейсхолдеры
for _, placeholder in ipairs(placeholders) do
if s:find(placeholder, 1, true) then
return true
end
end
-- Проверка на плейсхолдеры с указанием названия страны (например, <государство:Название страны>)
local complex_placeholders = {
'<государство:[^>]+>',
'<государства:[^>]+>',
'<в государстве:[^>]+>'
}
for _, pattern in ipairs(complex_placeholders) do
if s:find(pattern) then
return true
end
end
return false
end
-- Обработка стран, частей света стран или государств
local function process_country_placeholders(s, title, current_year)
if type(s) ~= 'string' then return {}, nil end
local result_lines = {}
local added_categories = {}
-- Вызываем resolve_country с нужными параметрами
local country_result = countryModule.resolve_country({
[1] = s,
title = title,
type = "year",
time = current_year
})
if country_result then
-- Обработка основного результата
if country_result.result and country_result.result ~= "" then
table.insert(result_lines, {text = country_result.result, type = "main"})
end
-- Обработка дополнительного результата
if country_result.extra_result and country_result.extra_result ~= "" then
table.insert(result_lines, {text = country_result.extra_result, type = "extra"})
end
-- Обработка ошибок от country_module
if country_result.error and country_result.error > 0 then
add_error(country_result.error == 1 and 6 or 7)
country_error_flag = true
end
end
return result_lines
end
--------------- Форматирование строк ---------------
-- Формирование шаблона строки для отображения года с учётом до н. э.
local function get_templ(s)
-- Формируем строку-шаблон вида: 'Мир в 99 году до н. э.' -> 'Мир в %s году%s'
local t
t, BC = gsub(s, '[0-9]+ (год[ау]?) до н%. э%.', '%%s %1%%s')
local n = BC
if BC ~= 1 then
t, n = gsub(s, '[0-9]+ (год[ау]?)', '%%s %1%%s')
end
if n ~= 1 then
add_error(1) -- Ошибка, если совпадений нет или их больше одного
end
-- Корректировка для "во втором году"
templ = gsub(t, 'во %%s году', 'в %%s году')
end
-- Форматирование года с учётом до н. э.
local function format(y, wiki)
local bcs, t
if y < 1 then
y = 1 - y
bcs = ' до н. э.'
t = '−'..y
else
bcs = ''
t = y
end
local s
if wiki then
local tt = templ
-- Корректировка для "во втором году"
if y == 2 then
tt = gsub(templ, 'в %%s году', 'во %%s году')
end
s = string.format(tt, y, bcs)
s = string.format('[[:К:%s|%s]]', s, t)
else
s = t
end
return s
end
--------------- Список категорий ---------------
-- Проверка на существование категории
local function category_exists(category_name)
if not category_name or category_name == '' then return false end
-- Удаление символов ? ~ вначале или ! с текстом вконце
category_name = mw.ustring.match(category_name, "^[%?~]*(.-)!") or category_name
local title = mw.title.new('Категория:' .. category_name)
return title and title.exists
end
-- Основная обработка категорий
local function cats(args)
local ret = ''
local added_categories = {}
local lines = {}
-- Вспомогательная функция для добавления категории
local function add_category(text)
local processed = do_expand(text:gsub("!", "|"))
local categories = mw.text.split(processed, "|")
local cat_name = categories[1]
if not added_categories[cat_name] then
ret = ret .. string.format('[[Категория:%s%s]]',
cat_name,
categories[2] and ('|' .. categories[2]) or ''
)
added_categories[cat_name] = true
return true
end
return false
end
-- Обработка входных аргументов и заполнение lines
for i, arg in sparseIpairs(args) do
if type(arg) == "string" and arg ~= "" then
if has_country_placeholders(arg) then
local result = process_country_placeholders(arg, title, year)
lines[i] = {
original = arg,
results = result or {},
is_placeholder = true
}
else
local text = do_expand(arg)
lines[i] = {
original = arg,
results = {
{text = text, type = "main"},
{text = text, type = "extra"}
},
is_placeholder = false
}
end
end
end
local i = 1
while i <= #lines do
local line = lines[i]
if line then
local first_char = mw.ustring.sub(line.original, 1, 1)
if first_char == '?' then
local exists = {main = false, extra = false}
local questions = {main = {}, extra = {}}
-- Проверяем существование категорий
for _, result in ipairs(line.results) do
local text = result.text:sub(2):gsub("^%s+", "")
local cat_name = do_expand(text:match("^(.-)!") or text)
questions[result.type][cat_name] = text
if category_exists(cat_name) then
exists[result.type] = true
add_category(result.text:sub(2))
end
end
local j = i + 1
while j <= #lines and mw.ustring.sub(lines[j].original, 1, 1) == '~' do
for _, result in ipairs(lines[j].results) do
if not exists[result.type] and next(questions[result.type]) then
add_category(result.text:sub(2))
end
end
j = j + 1
end
i = j - 1
elseif first_char ~= '~' then
for _, result in ipairs(line.results) do
if result.text and result.text ~= "" then
add_category(result.text)
end
end
end
end
i = i + 1
end
return ret
end
--------------- Навигационный блок ---------------
local function navbox()
-- Корректировка для до н. э.
local y = (BC == 1) and 1 - year or year
local wt = mw.html.create('div'):addClass('ts-module-Индекс_категории hlist')
local row = wt:tag('ul')
-- Корректировка min и max для до н. э.
local adjusted_min = year_min <= 0 and year_min + 1 or year_min
local adjusted_max = year_max <= 0 and year_max + 1 or year_max
local country_data = find_country_in_json(findCountry.findcountryinstring(title))
-- Определение минимального и максимального года для страны
local country_min_year = math.max(adjusted_min, country_data and country_data.min or adjusted_min)
local country_max_year = math.min(adjusted_max, country_data and country_data.max or adjusted_max)
-- Определение стартового и конечного года
local ystart = math.max(country_min_year, y - range)
local yend = math.min(country_max_year, y + range)
-- Если диапазон некорректный, возвращаем пустую строку
if yend < ystart then return "" end
-- Добавляем элементы в навигационную полоску
for i = ystart, yend do
row:tag('li'):wikitext(format(i, true))
end
return getStyles() .. tostring(wt)
end
--------------- Вывод ---------------
function p.main(frame)
local args = getArgs(frame)
title = args['title'] or title
range = tonumber(args['range'] or range)
if mw.title.getCurrentTitle().namespace == 10 then -- проверка пространства шаблонов
return "[[Категория:Шаблоны, использующие модуль YearMetaCat2]]" ..
"[[Категория:Шаблоны, использующие индекс категории (автоматический)]]"
end
-- Обработка вручную заданных min и max
year_min = tonumber(args['min'] or year_min)
year_max = tonumber(args['max'] or year_max)
-- Нахождение года по заголовку страницы
year = get_year(title)
if not year then
return publish_errors() -- Возврат ошибок и прекращаем выполнение, если год не найден
end
-- Создание шаблона-строки
get_templ(title)
-- Стандартная категоризация
local categories = cats(args)
-- Проверка, попадает ли год в допустимые границы
check_year_in_bounds(args)
local output = ""
-- Навигационная полоска с отключением
if args['nonav'] ~= "1" then
output = output .. navbox(title)
end
-- Автоиндекс с отключением
if args['noindex'] ~= "1" then
output = output .. mw.getCurrentFrame():preprocess('{{индекс категории (автоматический)}}\n')
end
-- Преобразование таблицы категорий в строку, если это таблица
if type(categories) == "table" then
local flat_categories = {}
for _, value in ipairs(categories) do
table.insert(flat_categories, value.text)
end
categories = table.concat(flat_categories, '')
end
output = output .. publish_errors()
return output .. (categories or "")
end
-- Вспомогательная функция для развёртывания
function p.expand(frame)
local args = getArgs(frame)
title = args['title'] or title
year = get_year(title)
if not year then
return publish_errors()
end
BC = mw.ustring.find(title, '[0-9]+ год[ау]? до н%. э%.')
if BC then
BC = 1
else
BC = 0
end
-- в/во
local tt = args[1]
if year == 2 then
tt = mw.ustring.gsub(args[1], 'в <год> году', 'во <год> году')
end
return do_expand(tt)
end
return p