Moduł:transkryptor-PWN

Z Wikisłownika – wolnego słownika wielojęzycznego

Dokumentacja dla tego modułu może zostać utworzona pod nazwą Moduł:transkryptor-PWN/opis

local p = {}

local errcat = '[[Kategoria:Błąd w transkrypcji]]'

local commonTransliteration = {
	["-"] = "-",
	["–"] = "–",
	["—"] = "—",
	["="] = "=",
	[","] = ",",
	["."] = ".",
	["/"] = "/",
	[";"] = ";",
	["'"] = "'",
	["["] = "[",
	["]"] = "]",
	["\\"] = "\\",
	["`"] = "`",
	["~"] = "~",
	["!"] = "!",
	["@"] = "@",
	["#"] = "#",
	["$"] = "$",
	["%"] = "%",
	["^"] = "^",
	["&"] = "&",
	["*"] = "*",
	["("] = "(",
	[")"] = ")",
	["_"] = "_",
	["+"] = "+",
	["{"] = "{",
	["}"] = "}",
	["|"] = "|",
	[":"] = ":",
	["\""] = "\"",
	["<"] = "<",
	[">"] = ">",
	["?"] = "?",
	[" "] = " "
}

-- Samogłoski rosyjskie, używane przy transkrypcji typu "ja"/"ia"/"a"
local vowelsRu = { "а", "а́", "е", "е́", "ё", "и", "и́", "о", "о́", "у", "ы", "э", "ю", "я", "я́", "ъ", "ь" }
local transliterationRu = {
	["а"] = "a",
	["а́"] = "a", -- zignoruj akcent
	["б"] = "b",
	["в"] = "w",
	["г"] = "g",
	["д"] = "d",
	-- Е > JE, IE, E
	["е"] = {
		{ vowelsRu, "", "je" }, -- "je" po samogłoskach
		{ commonTransliteration, "", "je" }, -- "je" na początku wyrazu i po interpunkcji
		{ { "ж", "л", "ц", "ч", "ш", "щ" }, "", "e" },
		"ie"
	},
    ["е́"] = {
		{ vowelsRu, "", "je" }, -- "je" po samogłoskach // zignoruj akcent
		{ commonTransliteration, "", "je" }, -- "je" na początku wyrazu i po interpunkcji
		{ { "ж", "л", "ц", "ч", "ш", "щ" }, "", "e" },
		"ie"
	},
	-- Ё > O
	["ё"] = {
		{ vowelsRu, "", "jo" }, -- "jo" po samogłoskach
		{ commonTransliteration, "", "jo" }, -- "jo" na początku wyrazu i po interpunkcji
		{ { "ж", "л", "ч", "ш", "щ" }, "", "o" },
		"io"
	},
	["ж"] = "ż",
	["з"] = "z",
	-- И > Y, JI, I
	["и"] = {
		{ { "ж", "ц", "ш" }, "", "y" },
		{ "ь", "", "ji" },
		"i"
	},
    ["и́"] = {
		{ { "ж", "ц", "ш" }, "", "y" }, -- zignoruj akcent
		{ "ь", "", "ji" },
		"i"
	},
	["й"] = "j",
	["к"] = "k",
	-- Л > L, Ł
	["л"] = {
		{ "", { "е", "ё", "и", "ь", "ю", "я" }, "l" },
		"ł"
	},
	["м"] = "m",
	["н"] = "n",
	["о"] = "o",
	["о́"] = "o", -- zignoruj akcent
	["п"] = "p",
	["р"] = "r",
	["с"] = "s",
	["т"] = "t",
	["у"] = "u",
	["ф"] = "f",
	["х"] = "ch",
	["ц"] = "c",
	["ч"] = "cz",
	["ш"] = "sz",
	["щ"] = "szcz",
	["ъ"] = "",
	["ы"] = "y",
	-- Ь > ∅, ´
	["ь"] = {
		{ { "л", "ж", "ч", "ш", "щ" }, "", "" }, -- ∅ po pewnych spółgłoskach
		{ "", { "а", "ё", "и", "о", "у", "ю", "я" }, "" }, -- ∅ przed pewnymi samogłoskami
		"´"
	},
	["э"] = "e",
	-- Ю > JU, IU, U
	["ю"] = {
		{ vowelsRu, "", "ju" }, -- "ju" po samogłoskach
		{ commonTransliteration, "", "ju" }, -- "ju" na początku wyrazu i po interpunkcji
		{ "л", "", "u" },
		"iu"
	},
	-- Я > JA, IA, A
	["я"] = {
		{ vowelsRu, "", "ja" }, -- "ja" po samogłoskach
		{ commonTransliteration, "", "ja" }, -- "ja" na początku wyrazu i po interpunkcji
		{ "л", "", "a" },
		"ia"
	},
    ["я́"] = {
		{ vowelsRu, "", "ja" }, -- "ja" po samogłoskach // zignoruj akcent
		{ commonTransliteration, "", "ja" }, -- "ja" na początku wyrazu i po interpunkcji
		{ "л", "", "a" },
		"ia"
	},
	["’"] = "’"
}

