Преамбула
Думаю, что объяснять, что такое транслит, никому не нужно — это написание русских слов 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();
}
Исходники
Класс Translit
на GitHub
Тестовый пример на GitHub