এই মডিউলের জন্য মডিউল:cau-nec-translit/নথি-এ নথিপত্র তৈরি করা হয়ে থাকতে পারে

local m_str_utils = require("Module:string utilities")

local gsub = m_str_utils.gsub
local lower = m_str_utils.lower
local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD
local u = m_str_utils.char
local upper = m_str_utils.upper

local CyrlConsonant = "бвгджзклмнпрстфхцчшщБВГДЖЗКЛМНПРСТФХЦЧШЩ"
local CyrlVowel = "аеёиоуыэюяАЕЁИОУЫЭЮЯ"
local ACUTE, CIRC, TILDE, MACRON, BREVE, DOTABOVE, DIAER, CARON, DOTBELOW = u(0x301), u(0x302), u(0x303), u(0x304), u(0x306), u(0x307), u(0x308), u(0x30C), u(0x323)
local accent = "[" .. ACUTE .. CIRC .. TILDE .. MACRON .. BREVE .. DOTABOVE .. DIAER .. CARON .. DOTBELOW .. "]"
local br = u(0xF000)

local export = {}

-- Structured like this to reduce size of loaded table.
local function getSubs(lang)
	--Aghul
	if lang == "agx" then
		return {
			{
				["гъ"] = "ğ", ["гь"] = "h", ["гӏ"] = "ʻʳ", ["къ"] = "qq", ["кь"] = "qʼ", ["кӏ"] = "kʼ", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["хъ"] = "q", ["хь"] = "x̂", ["хӏ"] = "ḥʳ", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "v", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ʔ", ["ы"] = "ə", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja"
			}
		}
	-- Akhvakh
	elseif lang == "akv" then
		return {
			{
				["гъӏ"] = "ğʰ", ["къӏ"] = "qˣʼ", ["кьӏ"] = "kˡʼ", ["лӏъ"] = "ᵏl", ["хъӏ"] = "qˣ"
			},
			{
				["гъ"] = "ɣ", ["гь"] = "h", ["гӏ"] = "ʻʳ", ["къ"] = "qxʼ", ["кь"] = "kkˡʼ", ["кӏ"] = "kʼ", ["лъ"] = "lˢ", ["ль"] = "ĺ", ["лӏ"] = "ᵏll", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["хъ"] = "qx", ["хь"] = "x̂", ["хӏ"] = "ḥʳ", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "v", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ʔ", ["ы"] = "ə", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja"
			}
		}
	-- Andi
	elseif lang == "ani" then
		return {
			{
				["къкъ"] = "qxʼ", ["хъхъ"] = "qx"
			},
			{
				["гъӏ"] = "ğʼ", ["жъӏ"] = "žʼ", ["къӏ"] = "qxʼ", ["лъӏ"] = "llˢʼ", ["хъӏ"] = "qx", ["цъӏ"] = "ccʼ", ["чъӏ"] = "cčʼ"
			},
			{
				["гъ"] = "ğ", ["гь"] = "h", ["гӏ"] = "gʼ", ["къ"] = "qˣʼ", ["кь"] = "kkˡʼ", ["кӏ"] = "kʼ", ["лъ"] = "lˢ", ["ль"] = "lˢʼ", ["лӏ"] = "ᵏll", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["хъ"] = "qˣ", ["хь"] = "x̂", ["хӏ"] = "xʼ", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "v", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ˀ", ["ы"] = "ə", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja"
			}
		}
	-- Archi
	elseif lang == "aqc" then
		return {
			{
				["ккъӏ"] = "qq̣ʼ"
			},
			{
				["гъӏ"] = "ğ̣", ["ккъ"] = "qqʼ", ["къӏ"] = "q̣ʼ", ["хъӏ"] = "q̣", ["хьӏ"] = "x̣"
			},
			{
				["гъ"] = "ğ", ["гь"] = "h", ["гӏ"] = "ˀ", ["къ"] = "qʼ", ["кь"] = "kˡʼ", ["кӏ"] = "kʼ", ["лъ"] = "lʰ", ["ль"] = "lˠ", ["лӏ"] = "ᵏl", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["хъ"] = "q", ["хӏ"] = "ḥʳ", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "w", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ʔ", ["ы"] = "ə", ["ь"] = "", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja"
			}
		}
	-- Avar
	elseif lang == "av" then
		return {
			{
				["гъ"] = "ğ", ["гь"] = "h", ["гӏ"] = "ʻ", ["къ"] = "qxʼ", ["кь"] = "kkˡʼ", ["кӏ"] = "kʼ", ["лъ"] = "lˢ", ["лӏ"] = "ᵏll", ["тӏ"] = "tʼ", ["хъ"] = "qx", ["хь"] = "x̂", ["хӏ"] = "ḥʳ", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "w", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ʔ", ["ы"] = "ə", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja"
			}
		}
	-- Bagvalal
	elseif lang == "kva" then
		return {
			{
				["гъ"] = "ğ", ["гь"] = "h", ["гӏ"] = "ˀ", ["къ"] = "qʼ", ["кь"] = "kkˡʼ", ["кӏ"] = "kʼ", ["лъ"] = "lˢ", ["лӏ"] = "ᵏll", ["сӏ"] = "sʼ", ["тӏ"] = "tʼ", ["хъ"] = "qx", ["хь"] = "x̂", ["хӏ"] = "ḥ", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ", ["шӏ"] = "šʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "v", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ʔ", ["ы"] = "ə", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja"
			}
		}
	-- Bezhta
	elseif lang == "kap" then
		return {
			{
				["гъ"] = "ğ", ["гь"] = "h", ["гӏ"] = "ʻ", ["къ"] = "qxʼ", ["кь"] = "kˡʼ", ["кӏ"] = "kʼ", ["лъ"] = "lˢ", ["лӏ"] = "ᵏll", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["хъ"] = "qx", ["хь"] = "x̂", ["хӏ"] = "ḥ", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "v", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ʔ", ["ы"] = "ə", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja"
			}
		}
	-- Botlikh
	elseif lang == "bph" then
		return {
			{
				["гъ"] = "ğ", ["гь"] = "h", ["къ"] = "qˣʼ", ["кь"] = "kkˡʼ", ["кӏ"] = "kʼ", ["лъ"] = "lˢ", ["лӏ"] = "ᵏll", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["хъ"] = "qˣ", ["хь"] = "x̂", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "w", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ˀ", ["ы"] = "ə", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja"
			}
		}
	-- Budukh
	elseif lang == "bdk" then
		return {
			{
				["къг"] = "gʰ"
			},
			{
				["гъ"] = "ğ", ["гь"] = "h", ["гӏ"] = "ʻ", ["къ"] = "qq", ["кь"] = "qʼ", ["кӏ"] = "kʼ", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["хъ"] = "q", ["хь"] = "x̂", ["хӏ"] = "ḥ", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "v", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ʔ", ["ы"] = "ı", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja", ["ӏ"] = "ˀ"
			}
		}
	-- Chamalal
	elseif lang == "cji" then
		return {
			{
				["кӏкӏ"] = "kxʰʼ"
			},
			{
				["гъ"] = "ğ", ["гь"] = "h", ["гӏ"] = "ʻ", ["къ"] = "qxʼ", ["кь"] = "kkˡʼ", ["кӏ"] = "kʼ", ["лъ"] = "lˢ", ["лӏ"] = "ᵏll", ["пӏ"] = "pʼ", ["сӏ"] = "sʼ", ["тӏ"] = "tʼ", ["хъ"] = "qx", ["хь"] = "x̂", ["хӏ"] = "ḥ", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "v", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šš", ["ъ"] = "ʔ", ["ы"] = "ə", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja"
			}
		}
	-- Chechen and Ingush
	elseif lang == "ce" or lang == "inh" then
		return {
			{
				["ккх"] = "qq", ["рхӏ"] = "rh"
			},
			{
				["гӏ"] = "ğ", ["кх"] = "q", ["къ"] = "qʼ", ["кӏ"] = "kʼ", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["хь"] = "ḥʳ", ["хӏ"] = "h", ["цӏ"] = "cʼ", ["чӏ"] = "čʼ"
			},
			{
				["а"] = "a", ["б"] = "b", ["в"] = "v", ["г"] = "g", ["д"] = "d", ["е"] = "e", ["ё"] = "jo", ["ж"] = "ž", ["з"] = "z", ["и"] = "ı̇", ["й"] = "j", ["к"] = "k", ["л"] = "l", ["м"] = "m", ["н"] = "n", ["о"] = "o", ["п"] = "p", ["р"] = "r", ["с"] = "s", ["т"] = "t", ["у"] = "u", ["ф"] = "f", ["х"] = "x", ["ц"] = "c", ["ч"] = "č", ["ш"] = "š", ["щ"] = "šč", ["ъ"] = "ʔ", ["ы"] = "y", ["ь"] = "ʲ", ["э"] = "e", ["ю"] = "ju", ["я"] = "ja", ["ӏ"] = "ˀ"
			}
		}
	end