-- Samogłoski ukraińskie, używane przy transkrypcji typu "ja"/"ia"/"a"
local vowelsUk = { "а", "а́", "е", "е́", "є", "є́", "и", "и́", "і", "і́", "ї", "ї́", "о", "о́", "у", "у́", "ю",  "ю́", "я", "я́", "’" }
local transliterationUk = {
	["а"] = "a",
	["а́"] = "a", -- zignoruj akcent
	["б"] = "b",
	["в"] = "w",
	["г"] = "h",
	["ґ"] = "g",
	["д"] = "d",
	["е"] = "e",
	["е́"] = "e", -- zignoruj akcent
	-- Є > JE, IE, E
	["є"] = {
		{ vowelsUk, "", "je" }, -- "je" po samogłoskach
		{ commonTransliteration, "", "je" }, -- "je" na początku wyrazu i po interpunkcji
		{ "л", "", "e" }, -- "e" po л
		"ie"
	},
    ["є́"] = {
		{ vowelsUk, "", "je" }, -- "je" po samogłoskach // zignoruj akcent
		{ commonTransliteration, "", "je" }, -- "je" na początku wyrazu i po interpunkcji
		{ "л", "", "e" }, -- "e" po л
		"ie"
	},
	["ж"] = "ż",
	["з"] = "z",
	["и"] = "y",
	["и́"] = "y",
	["і"] = "i",
	["і́"] = "i", -- zignoruj akcent
	["ї"] = "ji",
	["ї́"] = "ji", -- zignoruj akcent
	["й"] = "j",
	["к"] = "k",
	-- Л > L, Ł
	["л"] = {
		{ "", { "є", "і", "ї", "ю", "я", "л" }, "l" }, -- "l" tylko przed wybranymi literami
		{ "", "е", "l", when={"zapożyczenie"} }, -- "l", gdy w zapożyczeniu w "ле"
		"ł" -- "ł" w pozostałych przypadkach
	},
	["м"] = "m",
	["н"] = "n",
	["о"] = "o",
	["о́"] = "o", -- zignoruj akcent
	["п"] = "p",
	["р"] = "r",
	["с"] = "s",
	["т"] = "t",
	["у"] = "u",
	["у́"] = "u", -- zignoruj akcent
	["ф"] = "f",
	["х"] = "ch",
	["ц"] = "c",
	["ч"] = "cz",
	["ш"] = "sz",
	["щ"] = "szcz",
	-- Ь
	["ь"] = "´",
	["ьо"] = "io",
	["ьо́"] = "io", -- zignoruj akcent
	["ль"] = "l",
	["льо"] = "lo",
	["льо́"] = "lo", -- zignoruj akcent
	["нь"] = "ń",
	["зь"] = "ź",
	["сь"] = "ś",
	["ць"] = "ć",
	["ньо"] = "nio",
	["ньо́"] = "nio", -- zignoruj akcent
	["зьо"] = "zio",
	["зьо́"] = "zio", -- zignoruj akcent
	["сьо"] = "sio",
	["сьо́"] = "sio", -- zignoruj akcent
	["цьо"] = "cio",
	["цьо́"] = "cio", -- zignoruj akcent
	-- Ю > JU, IU, U
	["ю"] = {
		{ vowelsUk, "", "ju" }, -- "ju" po samogłoskach
		{ commonTransliteration, "", "ju" }, -- "ju" na początku wyrazu i po interpunkcji
		{ "л", "", "u" }, -- "u" po л
		"iu"
	},
    -- Ю > JU, IU, U // zignoruj akcent
	["ю́"] = {
		{ vowelsUk, "", "ju" }, -- "ju" po samogłoskach
		{ commonTransliteration, "", "ju" }, -- "ju" na początku wyrazu i po interpunkcji
		{ "л", "", "u" }, -- "u" po л
		"iu"
	},
	-- Я > JA, IA, A
	["я"] = {
		{ vowelsUk, "", "ja" }, -- "ja" po samogłoskach
		{ commonTransliteration, "", "ja" }, -- "ja" na początku wyrazu i po interpunkcji
		{ "л", "", "a" }, -- "a" po л
		"ia"
	},
	-- Я > JA, IA, A // zignoruj akcent
	["я́"] = {
		{ vowelsUk, "", "ja" }, -- "ja" po samogłoskach
		{ commonTransliteration, "", "ja" }, -- "ja" na początku wyrazu i po interpunkcji
		{ "л", "", "a" }, -- "a" po л
		"ia"
	},
	["’"] = ""
}

