This module will transliterate text in one of the กลุ่มภาษาคอเคเซียนตะวันออกเฉียงเหนือ. It is also used to transliterate Aghul, Akhvakh, Andi, Archi, อะวาร์, Budukh, Botlikh, เชเชน, Chamalal, อิงกุช, Bezhta, and Bagvalal. The module should preferably not be called directly from templates or other modules. To use it from a template, use {{xlit}}. Within a module, use Module:languages#Language:transliterate.

For testcases, see Module:cau-nec-translit/testcases.

tr(text, lang, sc)
Transliterates a given piece of text written in the script specified by the code sc, and language specified by the code lang.
When the transliteration fails, returns nil.

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ʼ", ["тӏ"] = "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
	elseif lang == "ce" then
		return {
			{
				["ккх"] = "qq", ["рхӏ"] = "rh"
			},
			{
				["гӏ"] = "ğ", ["ий"] = "ii", ["кх"] = "q", ["къ"] = "qʼ", ["кӏ"] = "kʼ", ["ов"] = "ow", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["ув"] = "uw", ["хь"] = "ḥʳ", ["хӏ"] = "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", ["ӏ"] = "ˀ"
			}
		}
	-- Ingush
	elseif lang == "inh" then
		return {
			{
				["ккх"] = "qq", ["рхӏ"] = "rh"
			},
			{
				["гӏ"] = "ğ", ["ий"] = "ii", ["кх"] = "q", ["къ"] = "qʼ", ["кӏ"] = "kʼ", ["ов"] = "ow", ["пӏ"] = "pʼ", ["тӏ"] = "tʼ", ["ув"] = "uw", ["хь"] = "ḥʳ", ["хӏ"] = "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

function export.tr(text, lang, sc)
	local subs = getSubs(lang)
	local UTF8_char = "[%z\1-\127\194-\244][\128-\191]*"
	local CyrlConsonant = "бвгджзклмнпрстфхцчшщБВГДЖЗКЛМНПРСТФХЦЧШЩ"
	local CyrlVowel = "аеёиоуыэюяАЕЁИОУЫЭЮЯ"
	local ACUTE, CIRC, TILDE, MACRON, BREVE, DOTABOVE, DIAER, CARON, DOTBELOW = mw.ustring.char(0x301), mw.ustring.char(0x302), mw.ustring.char(0x303), mw.ustring.char(0x304), mw.ustring.char(0x306), mw.ustring.char(0x307), mw.ustring.char(0x308), mw.ustring.char(0x30C), mw.ustring.char(0x323)
	local accent = "[" .. ACUTE .. CIRC .. TILDE .. MACRON .. BREVE .. DOTABOVE .. DIAER .. CARON .. DOTBELOW .. "]"
	local br = mw.ustring.char(0xF000)
	
	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 = mw.ustring.gsub(text, "[IlІӀ]", "ӏ")
	-- Convert dialectal nasal ᵸ written as Latin ᴴ.
	text = mw.ustring.gsub(text, "ᴴ", "ᵸ")
	
	-- Decompose precomposed characters, except for ё and й.
	text = mw.ustring.gsub(text, "[^ёЁйЙ]", mw.ustring.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 .. mw.ustring.lower(a) .. b .. br end
	text = mw.ustring.gsub(text, "([" .. CyrlConsonant .. "])" .. MACRON .. "([ъь])" .. MACRON, macronToDouble)
	text = mw.ustring.gsub(text, "([" .. CyrlConsonant .. "ъьЪЬ])" .. MACRON .. "(ӏ)" .. MACRON, macronToDouble)
	text = mw.ustring.gsub(text, "([" .. CyrlConsonant .. "])" .. MACRON, macronToDouble)
	
	-- Remove any double hard/soft signs or palochkas this creates.
	text = mw.ustring.gsub(text, "([ъьӏЪЬӀ])" .. "([ъьӏ])", function(a, b) if b == mw.ustring.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 = mw.ustring.gsub(text, "([" .. CyrlConsonant .. "ъьЪЬ]" .. br .. "?[ӏӀ]?" .. br .. "?)в", "%1ʷ")
	else
		text = mw.ustring.gsub(mw.ustring.gsub(text, "^е", "jе"), "^Е", "Jе")
		text = mw.ustring.gsub(text, "([" .. CyrlVowel .. "%s%p]" .. MACRON .. "?ь?ӏ?ᵸ?)е", "%1jе")
		text = mw.ustring.gsub(text, "([%s%p])Е", "%1Jе")
		text = mw.ustring.gsub(text, "у([аиоуыэ])", "w%1")
		text = mw.ustring.gsub(text, "У([аиоуыэ])", "W%1")
		text = mw.ustring.gsub(text, "([" .. CyrlVowel .. "]" .. MACRON .. "?ь?ӏ?ᵸ?)у", "%1w")
		text = mw.ustring.gsub(text, "([" .. CyrlConsonant .. "ъьЪЬ]" .. br .. "?)в", "%1ʷ")
	end
	
	-- Add "j" before iotated vowels, and substitute non-iotated equivalents.
	text = mw.ustring.gsub(mw.ustring.gsub(text, "ё", "jо"), "Ё", "Jо")
	text = mw.ustring.gsub(mw.ustring.gsub(text, "ю", "jу"), "Ю", "Jу")
	text = mw.ustring.gsub(mw.ustring.gsub(text, "я", "jа"), "Я", "Jа")
	
	-- Process vowel modifiers.
	text = mw.ustring.gsub(text, "ᵸ", TILDE)
	text = mw.ustring.gsub(text, "([" .. CyrlVowel .. "])(" .. MACRON .. "?" .. ACUTE .. "?ь?" .. MACRON .. "?)ӏ", "%1" .. DOTBELOW .. "%2")
	text = mw.ustring.gsub(text, "([" .. CyrlVowel .. "]" .. DOTBELOW .. "?)" .. MACRON .. "(ь?)" .. MACRON .. "?(" .. TILDE .. "?)", function(a, b, c) return a .. b .. c .. mw.ustring.lower(a) .. b .. c end)
	text = mw.ustring.gsub(text, "([" .. CyrlVowel .. "]" .. DOTBELOW .. "?)" .. "ь", "%1" .. DIAER)
	
	-- 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[mw.ustring.gsub(k, "^.", mw.ustring.upper)] = mw.ustring.gsub(v, "^.", "Ɂ")
			else
				t[mw.ustring.gsub(k, "^.", mw.ustring.upper)] = mw.ustring.gsub(v, "^.", mw.ustring.upper)
			end
		end
		for letter, replacement in pairs(t) do
			text = string.gsub(text, letter, replacement)
		end
	end
	
	-- Reposition apostrophes, remove temporary breaking characters, then decompose.
	text = mw.ustring.toNFD(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.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ƏƔɁʻˀ", UTF8_char) do
		text = mw.ustring.gsub(text, "(ᵏ?)" .. letter .. "(" .. accent .. "?" .. accent .. "?" .. accent .. "?)([ʰʲˡʳˢʷˣˠ]?[ʲʷ]?ʼ?)" .. "%1" .. mw.ustring.lower(letter) .. "%2%3", "%1" .. letter .. "%2" .. mw.ustring.lower(letter) .. "%2%3")
	end
	
	-- Remove consecutive j/ʲ and w/ʷ.
	text = mw.ustring.gsub(mw.ustring.gsub(text, "ʲ?([Jj])ʲ?", "%1"), "ʷ?([Ww])ʷ?", "%1")
	
	-- Substitute i for dotted dotless i if not followed by an acute or tilde, then recompose.
	return mw.ustring.toNFC(mw.ustring.gsub(mw.ustring.gsub(text, "ı" .. "(" .. DOTBELOW .. "?)" .. DOTABOVE .. "([^" .. ACUTE .. TILDE .. "])", "i%1%2"), "ı" .. "(" .. DOTBELOW .. "?)" .. DOTABOVE .. "$", "i%1"))
end

return export