Модуль:CountryMetaCatМодуль используется для автокатегоризации категорий по странам (для категорий с заголовком, включающим ). Возможности
Используемые списки данных для стран:
Использование{{#invoke:CountryMetaCat|main |Появились <в стране> |События <части света>!текст для сортировки }} Категория состоит из 4-х частей, разделенных Переменные
Именительный, родительный и предложный падежи для стран, частей света и государств подставляются автоматически, соответственно указанным переменным. Вариант предложного падежа у стран и государств автоматически выводится с нужным предлогом «в/во/на». Для частей света в предложном падеже автоматически ставится предлог «в». Следующие символы, указанные перед названием категории, осуществляют механизм проверки на существование категорий:
Для отдельных стран, расположенных на двух частях света или входящих в два государства, выполняется механизм раздваивания категорий с соответствующими переменными. Если переменная указана лишь в качестве ключа сортировки, то категория публикуется только один раз. Проверка на существование категорий осуществляется для каждой из частей света или государства в названии. Если одна из категорий не существует, то будет опубликована замена для соответствующей переменной. Полная версия{{#invoke:CountryMetaCat|main |Категория 1![ключ сортировки] |?Категория 2![ключ сортировки] |~Категория 3![ключ сортировки] ... |Категория N[...] }} Дополнительные параметры: |title = заголовок страницы, используемый вместо текущего (для тестов) |noindex = 1 (указывается, если необходимо отключить добавления шаблона индекса) Дополнительные функцииresolve_countryФункция используется для экспорта в другие модули. Принимает заголовок страницы (или принимает title) + строку. Обрабатывает строку, если в ней содержалась заготовка страны, части света или государства. Возвращает таблицу со значениями:
Категории отслеживания
См. также
local p = {}
local getArgs = require('Модуль:Arguments').getArgs
local findCountry = require('Модуль:Find country')
local error_category = '[[Категория:Википедия:Страницы с некорректным использованием модуля CountryMetaCat]]'
local errors = {}
--------------- Отслеживание ошибок ---------------
-- Добавляет сообщение об ошибке в список с указанием кода ошибки
local function add_error(error_code, additional_info)
local error_specific = {
[1] = 'Ошибка: страна не найдена.',
[2] = 'Ошибка: часть света не найдена для страны ' .. (additional_info or '') .. '.'
}
local error_text = error_specific[error_code]
if error_text then
table.insert(errors, '<span class="error">' .. error_text .. '</span>')
end
end
-- Возвращает строку с ошибками и очищает список ошибок
local function get_errors()
local result = table.concat(errors)
if result ~= "" then
result = result .. error_category
end
errors = {} -- Очищаем ошибки после получения
return result
end
---------------- Обработка государств и частей света ----------------
-- Проверяет наличие указанных плейсхолдеров в аргументах
local function has_placeholders(args, placeholders)
for _, value in pairs(args) do
if type(value) == "string" then
for _, placeholder in ipairs(placeholders) do
if mw.ustring.find(value, placeholder, 1, true) then
return true
end
end
end
end
return false
end
-- Проверяет наличие плейсхолдеров для частей света
local function has_continent_placeholders(args)
return has_placeholders(args, {'<часть света>', '<части света>', '<в части света>'})
end
-- Проверяет наличие плейсхолдеров для государств
local function has_state_placeholders(args)
for _, value in pairs(args) do
if type(value) == "string" then
if mw.ustring.find(value, '<государство[^>]*>') or
mw.ustring.find(value, '<государства[^>]*>') or
mw.ustring.find(value, '<в государстве[^>]*>') then
return true
end
end
end
return false
end
-- Загрузка данных о государствах из JSON
local function load_states_data()
return mw.loadJsonData('Модуль:CountryMetaCat/state-data.json')
end
-- Проверяет, попадает ли заданный год в указанный период (век, десятилетие, год)
local function check_time_period(start_year, end_year, type, time)
local year = tonumber(time)
if not year then return false end
if type == "century" then
local century_start = (year - 1) * 100 + 1
local century_end = year * 100
return (not end_year and start_year <= century_end) or
(start_year <= century_end and end_year and end_year >= century_start)
elseif type == "decade" then
local decade_start = math.floor(year / 10) * 10
local decade_end = decade_start + 9
return (not end_year and start_year <= decade_end) or
(start_year <= decade_end and end_year and end_year >= decade_start)
else -- year
return (not end_year and start_year <= year) or
(start_year <= year and end_year and end_year >= year)
end
end
-- Безопасно возвращает название страны в нужном падеже
local function get_safe_case(country, case)
if not country then return "" end
local result = findCountry.findcountryinstring(country, case)
if not result or result:match("^Ошибка") then result = "" end
return result
end
-- Находит государства по временному периоду, и сортирует их
local function find_states_by_time(country, type, time)
local states = {}
local states_data = load_states_data()
local country_data = states_data.country[country]
if not country_data then return states end
-- Создаем массив с годами для сортировки
local states_with_years = {}
for state_name, years in pairs(country_data) do
local start_year = tonumber(years[1])
local end_year = years[2] and tonumber(years[2]) or nil
if check_time_period(start_year, end_year, type, time) then
table.insert(states_with_years, {
name = state_name,
start_year = start_year,
end_year = end_year,
cases = {
['именительный'] = get_safe_case(state_name, 'именительный'),
['родительный'] = get_safe_case(state_name, 'родительный'),
['предлог'] = get_safe_case(state_name, 'предлог')
}
})
end
end
-- Сортируем по начальному году (по возрастанию)
table.sort(states_with_years, function(a, b)
return a.start_year < b.start_year
end)
-- При запросе со временем возвращаем все подходящие государства для дальнейшей обработки
return states_with_years
end
-- Проверяет соответствие названия государства из плейсхолдера с указанным государством
local function check_state_match(state, placeholder)
if not state then return false end
local required_state_name = mw.ustring.match(placeholder, ":([^>]+)>")
if not required_state_name then return true end -- если нет конкретного требования, разрешаем любое государство
-- Нормализуем названия для корректного сравнения
required_state_name = mw.ustring.gsub(required_state_name, "%s+", " ")
local state_name = mw.ustring.gsub(state.name or "", "%s+", " ")
-- Проверяем на исключения (формат ^Страна1^Страна2^...)
if mw.ustring.match(required_state_name, "^%^") then
-- Разбиваем строку исключений на отдельные страны
local excluded_states = {}
for excluded_state in mw.ustring.gmatch(required_state_name, "%^([^%^]+)") do
excluded_states[mw.ustring.gsub(excluded_state, "%s+", " ")] = true
end
-- Возвращаем true, если государство НЕ в списке исключений
return not excluded_states[state_name]
else
-- Обычное сравнение для включения конкретной страны
return state_name == required_state_name
end
end
-- Заменяет плейсхолдеры в тексте на значения из таблицы
local function replace_placeholders(text, replacements)
for placeholder, value in pairs(replacements) do
text = mw.ustring.gsub(text, placeholder, value)
end
return text
end
-- Удаляет неиспользованные плейсхолдеры из текста
local function remove_unused_placeholders(text, placeholders)
for _, placeholder in ipairs(placeholders) do
text = mw.ustring.gsub(text, placeholder, '')
end
return text
end
-- Вспомогательная функция для замены плейсхолдера государства в нужном падеже
local function replace_state_placeholder(result, pattern, case, state, is_question)
return result:gsub(pattern, function(state_name)
if check_state_match(state, pattern:gsub(":([^>]+)>", ":" .. state_name .. ">")) then
return state.cases[case] or ""
end
return is_question and (pattern:gsub("([^>]+)>", state_name .. ">")) or ""
end)
end
--------------- Обработка всех плейсхолдеров ---------------
-- Основная обработка всех плейсхолдеров с заменой
local function process_placeholders(s, country, continent, state, is_question)
if not s then return "" end
local result = s
local has_exclamation_space = mw.ustring.find(s, "! ")
-- Проверка на пустой возврат для не "?"
if not is_question then
if not state and result:match('<[^>]*государств[^>]*>') then
return ""
end
if state and result:match('<[^>]+:[^>]+>') then
local found_match = false
for pattern in result:gmatch('<[^>]+:[^>]+>') do
if check_state_match(state, pattern) then
found_match = true
break
end
end
if not found_match then return "" end
end
end
-- Обработка плейсхолдеров государства с использованием вспомогательной функции
if state then
result = replace_placeholders(result, {
['<государство>'] = state.cases['именительный'],
['<государства>'] = state.cases['родительный'],
['<в государстве>'] = state.cases['предлог']
})
result = replace_state_placeholder(result, '<государство:([^>]+)>', 'именительный', state, is_question)
result = replace_state_placeholder(result, '<государства:([^>]+)>', 'родительный', state, is_question)
result = replace_state_placeholder(result, '<в государстве:([^>]+)>', 'предлог', state, is_question)
end
-- Обработка плейсхолдеров частей света (без изменений)
if result:match('<[^>]*част[^>]*света[^>]*>') then
if continent and continent.cases then
result = replace_placeholders(result, {
['<часть света>'] = continent.cases['именительный'] or '',
['<части света>'] = continent.cases['родительный'] or '',
['<в части света>'] = continent.cases['предложный'] and ('в ' .. continent.cases['предложный']) or ''
})
else
add_error(2, country)
result = remove_unused_placeholders(result, {
'<часть света>', '<части света>', '<в части света>'
})
end
end
-- Обработка плейсхолдеров страны (без изменений)
if country then
result = replace_placeholders(result, {
['<страна>'] = get_safe_case(country, 'именительный') or "",
['<страны>'] = get_safe_case(country, 'родительный') or "",
['<в стране>'] = get_safe_case(country, 'предлог') or ""
})
elseif not is_question then
result = remove_unused_placeholders(result, {
'<страна>', '<страны>', '<в стране>'
})
end
-- Финальная обработка (без изменений)
result = mw.ustring.gsub(result, " !", "!")
result = mw.ustring.gsub(result, "[%!%s]+$", "")
if has_exclamation_space and not mw.ustring.find(result, "!") then
result = result .. "! "
end
return result
end
--------------- Обработка и публикация категорий ---------------
-- Генерирует комбинации частей света и государств
local function combinations_categories(country, args)
local combinations = {}
local continents = {}
local states = {}
-- Загрузка данных о частях света
if has_continent_placeholders(args) then
local continents_data = mw.loadJsonData('Модуль:CountryMetaCat/country-continents.json')
for _, continent in ipairs(continents_data.continents) do
if continent.countries then
for _, c in ipairs(continent.countries) do
if c == country then
table.insert(continents, continent)
break
end
end
end
end
end
-- Получение государств
if has_state_placeholders(args) then
states = find_states_by_time(country, args.type or "year", args.time or os.date("%Y"))
end
-- Вспомогательная функция для добавления комбинаций
local function add_combinations(continents_list, states_list)
if #continents_list == 0 and #states_list == 0 then
table.insert(combinations, { continent = nil, state = nil })
else
for _, continent in ipairs(continents_list) do
for _, state in ipairs(states_list) do
table.insert(combinations, { continent = continent, state = state })
end
end
-- Добавляем оставшиеся комбинации, если один из списков пуст
if #continents_list > 0 and #states_list == 0 then
for _, continent in ipairs(continents_list) do
table.insert(combinations, { continent = continent, state = nil })
end
elseif #continents_list == 0 and #states_list > 0 then
for _, state in ipairs(states_list) do
table.insert(combinations, { continent = nil, state = state })
end
end
end
end
-- Добавляем комбинации частей света и государств
add_combinations(continents, states)
return combinations
end
-- Проверка существования категории
local function category_exists(category_name)
if not category_name or category_name == '' then return false end
local clean_category_name = mw.ustring.match(category_name, "^(.-)!")
clean_category_name = clean_category_name or category_name
local title = mw.title.new('Категория:' .. clean_category_name)
return title and title.exists
end
-- Обрабатывает и добавляет уникальные категории на основе комбинаций частей света и государств
local function process_and_add_categories(text, country, combinations, results, added_categories, check_exists)
for _, combination in ipairs(combinations) do
if not combination.state or check_state_match(combination.state, text) then
local processed = process_placeholders(text, country, combination.continent, combination.state, check_exists)
if processed ~= "" then
local category_name = mw.ustring.match(processed, "^(.-)!") or processed
if not added_categories[category_name] then
table.insert(results, string.format('[[Категория:%s]]',
mw.ustring.gsub(processed, "!", "|")))
added_categories[category_name] = true
end
end
end
end
end
-- Создание категорий на основе аргументов и комбинаций
local function create_categories(args, country, combinations)
local results = {}
local added_categories = {}
if #combinations == 0 and has_continent_placeholders(args) then
add_error(2, country)
end
local ordered_args = {}
for i, arg in pairs(args) do
if type(arg) == "string" and arg ~= "" and type(i) == "number" then
table.insert(ordered_args, {index = i, value = arg})
end
end
table.sort(ordered_args, function(a, b) return a.index < b.index end)
local i = 1
while i <= #ordered_args do
local arg_data = ordered_args[i]
local arg = arg_data.value
if type(arg) == "string" and arg ~= "" then
local first_char = mw.ustring.sub(arg, 1, 1)
local rest_string = mw.ustring.sub(arg, 2):gsub("^%s+", "")
if first_char == '?' then
local replacements = {}
local j = i + 1
while j <= #ordered_args and mw.ustring.sub(ordered_args[j].value, 1, 1) == '~' do
local replacement_text = mw.ustring.sub(ordered_args[j].value, 2):gsub("^%s+", "")
table.insert(replacements, replacement_text)
j = j + 1
end
i = j - 1
-- Обрабатываем каждую комбинацию отдельно
for _, combination in ipairs(combinations) do
local processed = process_placeholders(rest_string, country, combination.continent, combination.state, true)
if processed ~= "" then
local category_name = mw.ustring.match(processed, "^(.-)!") or processed
if category_exists(category_name) then
-- Если категория существует, добавляем её
if not added_categories[category_name] then
table.insert(results, string.format('[[Категория:%s]]',
mw.ustring.gsub(processed, "!", "|")))
added_categories[category_name] = true
end
else
-- Если категория не существует, обрабатываем замены для этой комбинации
for _, replacement in ipairs(replacements) do
local replacement_processed = process_placeholders(replacement, country,
combination.continent, combination.state, false)
if replacement_processed ~= "" then
local replacement_category = mw.ustring.match(replacement_processed, "^(.-)!") or replacement_processed
if not added_categories[replacement_category] then
table.insert(results, string.format('[[Категория:%s]]',
mw.ustring.gsub(replacement_processed, "!", "|")))
added_categories[replacement_category] = true
end
end
end
end
end
end
elseif first_char ~= '~' then
-- Обработка обычных категорий без проверки существования
process_and_add_categories(arg, country, combinations, results, added_categories, false)
end
end
i = i + 1
end
return table.concat(results)
end
-- Основная функция модуля
function p.main(frame)
local args = getArgs(frame)
local title = args.title or mw.title.getCurrentTitle().text
if mw.title.getCurrentTitle().namespace == 10 then
return "[[Категория:Шаблоны, использующие модуль CountryMetaCat]]" ..
"[[Категория:Шаблоны, использующие индекс категории (автоматический)]]"
end
local country = findCountry.findcountryinstring(title, 'именительный')
if not country or country == "" then
add_error(1)
return get_errors()
end
local combinations = combinations_categories(country, args)
local result = create_categories(args, country, combinations)
if args.noindex ~= "1" then
result = mw.getCurrentFrame():preprocess('{{индекс категории (автоматический)}}') .. result
end
return result .. get_errors()
end
-- Функция для внешней обработки стран
function p._resolve_country(args)
local title = args.title or mw.title.getCurrentTitle().text
local country = findCountry.findcountryinstring(title, 'именительный')
local text = args[1] or ""
local is_question = text:match("^%?")
local has_state = text:match('<[^>]*государств[^>]*>')
local has_specific_state = text:match('<[^>]+:[^>]+>')
local result = {
result = "",
extra_result = nil,
error = country and country ~= "" and 0 or 1
}
-- Получаем комбинации, если страна найдена
local combinations = country and country ~= "" and combinations_categories(country, args) or {}
-- Ранний выход, если это ? с названием государства, но комбинаций нет
if is_question and has_state and #combinations == 0 then
result.result = process_placeholders(text, country, nil, nil, true)
return result
end
-- Проверка на ошибку части света
if #combinations > 0 and has_continent_placeholders(args) and not combinations[1].continent then
result.error = 2
end
-- Сбор допустимых результатов
local valid_results = {}
for _, combo in ipairs(combinations) do
local should_process = is_question and
(not has_specific_state or (combo.state and check_state_match(combo.state, text))) or
(not has_state or (combo.state and check_state_match(combo.state, text)))
if should_process then
local processed = process_placeholders(text, country, combo.continent, combo.state, is_question)
if processed ~= "" then
table.insert(valid_results, processed)
if #valid_results >= 2 then break end -- We only need max 2 results
end
end
end
-- Результаты
if #valid_results > 0 then
result.result = valid_results[1]
result.extra_result = valid_results[2]
elseif is_question and has_state then
result.result = process_placeholders(text, country,
combinations[1] and combinations[1].continent, nil, true)
else
result.result = process_placeholders(text, country, nil, nil, is_question)
end
return result
end
function p.resolve_country(frame)
local args = getArgs(frame)
return p._resolve_country(args)
end
return {
main = p.main,
resolve_country = p.resolve_country
}
|
Portal di Ensiklopedia Dunia