-- Samogłoski białoruskie, używane przy transkrypcji typu "ja"/"ia"/"a"
local vowelsBe = { "а", "а́", "е", "е́", "ё", "э", "і", "і́", "о", "о́", "у", "у́", "ў", "ы", "ы́", "ю", "ю́", "я", "я́", "’", "ь" }
local transliterationBe = {
	["а"] = "a",
	["а́"] = "a", -- zignoruj akcent
	["б"] = "b",
	["в"] = "w",
	["г"] = {
		{ "", "", "g", when={"zapożyczenie"} }, -- "g", gdy w zapożyczeniu
		"h"
	},
	["д"] = "d",
	-- Е > JE, IE, E
	["е"] = {
		{ vowelsBe, "", "je" }, -- "je" po samogłoskach
		{ commonTransliteration, "", "je" }, -- "je" na początku wyrazu i po interpunkcji
		{ "л", "", "e" }, -- "e" po л
		"ie"
	},
    ["е́"] = {
		{ vowelsBe, "", "je" }, -- "je" po samogłoskach // zignoruj akcent
		{ commonTransliteration, "", "je" }, -- "je" na początku wyrazu i po interpunkcji
		{ "л", "", "e" }, -- "e" po л
		"ie"
	},
	-- Ё > JO, IO, O
	["ё"] = {
		{ vowelsBe, "", "jo" }, -- "jo" po samogłoskach
		{ commonTransliteration, "", "jo" }, -- "jo" na początku wyrazu i po interpunkcji
		{ "л", "", "o" }, -- "o" po л
		"io"
	},
	["ж"] = "ż",
	["з"] = "z",
	["і"] = "i",
	["і́"] = "i",
	["й"] = "j",
	["к"] = "k",
	-- Л > L, Ł
	["л"] = {
		{ "", { "е", "ё", "я", "ю", "і", "ь" }, "l" }, -- "l" tylko przed wybranymi literami
		"ł"
	},
	["м"] = "m",
	["н"] = "n",
	["о"] = "o",
	["о́"] = "o", -- zignoruj akcent
	["п"] = "p",
	["р"] = "r",
	["с"] = "s",
	["т"] = "t",
	["у"] = "u",
	["у́"] = "u", -- zignoruj akcent
	["ў"] = "u",
	["ф"] = "f",
	["х"] = "ch",
	["ц"] = "c",
	["ч"] = "cz",
	["ш"] = "sz",
	["ы"] = "y",
	["ы́"] = "y", -- zignoruj akcent
	-- Ь > ∅, ´
	["нь"] = "ń",
	["зь"] = "ź",
	["сь"] = "ś",
	["ць"] = "ć",
	["ь"] = {
		{ "", vowelsBe, "" }, -- ∅ przed samogłoskami
		{ "л", "", "" }, -- ∅ po л
		"´"
	},
	["э"] = "e",
	-- Ю > JU, IU, U
	["ю"] = {
		{ vowelsBe, "", "ju" }, -- "ju" po samogłoskach
		{ commonTransliteration, "", "ju" }, -- "ju" na początku wyrazu i po interpunkcji
		{ "л", "", "u" }, -- "u" po л
		"iu"
	},
    ["ю́"] = {
		{ vowelsBe, "", "ju" }, -- "ju" po samogłoskach // zignoruj akcent
		{ commonTransliteration, "", "ju" }, -- "ju" na początku wyrazu i po interpunkcji
		{ "л", "", "u" }, -- "u" po л
		"iu"
	},
	-- Я > JA, IA, A
	["я"] = {
		{ vowelsBe, "", "ja" }, -- "ja" po samogłoskach
		{ commonTransliteration, "", "ja" }, -- "ja" na początku wyrazu i po interpunkcji
		{ "л", "", "a" }, -- "a" po л
		"ia"
	},
    ["я́"] = {
		{ vowelsBe, "", "ja" }, -- "ja" po samogłoskach // zignoruj akcent
		{ commonTransliteration, "", "ja" }, -- "ja" na początku wyrazu i po interpunkcji
		{ "л", "", "a" }, -- "a" po л
		"ia"
	},
	["’"] = ""
}

