Модуль:CountryMetaCat

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
Документация

Модуль используется для навигации и автокатегоризации категорий по странам (для категорий с заголовком, включающим название стран в именительном, родительном или предложном падеже). Модуль использует механизм обнаружения модуля Модуль:Find country с его списком стран и падежей.

  • Определяет название страны из заголовка в любом падеже.
  • В категориях меняет падеж страны в нужный.
  • Определяет, в какой части света (континенте) расположена страна и публикует её/их в нужном падеже.
  • Позволяет проверить существование категории или опубликовать замену для неё.
  • Добавляет {{автоиндекс}} (появляется от 200 статей, расширенный индекс от 1200 статей).
  • Добавляет категории.

Используемые базы данных:

Использование

[править код]
{{#invoke:CountryMetaCat|main
|Появились <в стране>
|События <части света>!текст для сортировки
}}
  • <страна> — страна в именительном падеже.
  • <страны> — страна в родительном падеже.
  • <в стране> — страна в предложном падеже, включая предлог (автоматически выбирает «в/во/на»).
  • <часть света> — часть света в именительном падеже.
  • <части света> — часть света в родительном падеже.
  • <в части света> — часть света в предложном падеже, включая предлог «в».

Вызов ключа части света обрабатывает помещение в соответствующую часть света, а также в некоторые надрегионы. Для отдельных стран работает механизм разделения на несколько частей света, когда страна, расположенная одновременно в двух частях света, будет автоматически получать две категории каждой части света. В случае, если один ключей <часть света> используется только в качестве ключа сортировки, а название категории при этом не меняется, то категория публикуется только один раз с первым ключом сортировки.

Механизм проверки существования категорий

  • ? — указывается первым символом перед названием категории, которую необходимо проверять на существование. Если категория существует, она публикуется. Если не существует, ищется замена для публикации на следующей строке, которая должна начинаться с символа ~. В случае неудачи категория не публикуется.
  • ~ — указывается первым символом перед названием категории, которая публикуется (без проверки на существование) только в том случае, если на предыдущей строке категория с ? не создана. Во всех остальных случаях пропускается.

Для проверок работает механизм раздваивания по частям света. Будет проверяться на существование категория с каждой частью света и подбираться замена для соответствующей (замена также должна содержать ключ <части света>).

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

{{#invoke:CountryMetaCat|main
|Категория 1![ключ сортировки]
|?Категория 2![ключ сортировки]
|~Категория 3![ключ сортировки]
...
|Категория N[...]
|title = заголовок страницы, используемый вместо текущего (для тестов)
|noindex = 1 (указывается, если необходимо отключить добавления шаблона индекса)
}}

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

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

Дополнительные функции

[править код]
  • resolve_country — используется для экспорта в другие модули. Принимает заголовок страницы (или принимает title) + строку. Обрабатывает строку, если в ней содержалась заготовка страны или части света. Возвращает таблицу со значениями:
    • result — основной вариант строки для единственного результата (или первого континента),
    • extra_result — возможный второй вариант строки для второго континента,
    • error — код ошибки (0 — ошибки нет, 1 — страна не найдена, 2 — континент не найден).

Категории отслеживания

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

См. также

[править код]
local p = {}
local getArgs = require('Модуль:Arguments').getArgs
local sparseIpairs = require('Модуль:TableTools').sparseIpairs
local gsub = mw.ustring.gsub
local findCountry = require('Модуль:Find country')
local error_category = '[[Категория:Википедия:Страницы с некорректным использованием модуля CountryMetaCat]]'
local function error_string(s)
	return '<span class="error">' .. s .. '</span>'
end

-- Безопасное получение данных о стране в указанном падеже
local function get_safe_country_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 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 has_continent_placeholders(args)
	local continent_placeholders = {'<часть света>', '<части света>', '<в части света>'}
	for _, value in pairs(args) do
		if type(value) == "string" then
			for _, placeholder in ipairs(continent_placeholders) do
				-- Проверка строки на наличие плейсхолдеров
				if mw.ustring.find(value, placeholder, 1, true) then
					return true
				end
			end
		end
	end
	return false
end

