Module:DiacriticNormalization
Appearance
Documentation for this module may be created at Module:DiacriticNormalization/doc
-- Module:DiacriticNormalization
-- Removes diacritics from text for word matching.
-- Features:
-- * Supports Latin and Cyrillic scripts
-- * UTF-8 compatible
-- * Case-preserving
--
-- Usage:
-- local DiacriticNormalization = require('Module:DiacriticNormalization')
-- local normalized = DiacriticNormalization.removeDiacritics("Français") -- Returns "Francais"
local p = {}
-- Cache for processed strings (persists during page render)
local resultCache = {}
-- Diacritics to base character mapping
local diacriticMap = {
-- Latin alphabet with diacritics (lowercase)
["á"] = "a", ["à"] = "a", ["â"] = "a", ["ä"] = "a", ["ã"] = "a", ["å"] = "a", ["ă"] = "a", ["ā"] = "a", ["ą"] = "a", ["ǎ"] = "a", ["ǻ"] = "a", ["ȁ"] = "a", ["ȃ"] = "a", ["ạ"] = "a", ["ả"] = "a", ["ấ"] = "a", ["ầ"] = "a", ["ẩ"] = "a", ["ẫ"] = "a", ["ậ"] = "a", ["ắ"] = "a", ["ằ"] = "a", ["ẳ"] = "a", ["ẵ"] = "a", ["ặ"] = "a",
["æ"] = "ae",
["ḃ"] = "b", ["ḅ"] = "b", ["ḇ"] = "b",
["ć"] = "c", ["ċ"] = "c", ["ĉ"] = "c", ["č"] = "c", ["ç"] = "c", ["ḉ"] = "c",
["ď"] = "d", ["ḋ"] = "d", ["ḍ"] = "d", ["ḏ"] = "d", ["ḑ"] = "d", ["ḓ"] = "d", ["đ"] = "d",
["é"] = "e", ["è"] = "e", ["ê"] = "e", ["ë"] = "e", ["ē"] = "e", ["ĕ"] = "e", ["ė"] = "e", ["ę"] = "e", ["ě"] = "e", ["ȅ"] = "e", ["ȇ"] = "e", ["ẹ"] = "e", ["ẻ"] = "e", ["ẽ"] = "e", ["ế"] = "e", ["ề"] = "e", ["ể"] = "e", ["ễ"] = "e", ["ệ"] = "e",
["ḟ"] = "f",
["ǵ"] = "g", ["ğ"] = "g", ["ĝ"] = "g", ["ġ"] = "g", ["ģ"] = "g", ["ǧ"] = "g", ["ḡ"] = "g",
["ĥ"] = "h", ["ḣ"] = "h", ["ḥ"] = "h", ["ḧ"] = "h", ["ḩ"] = "h", ["ḫ"] = "h", ["ẖ"] = "h",
["í"] = "i", ["ì"] = "i", ["î"] = "i", ["ï"] = "i", ["ĩ"] = "i", ["ī"] = "i", ["ĭ"] = "i", ["į"] = "i", ["ı"] = "i", ["ǐ"] = "i", ["ȉ"] = "i", ["ȋ"] = "i", ["ỉ"] = "i", ["ị"] = "i",
["ĵ"] = "j", ["ǰ"] = "j",
["ķ"] = "k", ["ǩ"] = "k", ["ḱ"] = "k", ["ḳ"] = "k", ["ḵ"] = "k",
["ĺ"] = "l", ["ļ"] = "l", ["ľ"] = "l", ["ḷ"] = "l", ["ḹ"] = "l", ["ḻ"] = "l", ["ḽ"] = "l", ["ł"] = "l",
["ḿ"] = "m", ["ṁ"] = "m", ["ṃ"] = "m",
["ń"] = "n", ["ǹ"] = "n", ["ņ"] = "n", ["ň"] = "n", ["ñ"] = "n", ["ṅ"] = "n", ["ṇ"] = "n", ["ṉ"] = "n", ["ṋ"] = "n",
["ó"] = "o", ["ò"] = "o", ["ô"] = "o", ["ö"] = "o", ["õ"] = "o", ["ō"] = "o", ["ŏ"] = "o", ["ő"] = "o", ["ơ"] = "o", ["ǒ"] = "o", ["ǫ"] = "o", ["ǭ"] = "o", ["ȍ"] = "o", ["ȏ"] = "o", ["ȫ"] = "o", ["ȭ"] = "o", ["ȯ"] = "o", ["ȱ"] = "o", ["ọ"] = "o", ["ỏ"] = "o", ["ố"] = "o", ["ồ"] = "o", ["ổ"] = "o", ["ỗ"] = "o", ["ộ"] = "o", ["ớ"] = "o", ["ờ"] = "o", ["ở"] = "o", ["ỡ"] = "o", ["ợ"] = "o",
["ø"] = "o", ["œ"] = "oe",
["ṕ"] = "p", ["ṗ"] = "p",
["ŕ"] = "r", ["ŗ"] = "r", ["ř"] = "r", ["ȑ"] = "r", ["ȓ"] = "r", ["ṙ"] = "r", ["ṛ"] = "r", ["ṝ"] = "r", ["ṟ"] = "r",
["ś"] = "s", ["ŝ"] = "s", ["ş"] = "s", ["š"] = "s", ["ș"] = "s", ["ṡ"] = "s", ["ṣ"] = "s", ["ṥ"] = "s", ["ṧ"] = "s", ["ṩ"] = "s",
["ţ"] = "t", ["ť"] = "t", ["ț"] = "t", ["ṫ"] = "t", ["ṭ"] = "t", ["ṯ"] = "t", ["ṱ"] = "t",
["þ"] = "th",
["ú"] = "u", ["ù"] = "u", ["û"] = "u", ["ü"] = "u", ["ũ"] = "u", ["ū"] = "u", ["ŭ"] = "u", ["ů"] = "u", ["ű"] = "u", ["ų"] = "u", ["ư"] = "u", ["ǔ"] = "u", ["ǖ"] = "u", ["ǘ"] = "u", ["ǚ"] = "u", ["ǜ"] = "u", ["ȕ"] = "u", ["ȗ"] = "u", ["ụ"] = "u", ["ủ"] = "u", ["ứ"] = "u", ["ừ"] = "u", ["ử"] = "u", ["ữ"] = "u", ["ự"] = "u",
["ṽ"] = "v", ["ṿ"] = "v",
["ẁ"] = "w", ["ẃ"] = "w", ["ẅ"] = "w", ["ẇ"] = "w", ["ẉ"] = "w", ["ẘ"] = "w",
["ẋ"] = "x", ["ẍ"] = "x",
["ý"] = "y", ["ỳ"] = "y", ["ŷ"] = "y", ["ÿ"] = "y", ["ȳ"] = "y", ["ẏ"] = "y", ["ẙ"] = "y", ["ỵ"] = "y", ["ỷ"] = "y", ["ỹ"] = "y",
["ź"] = "z", ["ż"] = "z", ["ž"] = "z", ["ẑ"] = "z", ["ẓ"] = "z", ["ẕ"] = "z",
-- Latin alphabet with diacritics (uppercase)
["Á"] = "A", ["À"] = "A", ["Â"] = "A", ["Ä"] = "A", ["Ã"] = "A", ["Å"] = "A", ["Ă"] = "A", ["Ā"] = "A", ["Ą"] = "A", ["Ǎ"] = "A", ["Ǻ"] = "A", ["Ȁ"] = "A", ["Ȃ"] = "A", ["Ạ"] = "A", ["Ả"] = "A", ["Ấ"] = "A", ["Ầ"] = "A", ["Ẩ"] = "A", ["Ẫ"] = "A", ["Ậ"] = "A", ["Ắ"] = "A", ["Ằ"] = "A", ["Ẳ"] = "A", ["Ẵ"] = "A", ["Ặ"] = "A",
["Æ"] = "AE",
["Ḃ"] = "B", ["Ḅ"] = "B", ["Ḇ"] = "B",
["Ć"] = "C", ["Ċ"] = "C", ["Ĉ"] = "C", ["Č"] = "C", ["Ç"] = "C", ["Ḉ"] = "C",
["Ď"] = "D", ["Ḋ"] = "D", ["Ḍ"] = "D", ["Ḏ"] = "D", ["Ḑ"] = "D", ["Ḓ"] = "D", ["Đ"] = "D",
["É"] = "E", ["È"] = "E", ["Ê"] = "E", ["Ë"] = "E", ["Ē"] = "E", ["Ĕ"] = "E", ["Ė"] = "E", ["Ę"] = "E", ["Ě"] = "E", ["Ȅ"] = "E", ["Ȇ"] = "E", ["Ẹ"] = "E", ["Ẻ"] = "E", ["Ẽ"] = "E", ["Ế"] = "E", ["Ề"] = "E", ["Ể"] = "E", ["Ễ"] = "E", ["Ệ"] = "E",
["Ḟ"] = "F",
["Ǵ"] = "G", ["Ğ"] = "G", ["Ĝ"] = "G", ["Ġ"] = "G", ["Ģ"] = "G", ["Ǧ"] = "G", ["Ḡ"] = "G",
["Ĥ"] = "H", ["Ḣ"] = "H", ["Ḥ"] = "H", ["Ḧ"] = "H", ["Ḩ"] = "H", ["Ḫ"] = "H",
["Í"] = "I", ["Ì"] = "I", ["Î"] = "I", ["Ï"] = "I", ["Ĩ"] = "I", ["Ī"] = "I", ["Ĭ"] = "I", ["Į"] = "I", ["İ"] = "I", ["Ǐ"] = "I", ["Ȉ"] = "I", ["Ȋ"] = "I", ["Ỉ"] = "I", ["Ị"] = "I",
["Ĵ"] = "J",
["Ķ"] = "K", ["Ǩ"] = "K", ["Ḱ"] = "K", ["Ḳ"] = "K", ["Ḵ"] = "K",
["Ĺ"] = "L", ["Ļ"] = "L", ["Ľ"] = "L", ["Ḷ"] = "L", ["Ḹ"] = "L", ["Ḻ"] = "L", ["Ḽ"] = "L", ["Ł"] = "L",
["Ḿ"] = "M", ["Ṁ"] = "M", ["Ṃ"] = "M",
["Ń"] = "N", ["Ǹ"] = "N", ["Ņ"] = "N", ["Ň"] = "N", ["Ñ"] = "N", ["Ṅ"] = "N", ["Ṇ"] = "N", ["Ṉ"] = "N", ["Ṋ"] = "N",
["Ó"] = "O", ["Ò"] = "O", ["Ô"] = "O", ["Ö"] = "O", ["Õ"] = "O", ["Ō"] = "O", ["Ŏ"] = "O", ["Ő"] = "O", ["Ơ"] = "O", ["Ǒ"] = "O", ["Ǫ"] = "O", ["Ǭ"] = "O", ["Ȍ"] = "O", ["Ȏ"] = "O", ["Ȫ"] = "O", ["Ȭ"] = "O", ["Ȯ"] = "O", ["Ȱ"] = "O", ["Ọ"] = "O", ["Ỏ"] = "O", ["Ố"] = "O", ["Ồ"] = "O", ["Ổ"] = "O", ["Ỗ"] = "O", ["Ộ"] = "O", ["Ớ"] = "O", ["Ờ"] = "O", ["Ở"] = "O", ["Ỡ"] = "O", ["Ợ"] = "O",
["Ø"] = "O", ["Œ"] = "OE",
["Ṕ"] = "P", ["Ṗ"] = "P",
["Ŕ"] = "R", ["Ŗ"] = "R", ["Ř"] = "R", ["Ȑ"] = "R", ["Ȓ"] = "R", ["Ṙ"] = "R", ["Ṛ"] = "R", ["Ṝ"] = "R", ["Ṟ"] = "R",
["Ś"] = "S", ["Ŝ"] = "S", ["Ş"] = "S", ["Š"] = "S", ["Ș"] = "S", ["Ṡ"] = "S", ["Ṣ"] = "S", ["Ṥ"] = "S", ["Ṧ"] = "S", ["Ṩ"] = "S",
["Ţ"] = "T", ["Ť"] = "T", ["Ț"] = "T", ["Ṫ"] = "T", ["Ṭ"] = "T", ["Ṯ"] = "T", ["Ṱ"] = "T",
["Þ"] = "TH",
["Ú"] = "U", ["Ù"] = "U", ["Û"] = "U", ["Ü"] = "U", ["Ũ"] = "U", ["Ū"] = "U", ["Ŭ"] = "U", ["Ů"] = "U", ["Ű"] = "U", ["Ų"] = "U", ["Ư"] = "U", ["Ǔ"] = "U", ["Ǖ"] = "U", ["Ǘ"] = "U", ["Ǚ"] = "U", ["Ǜ"] = "U", ["Ȕ"] = "U", ["Ȗ"] = "U", ["Ụ"] = "U", ["Ủ"] = "U", ["Ứ"] = "U", ["Ừ"] = "U", ["Ử"] = "U", ["Ữ"] = "U", ["Ự"] = "U",
["Ṽ"] = "V", ["Ṿ"] = "V",
["Ẁ"] = "W", ["Ẃ"] = "W", ["Ẅ"] = "W", ["Ẇ"] = "W", ["Ẉ"] = "W",
["Ẋ"] = "X", ["Ẍ"] = "X",
["Ý"] = "Y", ["Ỳ"] = "Y", ["Ŷ"] = "Y", ["Ÿ"] = "Y", ["Ȳ"] = "Y", ["Ẏ"] = "Y", ["Ỵ"] = "Y", ["Ỷ"] = "Y", ["Ỹ"] = "Y",
["Ź"] = "Z", ["Ż"] = "Z", ["Ž"] = "Z", ["Ẑ"] = "Z", ["Ẓ"] = "Z", ["Ẕ"] = "Z",
-- Cyrillic alphabet with diacritics (lowercase)
["ѐ"] = "е", ["ё"] = "е", ["є"] = "е", ["ѓ"] = "г", ["ќ"] = "к",
["ѝ"] = "и", ["й"] = "и", ["ї"] = "і", ["ў"] = "у", ["ґ"] = "г",
["ӂ"] = "ж", ["ӑ"] = "а", ["ӓ"] = "а", ["ӕ"] = "а", ["ӗ"] = "е",
["ӛ"] = "е", ["ӝ"] = "ж", ["ӟ"] = "з", ["ӡ"] = "з", ["ӣ"] = "и",
["ӥ"] = "и", ["ӧ"] = "о", ["ӫ"] = "о", ["ӯ"] = "у", ["ӱ"] = "у",
["ӳ"] = "у", ["ӵ"] = "ч", ["ӹ"] = "ы", ["ӻ"] = "ы", ["ӽ"] = "х",
["ӿ"] = "х",
-- Cyrillic alphabet with diacritics (uppercase)
["Ѐ"] = "Е", ["Ё"] = "Е", ["Є"] = "Е", ["Ѓ"] = "Г", ["Ќ"] = "К",
["Ѝ"] = "И", ["Й"] = "И", ["Ї"] = "І", ["Ў"] = "У", ["Ґ"] = "Г",
["Ӂ"] = "Ж", ["Ӑ"] = "А", ["Ӓ"] = "А", ["Ӕ"] = "А", ["Ӗ"] = "Е",
["Ӛ"] = "Е", ["Ӝ"] = "Ж", ["Ӟ"] = "З", ["Ӡ"] = "З", ["Ӣ"] = "И",
["Ӥ"] = "И", ["Ӧ"] = "О", ["Ӫ"] = "О", ["Ӯ"] = "У", ["Ӱ"] = "У",
["Ӳ"] = "У", ["Ӵ"] = "Ч", ["Ӹ"] = "Ы", ["Ӻ"] = "Ы", ["Ӽ"] = "Х",
["Ӿ"] = "Х"
}
-- Remove diacritics from Latin and Cyrillic text
function p.removeDiacritics(text)
if not text then return nil end
-- Check cache first
if resultCache[text] then
return resultCache[text]
end
-- Replace diacritics with base characters (UTF-8 aware)
local result = text:gsub(".", function(c) return diacriticMap[c] or c end)
-- Cache result
resultCache[text] = result
return result
end
return p