local transliterationBg = {
	["а"] = "a",
	["а̀"] = "a", -- zignoruj akcent
	["б"] = "b",
	["в"] = "w",
	["г"] = "g",
	["д"] = "d",
	["е"] = "e",
	["ѐ"] = "e", -- zignoruj akcent
	["ж"] = "ż",
	["з"] = "z",
	["и"] = "i",
	["ѝ"] = "i", -- zignoruj akcent
	["й"] = "j",
	["к"] = "k",
	-- Л > L, Ł
	["л"] = {
		{ "", { "е", "ѐ", "и", "ѝ", "я", "я̀", "ю", "ю̀" }, "l" }, -- "l" tylko przed wybranymi literami
		"ł"                                  -- "ł" w pozostałych przypadkach
	},
	-- koniec
	["м"] = "m",
	["н"] = "n",
	["о"] = "o",
	["о̀"] = "o", -- zignoruj akcent
	["п"] = "p",
	["р"] = "r",
	["с"] = "s",
	["т"] = "t",
	["у"] = "u",
	["ф"] = "f",
	["х"] = "ch",
	["ц"] = "c",
	["ч"] = "cz",
	["ш"] = "sz",
	["щ"] = "szt",
	["ъ"] = "y",
	["ъ̀"] = "y", -- zignoruj ackent
	["ь"] = "´",
	["ю"] = "ju",
	["ю̀"] = "ju", -- zignoruj ackent
	["я"] = "ja",
	["я̀"] = "ja", -- zignoruj ackent
}