-- Замена плейсхолдеров страны и континента на реальные значения
local function process_placeholders(s, country, continent, continents_data)
	if not s then return "" end
	-- Проверека используется ли пробел после ключа сортировки
	local has_exclamation_space = mw.ustring.find(s, "! ")
	-- Обработка подстановок для страны
	local country_cases = {
		['<страна>'] = country or "", 
		['<страны>'] = get_safe_country_case(country, 'родительный'),
		['<в стране>'] = get_safe_country_case(country, 'предлог')
	}
	for placeholder, value in pairs(country_cases) do
		s = gsub(s, placeholder, value)
	end
	-- Обработка подстановок для континента, если данные континента заданы
	if continent and continents_data then
		local continent_case = {
			['именительный'] = '', 
			['родительный'] = '', 
			['предложный'] = ''
		}
		-- Поиск данных о континенте и его падежах
		for _, continent_data in ipairs(continents_data.continents) do
			if continent_data.name == continent and continent_data.cases then
				for case, value in pairs(continent_data.cases) do
					continent_case[case] = value
				end
				break
			end
		end
		-- Замена плейсхолдеров континента
		s = gsub(s, '<часть света>', continent_case['именительный'])
		s = gsub(s, '<части света>', continent_case['родительный'])
		s = gsub(s, '<в части света>', continent_case['предложный'] ~= "" and 'в ' .. continent_case['предложный'] or '')
	else
		-- Замена на пустые значения, если континент не найден
		s = gsub(s, '<часть света>', '')
		s = gsub(s, '<части света>', '')
		s = gsub(s, '<в части света>', '')
	end
	-- Очищение строки от лишних символов
	s = gsub(s, " !", "!")
	s = mw.ustring.gsub(s, "[%!%s]+$", "")
	if has_exclamation_space and not mw.ustring.find(s, "!") then
		-- Возвращается пробел после ключа сортировки, если использовался
		s = s .. "! "
	end
	return s
end

-- Обработка категорий с использованием плейсхолдеров стран и частей света
local function process_category_placeholders(args, country, continent_list, continents_data)
	local categories = {}
	for _, arg in ipairs(args) do
		if type(arg) == "string" and arg ~= "" then
			local has_placeholders = has_continent_placeholders(args)
			local continents = has_placeholders and continent_list or {continent_list[1]}
			-- Обработка каждой категории для каждого континента
			for _, continent in ipairs(continents) do
				local processed_string = process_placeholders(arg, country, continent, continents_data)
				if processed_string ~= '' and not categories[processed_string] then
					table.insert(categories, processed_string)
					categories[processed_string] = true
				end
			end
		end
	end
	return categories
end

-- Обработка страны с поиском привязанных континентов
local function process_country_and_continent(args, title, use_continents)
	local country = findCountry.findcountryinstring(title, 'именительный')
	if not country or country == "" then
		return "", {}, error_string("Ошибка: страна не найдена. ") .. error_category, 1
	end
	local continents_found = {}
	local continents_data = nil
	if use_continents then
		continents_data = mw.loadJsonData('Модуль:CountryMetaCat/country-continents.json')
		if continents_data and continents_data.continents then
			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_found, continent.name)
							break
						end
					end
				end
			end
		end
	end
	local error_message = nil
	if use_continents and #continents_found == 0 then
		error_message = error_string("Ошибка: часть света не найдена для страны " .. country .. ". ") .. error_category
		return country, continents_found, error_message, 2, continents_data
	end
	return country, continents_found, nil, nil, continents_data
end

