package profanity import ( "strings" "unicode" ) // cyrillicHomoglyphsToLatin: кириллица, похожая на латиницу → латиница (обход «fuсk» с кириллической «с»). func cyrillicHomoglyphsToLatin(s string) string { var b strings.Builder b.Grow(len(s)) for _, r := range s { if lat, ok := cyrillicToLatinConfusable[r]; ok { b.WriteRune(lat) continue } b.WriteRune(r) } return b.String() } // latinAndDigitHomoglyphsToCyrillic: латиница и цифры, похожие на кириллицу → кириллица. // Для ников со смешанным вводом (есть хотя бы одна кириллическая буква). func latinAndDigitHomoglyphsToCyrillic(s string) string { var b strings.Builder b.Grow(len(s)) for _, r := range s { if cy, ok := latinToCyrillicConfusable[r]; ok { b.WriteRune(cy) continue } if cy, ok := digitToCyrillicConfusable[r]; ok { b.WriteRune(cy) continue } b.WriteRune(unicode.ToLower(r)) } return b.String() } func hasCyrillicLetter(s string) bool { for _, r := range s { if unicode.Is(unicode.Cyrillic, r) { return true } } return false } var cyrillicToLatinConfusable = map[rune]rune{ 'А': 'a', 'а': 'a', 'В': 'b', 'в': 'b', 'Е': 'e', 'е': 'e', 'З': '3', 'з': '3', 'И': 'u', 'и': 'u', 'К': 'k', 'к': 'k', 'М': 'm', 'м': 'm', 'Н': 'h', 'н': 'h', 'О': 'o', 'о': 'o', 'Р': 'p', 'р': 'p', 'С': 'c', 'с': 'c', 'Т': 't', 'т': 't', 'У': 'y', 'у': 'y', 'Х': 'x', 'х': 'x', 'Ч': '4', 'ч': '4', } var latinToCyrillicConfusable = map[rune]rune{ 'a': 'а', 'A': 'а', 'b': 'в', 'B': 'в', 'c': 'с', 'C': 'с', 'd': 'д', 'D': 'д', 'e': 'е', 'E': 'е', 'f': 'ф', 'F': 'ф', 'h': 'х', 'H': 'х', 'i': 'и', 'I': 'и', 'k': 'к', 'K': 'к', 'm': 'м', 'M': 'м', 'o': 'о', 'O': 'о', 'p': 'р', 'P': 'р', 's': 'с', 'S': 'с', 't': 'т', 'T': 'т', 'u': 'и', 'U': 'и', 'v': 'в', 'V': 'в', 'w': 'ш', 'W': 'ш', 'x': 'х', 'X': 'х', 'y': 'у', 'Y': 'у', } var digitToCyrillicConfusable = map[rune]rune{ '0': 'о', '3': 'з', '4': 'ч', '6': 'б', '8': 'в', }