Думаю, что объяснять, что такое транслит, никому не нужно — это написание русских слов latinskimi bukvami, понадобилось сделать это на C#.
Первая проблема, которая возникла, это то, что четкого стандарта транслитерации нет, к сожалению, кто в лес, кто по дрова. Я находил даже стандарты с мерзкой диакритикой, т.е. всякими кракозяблями над буквами. В общем, для своего примера я выбрал какой-то довольно пожилой телеграфный стандарт (нашел в печатной книжке), благо, если кому-то что-то не нравится, всегда можно подправить в исходнике, тут меня больше интересует сам пример.
Русская буква | А | Б | В | Г | Д | Е | Ё | Ж |
Латинская буква или буквосочетание | A | B | V | G | D | E | Yo | Zh |
Русская буква | З | И | Й | К | Л | М | Н | О |
Латинская буква или буквосочетание | Z | I | J | K | L | M | N | O |
Русская буква | П | Р | С | Т | У | Ф | Х | Ц |
Латинская буква или буквосочетание | P | R | S | T | U | F | Kh | Ts |
Русская буква | Ч | Ш | Щ | Ъ | Ы | Ь | Э | Ю |
Латинская буква или буквосочетание | Ch | Sh | Shch | » | Y | ‘ | E | Ju |
Русская буква | Я | |||||||
Латинская буква или буквосочетание | Ja |
Вторая проблема — этот стандарт проблематично использовать для транслитерации URL или имен файлов, надо что-то делать с пробелом (который в именах файлов и URL смотрится, как говно), так что в другом варианте таблицы, для замены Ъ
, Ь
и пробела
, был выбран знак подчеркивания (_
).
Создаем новый класс Translit
:
public class Translit { }
В класс Translit
добавляем словарь (Dictionary
), который, в качестве ключа, будет использовать тип char
(русскую букву), а в качестве значения string
, содержащий ее латинский эквивалент. Думаю, ясно, почему string
— некоторые русские буквы передаются латинскими буквосочетаниями:
private Dictionary<char, string> TranslitDict = new Dictionary<char,string>();
Теперь надо создать две функции, которые будут заполнять словарь нужными значениями.
Для общего случая:
private void FormDictStandart() { TranslitDict.Clear(); //Заглавные буквы (общий случай) TranslitDict.Add('А', "A"); //часть кода вырезана для экономии места TranslitDict.Add('Ъ', "''"); TranslitDict.Add('Ы', "Y"); TranslitDict.Add('Ь', "'"); TranslitDict.Add('Э', "E"); TranslitDict.Add('Ю', "Ju"); TranslitDict.Add('Я', "Ja"); //строчные буквы (общий случай) TranslitDict.Add('а', "a"); //часть кода вырезана для экономии места TranslitDict.Add('ъ', "''"); TranslitDict.Add('ы', "y"); TranslitDict.Add('ь', "'"); TranslitDict.Add('э', "e"); TranslitDict.Add('ю', "ju"); TranslitDict.Add('я', "ja"); }
И для «режима совместимости»:
private void FormDictCompat() { TranslitDict.Clear(); //Заглавные буквы (режим совместимости) TranslitDict.Add('А', "A"); //часть кода вырезана для экономии места TranslitDict.Add('Ъ', "_"); TranslitDict.Add('Ы', "Y"); TranslitDict.Add('Ь', "_"); TranslitDict.Add('Э', "E"); TranslitDict.Add('Ю', "Ju"); TranslitDict.Add('Я', "Ja"); //строчные буквы (режим совместимости) TranslitDict.Add('а', "a"); //часть кода вырезана для экономии места TranslitDict.Add('ъ', "_"); TranslitDict.Add('ы', "y"); TranslitDict.Add('ь', "_"); TranslitDict.Add('э', "e"); TranslitDict.Add('ю', "ju"); TranslitDict.Add('я', "ja"); //пробел TranslitDict.Add(' ', "_"); }
Теперь необходимо добавить в класс свойство, чтобы переключать режимы и переформировывать словарь. Классически, добавляем внутреннюю переменную compatibility
типа bool
, для хранения текущего значения свойства, и само свойство, при изменении которого, будет вызываться одна из вышеуказанных функций:
private bool compatibility = false; public bool Compatibility { get { return compatibility; } set { if (value) { FormDictCompat(); compatibility = true; } else { FormDictStandart(); compatibility = false; } } }
Добавим простой конструктор класса:
public Translit(bool Compat) { Compatibility = Compat; }
Алгоритм простой: надо проверить, содержится ли переданный в функцию символ в словаре, если да, то выдать соответствующее значение, если нет (т.е. передана латинская буква, знак препинания, цифра, или что-то иное), выдать этот же символ, преобразованный в строку:
public string TranslitChar(char Rus) { if (TranslitDict.ContainsKey(Rus)) { return TranslitDict[Rus]; } else { return Rus.ToString(); } }
Это я уже делал в маленьком примере (копия), так что просто вставлю функции оттуда:
//русские буквы public static bool ContainsRus(string TestString) { return Regex.IsMatch(TestString, @"[а-я]", RegexOptions.IgnoreCase); } //русские буквы и пробелы public static bool ContainsRusOrSpace(string TestString) { return Regex.IsMatch(TestString, @"[а-я]|\s", RegexOptions.IgnoreCase); }
1. Проверяем, содержит ли строка русские буквы или русские буквы и пробел, в зависимости от режима работы.
Примечание: Сильно работу алгоритма это замедлить не должно, а вот ускорить, в случае какой-нибудь гигантской строки может, т.к. Regex
, который используется в функции поверки, работает со строкой напрямую в памяти, средствами .NET.
Если искомого нет — возвращаем оригинальную строку.
2. Проходимся по символам строки, транслитерируем их, возвращаем новую строку.
Примечание: Для формирования новой строки лучше использовать StringBuilder
вместо простой конкатенации, опять же, на случай, если строка может оказаться гигантской. См. подробности в статье на Хабре
public string TranslitString(string Rus) { string sBuf = ""; StringBuilder sb = new StringBuilder(); if (compatibility) { if (!ContainsRusOrSpace(Rus)) return Rus; } else { if (!ContainsRus(Rus)) return Rus; } for (int i = 0; i < Rus.Length; i++) { if (TranslitDict.ContainsKey(Rus[i])) { sBuf = TranslitDict[Rus[i]]; } else { sBuf = Rus[i].ToString(); } sb.Append(sBuf); } return sb.ToString(); }