Module:Lang/documentor tool

From blackwiki
Jump to navigation Jump to search

Module:Lang/documentor tool implements the following templates:

The module is also used to produce the following testcases:


require('Module:No globals');
local p = {};

--[[-------------------------< L A N G - X X _ S E T T I N G S >-------------------------

{{#invoke:Lang/documentor tool|lang_xx_settings|template={{ROOTPAGENAME}}}}

Reads the content of the template and extracts the parameters from {{#invoke:Lang|...}} for display on the template's
documentation page.

]]

local function lang_xx_settings (frame)
	local page = mw.title.makeTitle ('Template', frame.args['template'] or frame.args[1]);	-- get a page object for this page in 'Template:' namespace
	if not page then
		return '';																-- TODO: error message?
	end
	
	local content = page:getContent();											-- get unparsed content
	if not page then
		return '';																-- TODO: error message?
	end

	local out = {};
	
	local params;
	local style;

	if content:match ('{{%s*#invoke:%s*[Ll]ang%s*|[^|]+|[^}]+}}') or content:match ('{{%s*#invoke:%s*[Ll]ang/sandbox%s*|[^|]+|[^}]+}}') then			-- if this template uses [[Module:Lang]]
		params = content:match ('{{%s*#invoke:%s*[Ll]ang%s*|[^|]+(|[^}]+)}}') or content:match ('{{%s*#invoke:%s*[Ll]ang/sandbox%s*|[^|]+(|[^}]+)}}')	-- extract the #invoke:'s parameters
		if not params then 
			return '';															-- there should be at least one or the template/module won't work TODO: error message?
		end
		table.insert (out, '{| class="wikitable" style="text-align:right; float:right"\n|+settings')	-- start a wikitable
		for k, v in params:gmatch ('%s*|%s*([^%s=]+)%s*=%s*([^%s|]+)') do		-- get the parameter names (k) and values (v)
			if 'label' == k then												-- special case for labels because spaces and pipes
				v = params:match ('label%s*=%s*(%[%[[^%]]+%]%])') or params:match ('label%s*=%s*([^|\n]+)') or 'missing label';
			end
			table.insert (out, table.concat ({k, '\n|', v}));					-- make rudimentary wikitable entries
		end

		style = content:match ('lang_xx_([^|]+)');
		return table.concat ({table.concat (out,'\n|-\n! scope="row" | '), '\n|-\n|colspan="2"|style: ', style, '\n|-\n|}'});	-- add inter-row markup and close the wikitable and done
	else
		return '';																-- does not use [[Module:Lang]] so abandon quietly
	end
end


--[[-------------------------< U S E S _ M O D U L E >-------------------------

{{#invoke:Lang/documentor tool|uses_module|template={{ROOTPAGENAME}}}}

Reads the content of the template to determine if this {{lang-xx}} template uses Module:Lang.
Returns the index of the substring '{{#invoke|lang|' in the template page content if true; empty string if false.

Used in template documentation {{#if:}} parser functions.

]]

local function uses_module (frame)
	local page = mw.title.makeTitle ('Template', frame.args['template'] or frame.args[1]);	-- get a page object for this page in 'Template:' namespace
	if not page then
		return '';																-- TODO: error message?
	end
	
	local content = page:getContent();											-- get unparsed content
	if not page then
		return '';																-- TODO: error message?
	end
	
	return content:find ('{{%s*#invoke:[Ll]ang%s*|') or '';						-- return index or empty string
end

--[[-------------------------< S H A R E D _ C O D E >-------------------------

- Tables:
-- language_categories
-- error_messages

- Functions:
-- get_error_message(message)
-- get_see_also_section(page_title, language_name, language_code)
-- get_top_section(frame)
-- get_bottom_section(frame, language_name, see_also_section, parent_category)

]]

local language_categories = {
	["SOURCES"] = "Articles with %s-language sources (%s)",
	["CS1"] = "CS1 %s-language sources (%s)",
	["LANGUAGE_TEXT"] = "Articles containing %s-language text",
	["LANGUAGES_COLLECTIVE_TEXT"] = "Articles with text from the %s languages collective",
	["LANGUAGES_COLLECTIVE_NEW_TEXT"] = "Articles with text from %s languages",
	["ENGLISH"] = "Articles containing explicitly cited %s-language text",
}

local error_messages = {
	["NOT_VALID_LANGUAGE_CODE"] = "[[%s]] is not a valid ISO 639 or IETF language name.",
	["NO_CATEGORY_TITLE_FOUND"] = "No language category found for %s.",
	["INCORRECT_CATEGORY_TITLE"] = "[[:%s]] is not the category being populated by the {{%s}} template. The correct category is located at: [[:%s]].",
}

local function get_error_message(message)
	return string.format('<span style="font-size:100%%; font-style:normal;" class="error">Error: %s </span>[[Category:Lang and lang-xx template errors]]', message)
end

local function get_language_link(language_name, language_code, is_collective_language)
	local lang_module = require('Module:Lang')
	-- Is a language collective?
	if is_collective_language or language_name:find('languages') then
		return lang_module.name_from_tag({language_code, link = "yes"})
	else
		return lang_module.name_from_tag({language_code, link = "yes", label = lang_module.name_from_tag({language_code}) .. "-language"})
	end
end

-- Generates a consistent style See also section for {{Category articles containing non-English-language text}} and {{Non-English-language source category}}.
-- If {{CS1 language sources}} is converted, it should also use it.
-- TODO: Currently getting the redirect cateogry Category:Articles with text from Berber languages. Need to prevent soft redirects.
local function get_see_also_section(page_title, language_name, language_code)
	local see_also_section = {}

	for _, value in pairs(language_categories) do
    	local category = mw.title.new(string.format(value, language_name, language_code), 14)
 		if category and page_title ~= category.text and category.exists then
			table.insert(see_also_section, "* [[:" .. category.prefixedText .. "]]")
		end
	end
	
	table.sort(see_also_section)
	table.insert(see_also_section, 1, '\n\n==See also==')
	
	if table.getn(see_also_section) == 1 then
		return ""
	else
		return table.concat(see_also_section, "\n")
	end
end

-- Generates a consistent top maintenance template section.
local function get_top_section(frame)
	local top_section = {}
	table.insert(top_section, frame:expandTemplate{title = 'Hidden category'})
	if mw.site.stats.pagesInCategory(mw.title.getCurrentTitle().text, "all") == 0 then
		table.insert(top_section, frame:expandTemplate{title = 'Possibly empty category'})
	else
		table.insert(top_section, frame:expandTemplate{title = 'Possibly empty category', args = {hidden=true}})
	end

	local purge_module = require('Module:Purge')
	table.insert(top_section, '<div style="font-size:x-small;">' .. purge_module._main({"Purge page cache"}) .. '</div>')
	
	return table.concat(top_section, "\n\n")
end

-- Generates a consistent non-text section.
local function get_bottom_section(frame, language_name, see_also_section, parent_category)
	local bottom_section = {}
	table.insert(bottom_section, frame:expandTemplate{title = 'CatAutoTOC'})
	table.insert(bottom_section, see_also_section)
	table.insert(bottom_section, frame:preprocess{text = "{{DEFAULTSORT:" .. language_name .. "}}"})
	if mw.title.getCurrentTitle().nsText == "Category" then
		table.insert(bottom_section, parent_category)
	end

	return table.concat(bottom_section, "\n\n\n")
end


--[[-------------------------< N O N _ E N G L I S H _ L A N G U A G E _ T E X T _ C A T E G O R Y >-------------------------

{{#invoke:Lang/documentor tool|non_english_language_text_category}}

This function implements {{Non-English-language text category}}.
]]

local non_english_language_text_text_strings = {
	["LINE1"] = "This category contains articles with %s%s text. The primary purpose of these categories is to facilitate manual or automated checking of text in other languages.",
	["LINE2"] = "This category should only be added with the %s family of templates, never explicitly.",
	["LINE3"] = 'For example %s, which wraps the text with <code>&lt;span lang="%s"&gt;</code>.%s',
	["LINE3_EXTRA"] = " Also available is %s which displays as %s.",
	["IN_SCRIPT"] = " (in %s)",
	["EXAMPLE_DEFAULT_TEXT"] = "text in %s language here",
	["PARENT_CATEGORY"] = "[[Category:Articles containing non-English-language text]]",
}

local function non_english_language_text_category(frame)
	local args = require('Module:Arguments').getArgs(frame)
	local page_title = mw.title.getCurrentTitle().text

	-- Naming style: Articles with text from the Berber languages collective
	local page_title_modified = page_title
	local split_title = "([^,]+)%%s([^,]+)"
	local part1 = ""
	local part2 = ""
	local suffix
	local is_collective_language
	if page_title_modified:find("languages collective") then		-- this form may or may not be replaced with the form in the elseif
		-- Wikipedia:Categories_for_discussion/Log/2020_August_18#Category:Articles_with_text_from_the_Afro-Asiatic_languages_collective
		-- Naming style: Category:Articles with text from the Afro-Asiatic languages collective
		part1, part2 = language_categories["LANGUAGES_COLLECTIVE_TEXT"]:match(split_title)
		suffix = " languages"
		is_collective_language = true
	elseif page_title_modified:find('Articles with text from') then
		-- Naming style: Category:Articles with text from Afro-Asiatic languages (as currently implemented in Module:lang)
		part1, part2 = language_categories["LANGUAGES_COLLECTIVE_NEW_TEXT"]:match(split_title)
		suffix = " languages"
	elseif page_title_modified:find('explicitly cited') then
		part1, part2 = language_categories["ENGLISH"]:match(split_title)
	else
		-- Naming style: Category:Articles containing French-language text
		part1, part2 = language_categories["LANGUAGE_TEXT"]:match(split_title)
	end

	page_title_modified = page_title_modified:gsub(part1, "")
	page_title_modified = page_title_modified:gsub(part2, "")
	local language_name = page_title_modified
	
	local full_language_tag_name = language_name
	if suffix then
		full_language_tag_name = language_name .. suffix
	end

	local lang_module = require('Module:Lang')
	local language_code = lang_module._tag_from_name({full_language_tag_name})

	local layout = {}
	local see_also_section = ""
	-- Check if error message
	if language_code:find('error') then
		table.insert(layout, get_error_message(string.format(error_messages["NOT_VALID_LANGUAGE_CODE"], language_name)))
	else
		local correct_language_category_title = lang_module._category_from_tag({language_code})
		if correct_language_category_title:find('error') then
			table.insert(layout, get_error_message(string.format(error_messages["NO_CATEGORY_TITLE_FOUND"], language_code)))
		else
			local current_category_title = mw.title.getCurrentTitle().prefixedText
			if current_category_title ~= correct_language_category_title then
				table.insert(layout, get_error_message(string.format(error_messages["INCORRECT_CATEGORY_TITLE"], current_category_title, "Lang", correct_language_category_title)))
			else
				table.insert(layout, get_top_section(frame))
				local script = args.script
				local script_text
				if script then
					script_text = string.format(non_english_language_text_text_strings["IN_SCRIPT"], script)
				else
					script_text = ""
				end
				local example_default_text = string.format(non_english_language_text_text_strings["EXAMPLE_DEFAULT_TEXT"], language_name)
				local example_text = args.example or example_default_text

				local language_link = get_language_link(language_name, language_code, is_collective_language)

				table.insert(layout, string.format(non_english_language_text_text_strings["LINE1"], language_link, script_text))
				local lang_template = frame:expandTemplate{title = 'Tl', args = {"Lang"}}
				table.insert(layout, string.format(non_english_language_text_text_strings["LINE2"], lang_template))

				local language_code_link = lang_module._name_from_tag({language_code, link="yes", label=language_code})
				local lang_template_example = frame:expandTemplate{title = 'Tlx', args = {"Lang", language_code_link, example_text}}
				local lang_x = mw.title.makeTitle(10, "Lang-" .. language_code)
				local line3_extra = ""
				if lang_x.exists then
					local lang_x_template_example = frame:expandTemplate{title = 'Tlx', args = {lang_x.text, example_text}}
					local lang_x_template = frame:expandTemplate{title = lang_x.text, args = {example_text}}
					line3_extra = string.format(non_english_language_text_text_strings["LINE3_EXTRA"], lang_x_template_example, lang_x_template)
				end
				table.insert(layout, string.format(non_english_language_text_text_strings["LINE3"], lang_template_example, language_code, line3_extra))
				see_also_section = get_see_also_section(page_title, language_name, language_code)
			end
		end
	end
	
	local bottom = get_bottom_section(frame, language_name, see_also_section, non_english_language_text_text_strings["PARENT_CATEGORY"])
	return table.concat(layout, "\n\n") .. bottom
end


--[[-------------------------< N O N _ E N G L I S H _ L A N G U A G E _ S O U R C E S _ C A T E G O R Y >-------------------------

{{#invoke:Lang/documentor tool|non_english_language_sources_category}}

This function implements {{Non-English-language sources category}}.

]]

local non_english_language_sources_text_strings = {
	["LINE1"] = "This is a tracking category for articles that use %s to identify %s sources.",
	["PARENT_CATEGORY"] = "[[Category:Articles with non-English-language sources]]",
}

local function non_english_language_sources_category(frame)
	local page_title_object = mw.title.getCurrentTitle()
	local page_title = page_title_object.text
	local language_code = page_title:match('%(([%a%-]+)%)')
	local language_name = require('Module:Lang')._name_from_tag({language_code})
	local layout = {}
	local see_also_section = ""
	local current_category_title = page_title_object.prefixedText
	local correct_language_category_title = require('Module:In lang')._in_lang({language_code, ["list-cats"]="yes"})
	if correct_language_category_title == "" then
		table.insert(layout, get_error_message(string.format(error_messages["NO_CATEGORY_TITLE_FOUND"], language_code)))
	elseif correct_language_category_title ~= current_category_title then
		table.insert(layout, get_error_message(string.format(error_messages["INCORRECT_CATEGORY_TITLE"], current_category_title, "In lang", correct_language_category_title)))
	else
		local language_link = get_language_link(language_name, language_code, nil)
		local text = string.format(non_english_language_sources_text_strings["LINE1"], frame:expandTemplate{title = 'Tlx', args = {"In lang", language_code}}, language_link)
	
		table.insert(layout, get_top_section(frame))	
		table.insert(layout, text)
		see_also_section = get_see_also_section(page_title, language_name, language_code)
	end

	local bottom = get_bottom_section(frame, language_name, see_also_section, non_english_language_sources_text_strings["PARENT_CATEGORY"])
	return table.concat(layout, "\n\n") .. bottom
end


--[[-------------------------< N O N _ E N G L I S H _ L A N G U A G E _ C S 1 _ S O U R C E S _ C A T E G O R Y >-------------------------

{{#invoke:Lang/documentor tool|non_english_language_cs1_sources_category}}

This function implements {{Non-English-language CS1 sources category}}.
]]

local non_english_language_cs1_text_strings = {
	["LINE1"] = "This is a tracking category for [[WP:CS1|CS1 citations]] that use the parameter %s to identify a source in [[%s language|%s]]. Pages in this category should only be added by CS1 templates and [[Module:Citation/CS1]].",
	["PARENT_CATEGORY"] = "[[Category:CS1 foreign language sources]]", -- #TODO change to "Articles with non-english CS1 language sources" or "CS1 non-English language sources"
}

	--"This is a tracking category for [[WP:CS1|CS1 citations]] that use the parameter %s to hold a citation title that uses %s characters and contains the language prefix <code>%s:</code>. Pages in this category should only be added by CS1 templates and [[Module:Citation/CS1]].",
	--"[[Category:CS1 uses foreign language script]]",
	
	-- "This is a tracking category for [[WP:CS1|CS1 citations]] that use the parameter %s. Pages in this category should only be added by CS1 templates and [[Module:Citation/CS1]].",
	-- "to identify a source in [[%s language|%s]].",
	-- "to hold a citation title that uses %s characters and contains the language prefix <code>%s:</code>.",

local function non_english_language_cs1_sources_category(frame)
	local page_title_object = mw.title.getCurrentTitle()
	local page_title = page_title_object.text
	local language_code = page_title:match('%(([%a%-]+)%)')
	local language_name = require('Module:Lang')._name_from_tag({language_code})
	local layout = {}
	local see_also_section = ""
	local parameter_doc = frame:expandTemplate{title = 'para', args = {"language", language_code}}
	table.insert(layout, get_top_section(frame))
	table.insert(layout, string.format(non_english_language_cs1_text_strings["LINE1"], parameter_doc, language_name, language_name))
	local see_also_section = get_see_also_section(page_title, language_name, language_code)
	local bottom = get_bottom_section(frame, language_name, see_also_section, non_english_language_cs1_text_strings["PARENT_CATEGORY"])
	return table.concat(layout, "\n\n") .. bottom
end


--[[-------------------------< S H A R E D _ T E S T C A S E S _ C O D E >-------------------------

Functions:
- get_unordered_list(language_list, list_modification, range)
- get_unordered_language_list_general(language_list, list_modification, range)
- get_data_from_language_code(language_list, language_code, unordered_table, reverse_table)
]]

local function get_unordered_list(language_list, list_modification, range)
	local unordered_table = {}
	if list_modification == "range" then
		for language_code in pairs(language_list) do
			if language_code:find(range) then
				table.insert(unordered_table, language_code)
			end
		end
	elseif list_modification == "length" then
		for language_code in pairs(language_list) do		
			if language_code:len() == 2 then
				table.insert(unordered_table, language_code)
			end
		end
	else
		for language_code in pairs(language_list) do
			table.insert(unordered_table, language_code)
		end
	end
	return unordered_table
end

local function get_data_from_language_code(language_list, language_code, unordered_table, reverse_table)
	for _, language_name in pairs(language_list[language_code]) do
		table.insert(unordered_table, language_name)
		reverse_table[language_name] = language_code
	end
end

local function get_unordered_and_reverse_lists(language_list, list_modification, range)
	local unordered_table = {}
	local reverse_table = {}
	local override_table = require("Module:Lang/data/sandbox").override			-- TODO: switch to live
	if list_modification == "range" then
--		for language_code in pairs(language_list) do
		for language_code, names in pairs(language_list) do
			if language_code:find(range) then
				if override_table[language_code] then
					local name = override_table[language_code][1]
					table.insert(unordered_table, name)							-- only the first name when there are multiples
					reverse_table[name] = language_code
				else
					table.insert(unordered_table, names[1])						-- only the first name when there are multiples
					reverse_table[names[1]] = language_code
				end
--				get_data_from_language_code(language_list, language_code, unordered_table, reverse_table)
			end
		end
	elseif list_modification == "length" then
--		for language_code in pairs(language_list) do		
		for language_code, names in pairs(language_list) do
			if 2 == language_code:len() then
				if override_table[language_code] then
					local name = override_table[language_code][1]
					table.insert(unordered_table, name)							-- only the first name when there are multiples
					reverse_table[name] = language_code
				else
					table.insert(unordered_table, names[1])						-- only the first name when there are multiples
					reverse_table[names[1]] = language_code
				end
--				get_data_from_language_code(language_list, language_code, unordered_table, reverse_table)
			end
		end
	else
		for language_code in pairs(language_list) do
			get_data_from_language_code(language_list, language_code, unordered_table, reverse_table)
		end
	end
	return unordered_table, reverse_table
end


--[[-------------------------< T E S T C A S E S _ C A T E G O R Y _ F R O M _ T A G >-------------------------

Entry point for the various category_from_tag testcases.

Build a table of test patterns where each entry in the table is a table with two members:
	{"<language_code>", "<category name according to Module:Lang>"}

]]

local function comp_by_keys(a, b)												-- local function used by table.sort()
	return a[1] < b[1]															-- ascending sort by code
end
	
local function testcases_category_from_tag(self, args)
	local cat_from_tag = require("Module:Lang")._category_from_tag				-- use Module:Lang to create the 'expected results'
	local test_patterns = {}													-- collect test patterns here
	
	local filter_function
	if args.code_mask then
		filter_function = function(language_code, code_mask) return language_code:find(code_mask) end	-- if code within limits (three-character codes)
	else
		filter_function = function(language_code, code_mask) return language_code:len() == 2 end		-- no limits for two-character codes
	end

	for language_code in pairs(args.language_list) do							-- list of names not needed here
		local pattern = {}														-- here we assemble the test pattern for <code>														-
		if filter_function(language_code, args.code_mask) then								
			table.insert(pattern, language_code)								-- add it to the pattern
			table.insert(pattern, cat_from_tag({language_code}))				-- call Module:Lang and add the 'expected results' for code to pattern
			table.insert(test_patterns, pattern)								-- accumulate in list of patterns
		end
	end

	table.sort(test_patterns, comp_by_keys)										-- make the list pretty

    self:preprocess_equals_preprocess_many(
    	'{{#invoke:Lang/sandbox|category_from_tag|', '}}', '', '',
		test_patterns, 
		{nowiki=false, templatestyles=true}
	)
end


--[[-------------------------< T E S T C A S E S _ N A M E _ F R O M _ T A G >-------------------------

Entry point for the various name_from_tag testcases.

]]

local function testcases_name_from_tag(self, args)
	local undabbed_language_list = {}											-- for this test, ISO 639-3 language name disambiguators must be removed; un-dabbed names go here
	for language_code, language_names in pairs(args.language_list) do
		if args.code_mask then
			if language_code:find(args.code_mask) then
				undabbed_language_list[language_code] = language_names[1]:gsub(' %b()$', '')	-- undab and save only the first name; ignore all other names assigned to a code
			end
		else
			if language_code:len() == 2 then
				undabbed_language_list[language_code] = language_names[1]:gsub(' %b()$', '')	-- undab and save only the first name; ignore all other names assigned to a code
			end
		end
	end

	local unordered_table = get_unordered_list(undabbed_language_list, args.list_modification, args.range)
	local ordered_table = {}
	local override_table = require("Module:Lang/data").override
	table.sort(unordered_table)
	for _, key in ipairs(unordered_table) do
		local override_lang_table = override_table[key]
		if override_lang_table then
			table.insert(ordered_table, {key, override_lang_table[1]})
		else
			table.insert(ordered_table, {key, undabbed_language_list[key]})
		end
	end

    self:preprocess_equals_preprocess_many(
    	'{{#invoke:Lang/sandbox|name_from_tag|', '}}', '', '',
		ordered_table, 
		{nowiki=false, templatestyles=true}
	)
end


--[[-------------------------< T E S T C A S E S _ T A G _ F R O M _ N A M E >-------------------------

Entry point for the various tag_from_name testcases.

]]

local function testcases_tag_from_name(self, args)
	local unordered_table, reverse_table = get_unordered_and_reverse_lists(args.language_list, args.list_modification, args.range)
	local ordered_table = {}
	table.sort(unordered_table)
	for _, key in ipairs(unordered_table) do
		table.insert(ordered_table, {key, reverse_table[key]})
	end

    self:preprocess_equals_preprocess_many(
    	'{{#invoke:Lang/sandbox|tag_from_name|', '}}', '', '',
		ordered_table, 
		{nowiki=false, templatestyles=true}
	)
end


--[[-------------------------< T E S T C A S E S _ I S O _ C O D E _ T O _ N A M E >-------------------------

Entry point for the various iso_code_to_name testcases.

]]

local function testcases_iso_code_to_name(self, args)
	local unordered_table = get_unordered_list(args.language_list, args.list_modification, args.range)
	local ordered_table = {}
	table.sort(unordered_table)
	for _, key in ipairs(unordered_table) do
		table.insert(ordered_table, {key})
	end

    self:preprocess_equals_preprocess_many(
    	'{{#invoke:ISO 639 name/sandbox|iso_639_code_to_name|link=yes|', '}}', '{{#invoke:ISO 639 name|iso_639_code_to_name|link=yes|', '}}',
		ordered_table, 
		{nowiki=false, templatestyles=true}
	)
end


--[[-------------------------< T E S T C A S E S _ I S O _ N A M E _ T O _ C O D E >-------------------------

Entry point for the various iso_name_to_code testcases.

]]

-- TODO: "Lua error: not enough memory."
local function testcases_iso_name_to_code(self, args)
	local unordered_table, reverse_table = get_unordered_and_reverse_lists(args.language_list, args.list_modification, args.range)
	local ordered_table = {}
	table.sort(unordered_table)
	for _, key in ipairs(unordered_table) do
		table.insert(ordered_table, {key})
	end

    self:preprocess_equals_preprocess_many(
    	'{{#invoke:ISO 639 name/sandbox|iso_639_name_to_code|', '}}', '{{#invoke:ISO 639 name|iso_639_name_to_code|', '}}',
		ordered_table, 
		{nowiki=false, templatestyles=true}
	)
end


--[[-------------------------< E X P O R T E D   F U N C T I O N S >-------------------------
]]

return {
	lang_xx_settings = lang_xx_settings,
	uses_module = uses_module,
	non_english_language_text_category = non_english_language_text_category,
	non_english_language_sources_category = non_english_language_sources_category,
	non_english_language_cs1_sources_category = non_english_language_cs1_sources_category,
	testcases_category_from_tag = testcases_category_from_tag,
	testcases_name_from_tag = testcases_name_from_tag,
	testcases_tag_from_name = testcases_tag_from_name,
	testcases_iso_code_to_name = testcases_iso_code_to_name,
	testcases_iso_name_to_code = testcases_iso_name_to_code,
	}