-- Функция для создания категорий с обработкой континентов
local function cats(args, country, continent_list, continents_data)
	local ret = ''
	local added_categories = {}  -- Отслеживание добавленных категорий без ключей сортировки
	local skip_first_tilde = true  -- Пропуск первой строки с ~
	for i, arg in sparseIpairs(args) do
		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+", "")
			-- Обработка для 1 континента
			local function process_for_continent(continent)
				local result = ''
				if first_char == '?' then
					-- Проверка категории с ?
					local check_category = process_placeholders(rest_string, country, continent, continents_data)
					local category_name = mw.ustring.match(check_category, "^(.-)!") or check_category  -- Извлекаем название без ключа сортировки
					if category_exists(check_category) then
						-- Публикация категории, если она существует
						if not added_categories[category_name] then
							result = string.format('[[Категория:%s]]', mw.ustring.gsub(check_category, "!", "|"))
							added_categories[category_name] = true  -- Добавление категории без ключа сортировки
						end
					else
						-- Проверка следующей строки с символом ~
						local next_arg = args[i + 1]  -- Следующая строка
						if next_arg and mw.ustring.sub(next_arg, 1, 1) == '~' then
							-- Если следующая строка начинается с ~, публикуем её как замену
							local then_category = process_placeholders(mw.ustring.sub(next_arg, 2):gsub("^%s+", ""), country, continent, continents_data)
							local then_category_name = mw.ustring.match(then_category, "^(.-)!") or then_category  -- Извлекаем название без ключа сортировки
							
							if not added_categories[then_category_name] then
								result = string.format('[[Категория:%s]]', mw.ustring.gsub(then_category, "!", "|"))
								added_categories[then_category_name] = true  -- Отслеживаем категорию без ключа сортировки
							end
						end
					end
				elseif first_char == '~' then
					-- Пропускаем первую строку с ~, если до неё не было ?
					if skip_first_tilde then
						skip_first_tilde = false
					else
					-- Иначе игнорируем ~, если нет связанной строки с ?
					end
				else
					-- Обычная категория, если нет символов "?" или "~"
					local category = process_placeholders(arg, country, continent, continents_data)
					local category_name = mw.ustring.match(category, "^(.-)!") or category  -- Извлекаем название без ключа сортировки
					
					if not added_categories[category_name] then
						result = string.format('[[Категория:%s]]', mw.ustring.gsub(category, "!", "|"))
						added_categories[category_name] = true  -- Отслеживаем категорию без ключа сортировки
					end
				end
				return result
			end
			-- Если континентов нет или он один
			ret = ret .. process_for_continent(continent_list[1])
			-- Если континентов больше одного
			if #continent_list > 1 then
				ret = ret .. process_for_continent(continent_list[2])
			end
		end
	end
	return ret
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 use_continents = has_continent_placeholders(args)
	
	-- Обработка страны и континента
	local country, continent_list, error_message, error_code, continents_data = process_country_and_continent(args, title, use_continents)
	
	-- Возврат ошибки, если страна не найдена
	if error_code == 1 then
		return error_message
	end
	local ret = ''

	-- Публикация категорий
	ret = ret .. cats(args, country, continent_list, continents_data)

	-- Добавление ошибки, если континент не найден
	if error_code == 2 then
		ret = ret .. error_message
	end
	
	-- Добавление индекса категории, если параметр noindex не установлен
	if args.noindex ~= "1" then
		ret = mw.getCurrentFrame():preprocess('{{индекс категории (автоматический)}}\n') .. ret
	end

	return ret
end

function p.resolve_country(frame)
	local args = getArgs(frame)
	return p._resolve_country(args)
end

-- Функция значений экспорта стран и континентов
function p._resolve_country(args)
	local title = args.title or mw.title.getCurrentTitle().text

	-- Определение необходимости использования континентов
	local use_continents = has_continent_placeholders(args)

	-- Обработка страны и континента
	local country, continent_list, error_message, error_code, continents_data = process_country_and_continent(args, title, use_continents)
	local result = {result = '', extra_result = nil, error = error_code or 0}
	local added_categories = {}

	-- Обрабатываем основной результат
	result.result = process_placeholders(args[1] or '', country, continent_list[1], continents_data)

	-- Проверяем, нужно ли возвращать extra_result
	if #continent_list > 1 then
		result.extra_result = process_placeholders(args[1], country, continent_list[2], continents_data)
	end

	-- Добавление категорий, если они уникальны
	if result.result ~= '' and not added_categories[result.result] then
		added_categories[result.result] = true
	end

	if result.extra_result and result.extra_result ~= '' and not added_categories[result.extra_result] then
		added_categories[result.extra_result] = true
	end
	return result
end

return {
	main = p.main,
	resolve_country = p.resolve_country
}