local transliterationMk = {
	["а"] = "a",
    ["б"] = "b",
    ["в"] = "w",
    ["г"] = "g",
    ["д"] = "d",
    ["ѓ"] = {
		{ "", { "а", "о", "у" }, "dzi" }, -- "dzi" przed "а", "о", "у"
		{ "", { "е", "ѐ" }, "gi" }, -- "gi" przed "е"
		{ "", { "и", "ѝ" }, "g" }, -- "g" przed "и"
		"dź"
	},
    ["е"] = "e",
    ["ѐ"] = "e", -- zignoruj akcent
    ["ж"] = "ż",
    ["з"] = "z",
    ["ѕ"] = "dz",
    ["и"] = "i",
    ["ѝ"] = "i", -- zignoruj akcent
    ["ј"] = "j",
    ["к"] = "k",
    ["л"] = {
		{ "", { "и", "ѝ" }, "l" }, -- "l" tylko przed и
		"ł"                    -- "ł" w pozostałych przypadkach
	},
    ["љ"] = "l",
    ["м"] = "m",
    ["н"] = "n",
    ["њ"] = {
		{ "", { "а", "е", "ѐ", "и", "ѝ", "о", "у" }, "ni" }, -- "ni" tylko przed samogłoskami
		"ń"                                  -- "ń" w pozostałych przypadkach
	},
    ["о"] = "o",
    ["п"] = "p",
    ["р"] = "r",
    ["с"] = "s",
    ["т"] = "t",
    ["ќ"] = {
		{ "", { "а", "о", "у" }, "ci" }, -- "ci" przed "а", "о", "у"
		{ "", { "е", "ѐ" }, "ki" }, -- "ki" przed "е"
		{ "", { "и", "ѝ" }, "k" }, -- "k" przed "и"
		"ć"
	},
    ["у"] = "u",
    ["ф"] = "f",
    ["х"] = "h",
    ["ц"] = "c",
    ["ч"] = "cz",
    ["џ"] = "dż",
    ["ш"] = "sz",
}

-- Samogłoski ormiańskie, używane do transkrypcji "r"/"rr"
local vowelsHy = { "ա", "ե", "է", "ը", "ի", "ո", "օ" }
local transliterationHy = {
	["ա"] = "a",
	["բ"] = "b",
	["գ"] = "g",
	["դ"] = "d",
	["ե"] = {
		{ commonTransliteration, "", "je" }, -- "je", gdy na początku i po interpunkcji
		"e"
	},
	["զ"] = "z",
	["է"] = "e",
	["ը"] = "y",
	["թ"] = "t",
	["ժ"] = "ż",
	["ի"] = "i",
	["լ"] = "l",
	["խ"] = "ch",
	["ծ"] = "c",
	["կ"] = "k",
	["հ"] = "h",
	["ձ"] = "dz",
	["ղ"] = "gh",
	-- Ղ > CH (ubezdźwięcznienie niektórych dźwięcznych) ale nie na początku
	["ղբ"] = {
		{ commonTransliteration, "", "chb" }, -- "chb", gdy na początku i po interpunkcji
		"chp"
	},
    ["ղգ"] = {
		{ commonTransliteration, "", "chg" }, -- "chg", gdy na początku i po interpunkcji
		"chk"
	},
    ["ղդ"] = {
		{ commonTransliteration, "", "chd" }, -- "chd", gdy na początku i po interpunkcji
		"cht"
	},
    ["ղձ"] = {
		{ commonTransliteration, "", "chdz" }, -- "chdz", gdy na początku i po interpunkcji
		"chc"
	},
    ["ղջ"] = {
		{ commonTransliteration, "", "chdż" }, -- "chdż", gdy na początku i po interpunkcji
		"chcz"
	},
	-- koniec
	["ճ"] = "cz",
	["մ"] = "m",
	["յ"] = "j",
	["ն"] = "n",
	["շ"] = "sz",
	["ո"] = {
		{ commonTransliteration, "", "wo" }, -- "wo", gdy na początku i po interpunkcji
		"o"
	},
	["չ"] = "cz",
	["պ"] = "p",
	["ջ"] = "dż",
	-- Ռ > R, RR
	["ռ"] = {
		{ vowelsHy, vowelsHy, "rr" }, -- "rr", gdy między samogłoskami
		"r"
	},
	["ս"] = "s",
	["վ"] = "w",
	["տ"] = "t",
	["ր"] = "r",
	["ց"] = "c",
	["ու"] = "u",
	["փ"] = "p",
	["ք"] = "k",
	["օ"] = "o",
	["ֆ"] = "f",
	["եւ"] = {
		{ commonTransliteration, "", "jew" }, -- "jew", gdy na początku i po interpunkcji
		"ew"
	}
}

local transliterations = {
	["ru"] = transliterationRu,
	["uk"] = transliterationUk,
	["be"] = transliterationBe,
	["bg"] = transliterationBg,
	["mk"] = transliterationMk,
	["hy"] = transliterationHy
}

