I want to get a code review on the following transliteration code. I wrote it because there were some libraries that I have tried and they were specifically failing with the name "Yuliya" from Latin to Cyrillic. I wrote unit tests for my functions.
Things I noticed:
- I don't need it to support both standards GOST 16876-71 and ISO 9-95, because I would use it to transliterate from/to Bulgarian and if you pay attention to
CyrillicLowercase
andLatinLowercase
, it's a bit different than these standards. - Having
CyrillicUppercase
andCyrillicLowercase
separately is not great. I believe this snippet solves it. I mean it doesn't have 4x const strings like I do, which is much better. It puts.ToLower()
and.ToUpper()
in the code. - you might ask why I did
.OrderByDescending(x => x.Value.Length)
specifically forLatinToCyrillic
and the reason is because if you transliterate the name "Yuliya Koleva Hadzhiivanova" (Latin to Cyrillic), it won't think ofYu
as a whole, instead it will think of it asY
andu
, which would result in "Иулия..." instead of "Юлия...", which is wrong. I found that solution in this code and used it. - I used
.Aggregate
and this code uses.Replace
. Which one is more optimal?
public sealed class Transliteration
{
private const string CyrillicLowercase = "а б в г д е ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ь ю я";
private const string CyrillicUppercase = "А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ь Ю Я";
private const string LatinLowercase = "a b v g d e zh z i y k l m n o p r s t u f h ts ch sh sht a y yu ya";
private const string LatinUppercase = "A B V G D E Zh Z I Y K L M N O P R S T U F H Ts Ch Sh Sht A Y Yu Ya";
private readonly Dictionary<string,string> _dict;
private Transliteration()
{
_dict = new Dictionary<string, string>();
}
private Transliteration(Dictionary<string, string> dict)
{
ArgumentNullException.ThrowIfNull(dict);
_dict = dict;
}
public static Transliteration Create()
{
var lowercaseDict = CyrillicLowercase.Split(' ')
.Zip(LatinLowercase.Split(' '), (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
var uppercaseDict = CyrillicUppercase.Split(' ')
.Zip(LatinUppercase.Split(' '), (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
var both = lowercaseDict.Concat(uppercaseDict)
.ToLookup(x => x.Key, x => x.Value)
.ToDictionary(x => x.Key, g => g.First());
return new Transliteration(both);
}
/// <summary>
/// Transliterates cyrillic text to latin.
/// </summary>
/// <param name="text">The text to be transliterated.</param>
/// <returns>The transliterated text.</returns>
public string CyrillicToLatin(string text)
{
return _dict.Aggregate(text, (current, pair) => current.Replace(pair.Key, pair.Value));
}
/// <summary>
/// Transliterates latin text to cyrillic.
/// </summary>
/// <param name="text">The text to be transliterated.</param>
/// <returns>The transliterated text.</returns>
public string LatinToCyrillic(string text)
{
return _dict.OrderByDescending(x => x.Value.Length).Aggregate(text, (current, pair) => current.Replace(pair.Value, pair.Key));
}
}
xUnit
public class TransliterationTests
{
private readonly Transliteration _transliteration;
public TransliterationTests()
{
_transliteration = Transliteration.Create();
}
[Fact]
public void CyrillicToLatinTextShouldEqualTheirEqual()
{
// Arrange
// Act
var actual = _transliteration.CyrillicToLatin("Юлия Колева Хаджииванова");
// Assert
actual.Should().Be("Yuliya Koleva Hadzhiivanova");
}
[Fact]
public void LatinToCyrillicTextShouldEqualTheirEqual()
{
// Arrange
// Act
var actual = _transliteration.LatinToCyrillic("Yuliya Koleva Hadzhiivanova");
// Assert
actual.Should().Be("Юлия Колева Хаджииванова");
}
}