end

local function double_with_j(vowel, acute, nasal)
	local ret = vowel .. (nasal ~= "" and TILDE or nasal)
	return ret .. acute .. lower(ret)
end

function export.tr(text, lang, sc)
	local subs = getSubs(lang)
	
	if not subs then
		return nil
	end
	
	-- Convert uppercase palochka to lowercase, along with any "false" palochkas (entered as Latin "I" or "l", or Cyrillic "І"). Lowercase palochka is found in tables above.
	text = gsub(text, "[IlІӀ]", "ӏ")
	-- Convert dialectal nasal ᵸ written as Latin ᴴ.
	text = gsub(text, "ᴴ", "ᵸ")
	
	-- Decompose precomposed characters, except for ё and й.
	text = gsub(text, "[^ёЁйЙ]", toNFD)
	
	-- Substitute double consonants for macrons over consonants. Add a temporary breaking character after, to prevent the creation of false multigraphs with following characters.
	local function macronToDouble(a, b) return a .. b .. lower(a) .. b .. br end
	text = gsub(text, "([" .. CyrlConsonant .. "])" .. MACRON .. "([ъь])" .. MACRON, macronToDouble)
	text = gsub(text, "([" .. CyrlConsonant .. "ъьЪЬ])" .. MACRON .. "(ӏ)" .. MACRON, macronToDouble)
	text = gsub(text, "([" .. CyrlConsonant .. "])" .. MACRON, macronToDouble)
	
	-- Remove any double hard/soft signs or palochkas this creates.
	text = gsub(text, "([ъьӏЪЬӀ])" .. "([ъьӏ])", function(a, b) if b == lower(a) then return a else return a .. b end end)
	
	-- Contextual substitution of "j" before "е", "w" for "у" and ʷ for "в".
	if lang == "aqc" then
		text = gsub(text, "([" .. CyrlConsonant .. "ъьЪЬ]" .. br .. "?[ӏӀ]?" .. br .. "?)в", "%1ʷ")
	else
		text = gsub(gsub(text, "^е", "jе"), "^Е", "Jе")
		text = gsub(text, "([" .. CyrlVowel .. "%s%p]" .. MACRON .. "?ь?ӏ?ᵸ?)е", "%1jе")
		text = gsub(text, "([%s%p])Е", "%1Jе")
		text = gsub(text, "у([аиоуыэ])", "w%1")
		text = gsub(text, "У([аиоуыэ])", "W%1")
		text = gsub(text, "([" .. CyrlVowel .. "]" .. MACRON .. "?ь?ӏ?ᵸ?)у", "%1w")
		text = gsub(text, "([" .. CyrlConsonant .. "ъьЪЬ]" .. br .. "?)в", "%1ʷ")
	end
	
	-- Add "j" before iotated vowels, and substitute non-iotated equivalents.
	text = gsub(gsub(text, "ё", "jо"), "Ё", "Jо")
	text = gsub(gsub(text, "ю", "jу"), "Ю", "Jу")
	text = gsub(gsub(text, "я", "jа"), "Я", "Jа")
	
	-- Process vowel modifiers.
	text = gsub(text, "([" .. CyrlVowel .. "])(" .. MACRON .. "?)(" .. ACUTE .. "?)(ь?)(" .. MACRON .. "?)(ӏ?)(ᵸ?)", function(vowel, macron1, acute, soft, macron2, palochka, nasal)
		local ret = vowel ..
			(soft ~= "" and DIAER or soft) ..
			(palochka ~= "" and lang == "aqc" and DOTBELOW or "") ..
			(nasal ~= "" and TILDE or nasal)
		if macron1 ~= "" then
			ret = ret .. acute .. lower(ret)
		else
			ret = ret .. acute
		end
		return ret .. (lang ~= "aqc" and palochka or "")
	end)
	
	if lang == "ce" or lang == "inh" then
		text = gsub(text, "([иИ])(" .. ACUTE .. "?" .. ")й(ᵸ?)", double_with_j)
		text = gsub(text, "([уУ]" .. DIAER .. ")(" .. ACUTE .. "?" .. ")й(ᵸ?)", double_with_j)
	end
	
	-- Apply language-specific substitutions by iterating over each subtable. For each one, create a temporary table that stores each substitution in lowercase and uppercase variants. Then, iterate over all substitutions.
	for _,i in ipairs(subs) do
		local t = {}
		-- Create a temporary table, then iterate over all of them.
		for k, v in pairs(i) do
			t[k] = v
			if v == "ʔ" then
				t[gsub(k, "^.", upper)] = gsub(v, "^.", "Ɂ")
			else
				t[gsub(k, "^.", upper)] = gsub(v, "^.", upper)
			end
		end
		for letter, replacement in pairs(t) do
			text = text:gsub(letter, replacement)
		end
	end
	
	-- Reposition apostrophes, remove temporary breaking characters, then decompose.
	text = toNFD(gsub(gsub(gsub(text, "ʼʲ", "ʲʼ"), "ʼʷ", "ʷʼ"), br, ""))
	
	-- When double letters both have a modifier letter and/or an apostrophe, only show on the first or second for readability purposes.
	for letter in string.gmatch("abcdefghijklmnopqrstuvwxyzəɣıʔABCDEFGHIJKLMNOPQRSTUVWXYZƏƔɁʻˀ", ".[\128-\191]*") do
		text = gsub(text, "(ᵏ?)" .. letter .. "(" .. accent .. "?" .. accent .. "?" .. accent .. "?)([ʰʲˡʳˢʷˣˠ]?[ʲʷ]?ʼ?)" .. "%1" .. lower(letter) .. "%2%3", "%1" .. letter .. "%2" .. lower(letter) .. "%2%3")
	end
	
	-- Remove consecutive j/ʲ and w/ʷ.
	text = gsub(gsub(text, "ʲ?([Jj])ʲ?", "%1"), "ʷ?([Ww])ʷ?", "%1")
	
	-- Substitute i for dotted dotless i if not followed by an acute or tilde, then recompose.
	return toNFC(gsub(gsub(text, "ı" .. "(" .. DOTBELOW .. "?)" .. DOTABOVE .. "([^" .. ACUTE .. TILDE .. "])", "i%1%2"), "ı" .. "(" .. DOTBELOW .. "?)" .. DOTABOVE .. "$", "i%1"))
end

return export