function sanitizeText( text )
	local result = text
	result = string.gsub( result, "&#39;", "'" )
	result = string.gsub( result, "&quot;", "\"" )
	result = string.gsub( result, "&amp;", "&" )
	return result
end

-- Sprawdza, czy w `text` przed pozycją `position` znajduje się ciąg znaków z `lookbehinds`
-- `position` jest indeksem pierwszego znaku za ciągiem z `lookbehinds`
-- `lookbehinds` może być tablicą, wtedy wystarczy by jeden z jej elementów pasował
-- lub ciągiem znaków, wtedy jest traktowany jak jednoelementowa tablica
function doLookbehind( text, position, lookbehinds )
	-- Normalizacja typu
	if type(lookbehinds) == "string" then
		lookbehinds = { lookbehinds }
	end

	for _, lookbehind in pairs( lookbehinds ) do
		local lookbehindLen = mw.ustring.len( lookbehind )
		local lookbehindPos = position - lookbehindLen
	
		-- Pusty ciąg znaków zawsze pasuje
		if lookbehindLen == 0 then
			return true
		end
	
		-- Ciąg się zmieści
		if lookbehindPos >= 1 and lookbehind == mw.ustring.sub( text, lookbehindPos, position - 1 ) then
			return true
		end
	end

	return false
end

-- Sprawdza, czy w `text` za pozycją `position` znajduje się ciąg znaków z `lookbehinds`
-- `position` jest indeksem ostatniego znaku przed ciągiem z `lookaheads`
-- `lookaheads` może być tablicą, wtedy wystarczy by jeden z jej elementów pasował
-- lub ciągiem znaków, wtedy jest traktowany jak jednoelementowa tablica
function doLookahead( text, position, lookaheads )
	-- Normalizacja typu
	if type(lookaheads) == "string" then
		lookaheads = { lookaheads }
	end

	local textLen = mw.ustring.len( text )
	for _, lookahead in pairs( lookaheads ) do
		local lookaheadLen = mw.ustring.len( lookahead )
		local lookaheadEnd = position + lookaheadLen

		-- Pusty ciąg znaków zawsze pasuje
		if lookaheadLen == 0 then
			return true
		end

		-- Ciąg się zmieści
		if lookaheadEnd <= textLen and lookahead == mw.ustring.sub( text, position + 1, lookaheadEnd ) then
			return true
		end
	end

	return false
end

-- Sprawdza zgodność flag z `variantFlags` z `userFlags`
-- Zwraca prawdę jeśli `variantFlags` jest pusta lub jeśli przynajmniej
-- jedna flaga z `variantFlags` jest zawarta w `userFlags`
-- `userFlags` jest tablicą asocjacyjną, gdzie kluczem jest nazwa flagi, a wartością `true`
-- `variantFlags` natomiast jest sekwencją nazw flag
function checkFlags( userFlags, variantFlags )
	if #variantFlags == 0 then
		return true
	end

	for _, flag in pairs( variantFlags ) do
		if userFlags[ flag ] then
			return true
		end
	end

	return false
end

-- Próbuje dopasować regułę dla podciągu `text` zaczynającego się od pozycji `position`,
-- o długości `length`, wykorzystując reguły z `transliteration`
-- `flags` - tablica z opcjami
-- Zwraca dopasowany ciąg znaków lub `nil` jeśli nie udało się dopasować
function tryMatch( transliteration, text, position, length, flags )
	local seq = mw.ustring.sub( text, position, position + length - 1 )
	local trl = transliteration[ seq ] or commonTransliteration[ seq ]

	-- Gdy dopasowanie nie wymaga dodatkowych sprawdzeń
	if type(trl) == "string" then
		return trl
	end

	-- Gdy dopasowanie wymaga dodatkowych sprawdzeń
	if type(trl) == "table" then
		for _, trlVariant in ipairs(trl) do
			-- Wariant bez sprawdzania otoczenia
			if type(trlVariant) ~= "table" then
				return trlVariant
			end

			-- Wariant z sprawdzaniem otoczenia
			local lookbehind = trlVariant[1] or ""
			local lookahead = trlVariant[2] or ""
			local result = trlVariant[3]
			local variantFlags = trlVariant["when"] or {}

			if doLookbehind( text, position, lookbehind )
				and doLookahead( text, position + length - 1, lookahead )
				and checkFlags( flags, variantFlags ) then
					return result
			end
		end
	end

	-- Gdy nie dopasowano
	return nil
