Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add russian translation for ToWords #147

Closed
wants to merge 13 commits into from
9 changes: 9 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,15 @@ Humanizer can change numbers to words using the `ToWords` extension:
3501.ToWords() => "three thousand five hundred and one"
```

You can also pass a second argument, `GrammaticalGender`, to `ToWords` to specify which gender the number should be outputted in. The possible values are `GrammaticalGender.Masculine`, `GrammaticalGender.Feminine` and `GrammaticalGender.Neuter`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this :)


```C#
// for Russian locale
1.ToWords(GrammaticalGender.Masculine) => "один"
1.ToWords(GrammaticalGender.Feminine) => "одна"
1.ToWords(GrammaticalGender.Neuter) => "одно"
```

###<a id="number-toordinalwords">Number to ordinal words</a>
This is kind of mixing `ToWords` with `Ordinalize`. You can call `ToOrdinalWords` on a number to get an ordinal representation of the number in words!! Let me show that with an example:

Expand Down
2 changes: 2 additions & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
###In Development
- [#147](https://github.com/MehdiK/Humanizer/pull/147): Added Russian translation for ToWords

[Commits](https://github.com/MehdiK/Humanizer/compare/v1.20.15...master)

###v1.20.15 - 2014-04-12
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@ public class EnumHumanizeExtensions
public string Humanize(System.Enum input, Humanizer.LetterCasing casing) { }
}

public enum GrammaticalCase
{
Accusative,
Dative,
Genitive,
Instrumental,
Nominative,
Prepositional,
value__,
}

public enum GrammaticalGender
{
Feminine,
Masculine,
Neuter,
value__,
}

public class In
{
public In() { }
Expand Down Expand Up @@ -181,12 +200,6 @@ public interface IFormatter
string TimeSpanHumanize_Zero();
}

public interface INumberToWordsConverter
{
string Convert(int number);
string ConvertToOrdinal(int number);
}

public class ResourceKeys
{
public ResourceKeys() { }
Expand Down Expand Up @@ -238,6 +251,7 @@ public class NumberToWordsExtension
{
public string ToOrdinalWords(int number) { }
public string ToWords(int number) { }
public string ToWords(int number, Humanizer.GrammaticalGender gender) { }
}

public class On
Expand Down
1 change: 1 addition & 0 deletions src/Humanizer.Tests/Humanizer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
<Compile Include="Localisation\pl\DateHumanizeTests.cs" />
<Compile Include="Localisation\pl\NumberToWordsTests.cs" />
<Compile Include="Localisation\pl\TimeSpanHumanizeTests.cs" />
<Compile Include="Localisation\ru-RU\NumberToWordsTests.cs" />
<Compile Include="Localisation\ru-RU\TimeSpanHumanizeTests.cs" />
<Compile Include="Localisation\sk\DateHumanizeTests.cs" />
<Compile Include="Localisation\ar\DateHumanizeTests.cs" />
Expand Down
78 changes: 78 additions & 0 deletions src/Humanizer.Tests/Localisation/ru-RU/NumberToWordsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using Xunit;
using Xunit.Extensions;

namespace Humanizer.Tests.Localisation.ruRU
{
public class NumberToWordsTests : AmbientCulture
{
public NumberToWordsTests() : base("ru-RU") { }

[Theory]
[InlineData(0, "ноль")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holy crap!! ;)

[InlineData(1, "один")]
[InlineData(10, "десять")]
[InlineData(11, "одиннадцать")]
[InlineData(12, "двенадцать")]
[InlineData(13, "тринадцать")]
[InlineData(14, "четырнадцать")]
[InlineData(15, "пятнадцать")]
[InlineData(16, "шестнадцать")]
[InlineData(17, "семнадцать")]
[InlineData(18, "восемнадцать")]
[InlineData(19, "девятнадцать")]
[InlineData(20, "двадцать")]
[InlineData(30, "тридцать")]
[InlineData(40, "сорок")]
[InlineData(50, "пятьдесят")]
[InlineData(60, "шестьдесят")]
[InlineData(70, "семьдесят")]
[InlineData(80, "восемьдесят")]
[InlineData(90, "девяносто")]
[InlineData(100, "сто")]
[InlineData(200, "двести")]
[InlineData(300, "триста")]
[InlineData(400, "четыреста")]
[InlineData(500, "пятьсот")]
[InlineData(600, "шестьсот")]
[InlineData(700, "семьсот")]
[InlineData(800, "восемьсот")]
[InlineData(900, "девятьсот")]
[InlineData(1000, "одна тысяча")]
[InlineData(2000, "две тысячи")]
[InlineData(3000, "три тысячи")]
[InlineData(4000, "четыре тысячи")]
[InlineData(5000, "пять тысячь")]
[InlineData(10000, "десять тысячь")]
[InlineData(100000, "сто тысячь")]
[InlineData(1000000, "один миллион")]
[InlineData(2000000, "два миллиона")]
[InlineData(10000000, "десять миллионов")]
[InlineData(100000000, "сто миллионов")]
[InlineData(1000000000, "один миллиард")]
[InlineData(2000000000, "два миллиарда")]
[InlineData(122, "сто двадцать два")]
[InlineData(3501, "три тысячи пятьсот один")]
[InlineData(111, "сто одиннадцать")]
[InlineData(1112, "одна тысяча сто двенадцать")]
[InlineData(11213, "одиннадцать тысячь двести тринадцать")]
[InlineData(121314, "сто двадцать одна тысяча триста четырнадцать")]
[InlineData(2132415, "два миллиона сто тридцать две тысячи четыреста пятнадцать")]
[InlineData(12345516, "двенадцать миллионов триста сорок пять тысячь пятьсот шестнадцать")]
[InlineData(751633617, "семьсот пятьдесят один миллион шестьсот тридцать три тысячи шестьсот семнадцать")]
[InlineData(1111111118, "один миллиард сто одиннадцать миллионов сто одиннадцать тысячь сто восемнадцать")]
[InlineData(-751633617, "минус семьсот пятьдесят один миллион шестьсот тридцать три тысячи шестьсот семнадцать")]
public void ToWords(int number, string expected)
{
Assert.Equal(expected, number.ToWords());
}

[Theory]
[InlineData(122, "сто двадцать две", GrammaticalGender.Feminine)]
[InlineData(3501, "три тысячи пятьсот одна", GrammaticalGender.Feminine)]
[InlineData(3501, "три тысячи пятьсот одно", GrammaticalGender.Neuter)]
public void ToWordsWithGender(int number, string expected, GrammaticalGender gender)
{
Assert.Equal(expected, number.ToWords(gender));
}
}
}
33 changes: 33 additions & 0 deletions src/Humanizer/GrammaticalCase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Humanizer
{
/// <summary>
/// Options for specifying the desired grammatical case for the output words
/// </summary>
public enum GrammaticalCase
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Thanks for promoting this.

{
/// <summary>
/// Indicates the subject of a finite verb
/// </summary>
Nominative,
/// <summary>
/// Indicates the possessor of another noun
/// </summary>
Genitive,
/// <summary>
/// Indicates the indirect object of a verb
/// </summary>
Dative,
/// <summary>
/// Indicates the direct object of a verb
/// </summary>
Accusative,
/// <summary>
/// Indicates an object used in performing an action
/// </summary>
Instrumental,
/// <summary>
/// Indicates the object of a preposition
/// </summary>
Prepositional,
}
}
21 changes: 21 additions & 0 deletions src/Humanizer/GrammaticalGender.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Humanizer
{
/// <summary>
/// Options for specifying the desired grammatical gender for the output words
/// </summary>
public enum GrammaticalGender
{
/// <summary>
/// Indicates masculine grammatical gender
/// </summary>
Masculine,
/// <summary>
/// Indicates feminine grammatical gender
/// </summary>
Feminine,
/// <summary>
/// Indicates neuter grammatical gender
/// </summary>
Neuter
}
}
5 changes: 5 additions & 0 deletions src/Humanizer/Humanizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
<AssemblyOriginatorKeyFile>Humanizer.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="GrammaticalCase.cs" />
<Compile Include="GrammaticalGender.cs" />
<Compile Include="Localisation\GrammaticalNumber\RussianGrammaticalNumber.cs" />
<Compile Include="Localisation\GrammaticalNumber\RussianGrammaticalNumberDetector.cs" />
<Compile Include="Localisation\NumberToWords\DefaultNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\FarsiNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\ArabicNumberToWordsConverter.cs" />
Expand All @@ -80,6 +84,7 @@
<Compile Include="DateTimeHumanizeStrategy\IDateTimeHumanizeStrategy.cs" />
<Compile Include="DateTimeHumanizeStrategy\PrecisionDateTimeHumanizeStrategy.cs" />
<Compile Include="Localisation\NumberToWords\PolishNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\RussianNumberToWordsConverter.cs" />
<Compile Include="Localisation\Tense.cs" />
<Compile Include="Localisation\NumberToWords\SpanishNumberToWordsConverter.cs" />
<Compile Include="TimeSpanHumanizeExtensions.cs" />
Expand Down
32 changes: 15 additions & 17 deletions src/Humanizer/Localisation/Formatters/RussianFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
namespace Humanizer.Localisation.Formatters
using Humanizer.Localisation.GrammaticalNumber;

namespace Humanizer.Localisation.Formatters
{
internal class RussianFormatter : DefaultFormatter
{
private const string SingularPostfix = "_Singular";
private const string PaucalPostfix = "_Paucal";

protected override string GetResourceKey(string resourceKey, int number)
{
var mod100 = number%100;
if (mod100/10 != 1)
{
var mod10 = number%10;

if (mod10 == 1) // 1, 21, 31, 41 ... 91, 101, 121 ..
return resourceKey + SingularPostfix;
var grammaticalNumber = RussianGrammaticalNumberDetector.Detect(number);
var suffix = GetSuffix(grammaticalNumber);
return resourceKey + suffix;
}

if (mod10 > 1 && mod10 < 5) // 2, 3, 4, 22, 23, 24 ...
return resourceKey + PaucalPostfix;
}

return resourceKey;
private string GetSuffix(RussianGrammaticalNumber grammaticalNumber)
{
if (grammaticalNumber == RussianGrammaticalNumber.Singular)
return "_Singular";
if (grammaticalNumber == RussianGrammaticalNumber.Paucal)
return "_Paucal";
return "";
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Humanizer.Localisation.GrammaticalNumber
{
internal enum RussianGrammaticalNumber
{
Singular,
Paucal,
Plural
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Humanizer.Localisation.GrammaticalNumber
{
internal static class RussianGrammaticalNumberDetector
{
public static RussianGrammaticalNumber Detect(int number)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice :)

{
var tens = number % 100 / 10;
if (tens != 1)
{
var unity = number % 10;

if (unity == 1) // 1, 21, 31, 41 ... 91, 101, 121 ...
return RussianGrammaticalNumber.Singular;

if (unity > 1 && unity < 5) // 2, 3, 4, 22, 23, 24 ...
return RussianGrammaticalNumber.Paucal;
}

return RussianGrammaticalNumber.Plural;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
{
internal class DefaultNumberToWordsConverter : INumberToWordsConverter
{
public virtual string Convert(int number, GrammaticalGender gender)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, as mentioned above, this is unnecessary API change that would be good if we could avoid.

{
return Convert(number);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much neater. Thanks.

}

public virtual string Convert(int number)
{
return number.ToString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Humanizer.Localisation.NumberToWords
{
internal class EnglishNumberToWordsConverter : INumberToWordsConverter
internal class EnglishNumberToWordsConverter : DefaultNumberToWordsConverter
{
private static readonly string[] UnitsMap = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
private static readonly string[] TensMap = { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
Expand All @@ -20,7 +20,7 @@ internal class EnglishNumberToWordsConverter : INumberToWordsConverter
{12, "twelfth"},
};

public string Convert(int number)
public override string Convert(int number)
{
if (number == 0)
return "zero";
Expand Down Expand Up @@ -74,7 +74,7 @@ public string Convert(int number)
return string.Join(" ", parts.ToArray());
}

public string ConvertToOrdinal(int number)
public override string ConvertToOrdinal(int number)
{
string towords;
// 9 => ninth
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// <summary>
/// An abstraction to convert number to words
/// </summary>
public interface INumberToWordsConverter
internal interface INumberToWordsConverter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I am wondering why we even need this interface anymore, as it could be a bit confusing. Keeping it there for now; but may drop it later.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right it's the same as DefaultNumberToWordsConverter.

{
/// <summary>
/// 3501.ToWords() -> "three thousand five hundred and one"
Expand All @@ -12,6 +12,16 @@ public interface INumberToWordsConverter
/// <returns></returns>
string Convert(int number);

/// <summary>
/// for Russian locale
/// 1.ToWords(GrammaticalGender.Masculine) -> "один"
/// 1.ToWords(GrammaticalGender.Feminine) -> "одна"
/// </summary>
/// <param name="number">Number to be turned to words</param>
/// <param name="gender">The grammatical gender to use for output words</param>
/// <returns></returns>
string Convert(int number, GrammaticalGender gender);

/// <summary>
/// 1.ToOrdinalWords() -> "first"
/// </summary>
Expand Down
Loading