end

function p.transliterate( frame )
	local language = frame.args[1]
	local text = sanitizeText( frame.args[2] )
	local flagsText = frame.args["opcje"] or ""
	local transliteration = transliterations[ language ]
	
	if transliteration == nil then
		return "Błędny kod języka: " .. language
	end

	local flags = {}
	if #flagsText > 0 then
		for flag in mw.text.gsplit( flagsText, "," ) do
			flags[ flag ] = true
		end
	end
	
	-- Dla znaków, które należy transkrybować inaczej na początku lub końcu wyrazu
	local boundaryMarker = ' '
	text = boundaryMarker .. text .. boundaryMarker

	local inputLength = mw.ustring.len( text )
	local currentPos = 1
	local result = ''
	local maxSeqLength = 1
	
	-- Wyznacz najdłuższą sekwencję w języku wejściowym
	for seq, _ in pairs( transliteration ) do
		maxSeqLength = math.max( maxSeqLength, mw.ustring.len( seq ) )
	end
	
	while currentPos <= inputLength do
		-- Zacznij od najdłuższej sekwencji, która jeszcze się zmieści
		local initialSeqLength = math.min( maxSeqLength, inputLength - currentPos + 1 )
		local textLower = mw.ustring.lower( text )
		
		-- Próbuj dopasować sekwencje od najdłuższej możliwej (czyli najszczegółowszej)
		for seqLength = initialSeqLength, 1, -1 do
			local trl = tryMatch( transliteration, textLower, currentPos, seqLength, flags )
			
			-- Jeśli dopasowano, dopisz do wyniku i przesuń wskaźnik pozycji
			if trl ~= nil then
				-- Jeśli w oryginale była wielka litera, zmień pierwszą literę wyniku na wielką
				-- Ale pomiń znaki brzegu wyrazu
				local firstNoBoundaryPos = currentPos
				while mw.ustring.sub( text, firstNoBoudaryPos, firstNoBoundaryPos ) == boundaryMarker do
					firstNoBoundaryPos = firstNoBoundaryPos + 1
				end
				if firstNoBoundaryPos >= currentPos + seqLength then
					-- Jeśli wyszliśmy poza dopasowaną sekwencję, wróć do początku
					-- (de facto nic nie rób, bo boundaryMarker nie jest literą)
					firstNoBoundaryPos = currentPos
				end
				
				local firstChar = mw.ustring.sub( text, firstNoBoundaryPos, firstNoBoundaryPos )
				if firstChar ~= mw.ustring.lower( firstChar ) then
					-- Tutaj nie ma konieczności uwzględniania boundaryMarker, bo reguły transkrypcji
					-- nie zawierają go w wyniku
					local firstTrl = mw.ustring.sub( trl, 1, 1 )
					trl = mw.ustring.upper( firstTrl ) .. mw.ustring.sub( trl, 2 )
				end
				
				result = result .. trl
				currentPos = currentPos + seqLength
				break
			end
			
			-- Jeśli nie dopasowano, zwróć błąd tylko przy sekwencji długości 1 (nie ma już ratunku)
			if trl == nil and seqLength == 1 then
				local unTrl = mw.ustring.sub( text, currentPos, currentPos )
				return "Nieprawidłowy znak " .. unTrl .. " dla języka o kodzie " .. language .. "." .. (
					mw.title.getCurrentTitle():inNamespace( 0 ) and errcat or ''
				)
			end
		end
	end
	
	if mw.ustring.sub( result, 1, 1 ) == boundaryMarker then
		result = mw.ustring.sub( result, 2 )
	end
	
	if mw.ustring.sub( result, -1 ) == boundaryMarker then
		result = mw.ustring.sub( result, 1, -2 )
	end
	
	return result
end

return p