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

Hebrew ToWords implementation. #199

Merged
merged 7 commits into from
Apr 13, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ obj/
*.swp
*.orig
*.nupkg
*.crunchproject.local.xml
*.crunchsolution.local.xml
*.ncrunchsolution
*.ncrunchproject
*ncrunch*
*crunch*.local.xml
*.cache
src/packages/*
PackageBuild/*
Expand Down
1 change: 1 addition & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
###In Development
- [#166](https://github.com/MehdiK/Humanizer/pull/166): Added Dutch (NL) Number to words and ordinals
- [#199](https://github.com/MehdiK/Humanizer/pull/199): Added Hebrew Number to words (both genders)

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

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 @@ -87,6 +87,7 @@
<Compile Include="Localisation\fr\DateHumanizeTests.cs" />
<Compile Include="Localisation\fr\NumberToWordsTests.cs" />
<Compile Include="Localisation\fr\TimeSpanHumanizeTests.cs" />
<Compile Include="Localisation\he\NumberToWordsTests.cs" />
<Compile Include="Localisation\invariant\NumberToWordsTests.cs" />
<Compile Include="Localisation\invariant\ToQuantityTests.cs" />
<Compile Include="Localisation\es\NumberToWordsTests.cs" />
Expand Down
115 changes: 115 additions & 0 deletions src/Humanizer.Tests/Localisation/he/NumberToWordsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using Xunit;
using Xunit.Extensions;

namespace Humanizer.Tests.Localisation.he
{
public class NumberToWordsTests : AmbientCulture
{
public NumberToWordsTests() : base("he") { }

[Theory]
[InlineData(0, "אפס")]
[InlineData(1, "אחת")]
[InlineData(2, "שתיים")]
[InlineData(3, "שלוש")]
[InlineData(4, "ארבע")]
[InlineData(5, "חמש")]
[InlineData(6, "שש")]
[InlineData(7, "שבע")]
[InlineData(8, "שמונה")]
[InlineData(9, "תשע")]
[InlineData(10, "עשר")]
[InlineData(11, "אחת עשרה")]
[InlineData(12, "שתים עשרה")]
[InlineData(19, "תשע עשרה")]
[InlineData(20, "עשרים")]
[InlineData(22, "עשרים ושתיים")]
[InlineData(50, "חמישים")]
[InlineData(99, "תשעים ותשע")]
[InlineData(100, "מאה")]
[InlineData(101, "מאה ואחת")]
[InlineData(111, "מאה ואחת עשרה")]
[InlineData(200, "מאתיים")]
[InlineData(241, "מאתיים ארבעים ואחת")]
[InlineData(500, "חמש מאות")]
[InlineData(505, "חמש מאות וחמש")]
[InlineData(725, "שבע מאות עשרים וחמש")]
[InlineData(1000, "אלף")]
[InlineData(1009, "אלף ותשע")]
[InlineData(1011, "אלף ואחת עשרה")]
[InlineData(1024, "אלף עשרים וארבע")]
[InlineData(1040, "אלף ארבעים")]
[InlineData(2000, "אלפיים")]
[InlineData(7021, "שבעת אלפים עשרים ואחת")]
[InlineData(20000, "עשרים אלף")]
[InlineData(28123, "עשרים ושמונה אלף מאה עשרים ושלוש")]
[InlineData(500000, "חמש מאות אלף")]
[InlineData(500001, "חמש מאות אלף ואחת")]
[InlineData(1000000, "מיליון")]
[InlineData(1000001, "מיליון ואחת")]
[InlineData(2000408, "שני מיליון ארבע מאות ושמונה")]
[InlineData(1000000000, "מיליארד")]
[InlineData(1000000001, "מיליארד ואחת")]
[InlineData(int.MaxValue /* 2147483647 */, "שני מיליארד מאה ארבעים ושבעה מיליון ארבע מאות שמונים ושלוש אלף שש מאות ארבעים ושבע")]
public void ToWords(int number, string expected)
{
Assert.Equal(expected, number.ToWords());
}

[Theory]
[InlineData(0, "אפס")]
[InlineData(1, "אחד")]
[InlineData(2, "שניים")]
[InlineData(3, "שלושה")]
[InlineData(4, "ארבעה")]
[InlineData(5, "חמישה")]
[InlineData(6, "שישה")]
[InlineData(7, "שבעה")]
[InlineData(8, "שמונה")]
[InlineData(9, "תשעה")]
[InlineData(10, "עשרה")]
[InlineData(11, "אחד עשר")]
[InlineData(12, "שנים עשר")]
[InlineData(19, "תשעה עשר")]
[InlineData(20, "עשרים")]
[InlineData(22, "עשרים ושניים")]
[InlineData(50, "חמישים")]
[InlineData(99, "תשעים ותשעה")]
[InlineData(100, "מאה")]
[InlineData(101, "מאה ואחד")]
[InlineData(111, "מאה ואחד עשר")]
[InlineData(200, "מאתיים")]
[InlineData(241, "מאתיים ארבעים ואחד")]
[InlineData(500, "חמש מאות")]
[InlineData(505, "חמש מאות וחמישה")]
[InlineData(725, "שבע מאות עשרים וחמישה")]
[InlineData(1000, "אלף")]
[InlineData(1009, "אלף ותשעה")]
[InlineData(1011, "אלף ואחד עשר")]
[InlineData(1024, "אלף עשרים וארבעה")]
[InlineData(1040, "אלף ארבעים")]
[InlineData(2000, "אלפיים")]
[InlineData(7021, "שבעת אלפים עשרים ואחד")]
[InlineData(20000, "עשרים אלף")]
[InlineData(28123, "עשרים ושמונה אלף מאה עשרים ושלושה")]
[InlineData(500000, "חמש מאות אלף")]
[InlineData(500001, "חמש מאות אלף ואחד")]
[InlineData(1000000, "מיליון")]
[InlineData(1000001, "מיליון ואחד")]
[InlineData(2000408, "שני מיליון ארבע מאות ושמונה")]
[InlineData(1000000000, "מיליארד")]
[InlineData(1000000001, "מיליארד ואחד")]
[InlineData(int.MaxValue /* 2147483647 */, "שני מיליארד מאה ארבעים ושבעה מיליון ארבע מאות שמונים ושלוש אלף שש מאות ארבעים ושבעה")]
public void ToWordsMasculine(int number, string expected)
{
Assert.Equal(expected, number.ToWords(GrammaticalGender.Masculine));
}

[Theory]
[InlineData(-2, "מינוס שתיים")]
public void NegativeToWords(int number, string expected)
{
Assert.Equal(expected, number.ToWords());
}
}
}
2 changes: 2 additions & 0 deletions src/Humanizer.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_IFELSE_BRACES_STYLE/@EntryValue">ONLY_FOR_MULTILINE</s:String></wpf:ResourceDictionary>
1 change: 1 addition & 0 deletions src/Humanizer/Humanizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
<Compile Include="DateHumanizeExtensions.cs" />
<Compile Include="Localisation\NumberToWords\EnglishNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\FrenchNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\HebrewNumberToWordsConverter.cs" />
<Compile Include="Localisation\Formatters\CzechSlovakPolishFormatter.cs" />
<Compile Include="DateTimeHumanizeStrategy\DefaultDateTimeHumanizeStrategy.cs" />
<Compile Include="DateTimeHumanizeStrategy\IDateTimeHumanizeStrategy.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;

namespace Humanizer.Localisation.NumberToWords
{
internal class HebrewNumberToWordsConverter : DefaultNumberToWordsConverter
{
private static readonly string[] UnitsFeminine = { "אפס", "אחת", "שתיים", "שלוש", "ארבע", "חמש", "שש", "שבע", "שמונה", "תשע", "עשר" };
private static readonly string[] UnitsMasculine = { "אפס", "אחד", "שניים", "שלושה", "ארבעה", "חמישה", "שישה", "שבעה", "שמונה", "תשעה", "עשרה" };
private static readonly string[] TensUnit = { "עשר", "עשרים", "שלושים", "ארבעים", "חמישים", "שישים", "שבעים", "שמונים", "תשעים" };

private class DescriptionAttribute : Attribute
{
public string Description { get; set; }

public DescriptionAttribute(string description)
{
Description = description;
}
}

private enum Group
{
Hundreds = 100,
Thousands = 1000,
[Description("מיליון")]
Millions = 1000000,
[Description("מיליארד")]
Billions = 1000000000
}

public override string Convert(int number)
{
// in Hebrew, the default number gender form is feminine.
return Convert(number, GrammaticalGender.Feminine);
}

public override string Convert(int number, GrammaticalGender gender)
{
if (number < 0)
return string.Format("מינוס {0}", Convert(-number, gender));

if (number == 0)
return UnitsFeminine[0];

var parts = new List<string>();
if (number >= (int)Group.Billions)
{
ToBigNumber(number, Group.Billions, parts);
number %= (int)Group.Billions;
}

if (number >= (int)Group.Millions)
{
ToBigNumber(number, Group.Millions, parts);
number %= (int)Group.Millions;
}

if (number >= (int)Group.Thousands)
{
ToThousands(number, parts);
number %= (int)Group.Thousands;
}

if (number >= (int)Group.Hundreds)
{
ToHundreds(number, parts);
number %= (int)Group.Hundreds;
}

if (number > 0)
{
bool appendAnd = parts.Count != 0;

if (number <= 10)
{
string unit = gender == GrammaticalGender.Masculine ? UnitsMasculine[number] : UnitsFeminine[number];
if (appendAnd)
unit = "ו" + unit;
parts.Add(unit);
}
else if (number < 20)
{
string unit = Convert(number % 10, gender);
unit = unit.Replace("יי", "י");
unit = string.Format("{0} {1}", unit, gender == GrammaticalGender.Masculine ? "עשר" : "עשרה");
if (appendAnd)
unit = "ו" + unit;
parts.Add(unit);
}
else
{
string tenUnit = TensUnit[number / 10 - 1];
if (number % 10 == 0)
parts.Add(tenUnit);
else
{
string unit = Convert(number % 10, gender);
parts.Add(string.Format("{0} ו{1}", tenUnit, unit));
}
}
}

return string.Join(" ", parts);
}

private void ToBigNumber(int number, Group group, List<string> parts)
{
// Big numbers (million and above) always use the masculine form
// See https://www.safa-ivrit.org/dikduk/numbers.php

int digits = number / (int)@group;
if (digits == 2)
parts.Add("שני");
else if (digits > 2)
parts.Add(Convert(digits, GrammaticalGender.Masculine));
parts.Add(@group.Humanize());
}

private void ToThousands(int number, List<string> parts)
{
int thousands = number / (int)Group.Thousands;

if (thousands == 1)
parts.Add("אלף");
else if (thousands == 2)
parts.Add("אלפיים");
else if (thousands <= 10)
parts.Add(UnitsFeminine[thousands] + "ת" + " אלפים");
else
parts.Add(Convert(thousands) + " אלף");
}

private void ToHundreds(int number, List<string> parts)
{
// For hundreds, Hebrew is using the feminine form
// See https://www.safa-ivrit.org/dikduk/numbers.php

int hundreds = number / (int)Group.Hundreds;

if (hundreds == 1)
parts.Add("מאה");
else if (hundreds == 2)
parts.Add("מאתיים");
else
parts.Add(UnitsFeminine[hundreds] + " מאות");
}
}
}
22 changes: 17 additions & 5 deletions src/Humanizer/NumberToWordsExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public static class NumberToWordsExtension
{"pt-BR", () => new BrazilianPortugueseNumberToWordsConverter()},
{"ru", () => new RussianNumberToWordsConverter()},
{"fr", () => new FrenchNumberToWordsConverter()},
{"nl", () => new DutchNumberToWordsConverter()}
{"nl", () => new DutchNumberToWordsConverter()},
{"he", () => new HebrewNumberToWordsConverter()}
};

/// <summary>
Expand All @@ -35,12 +36,23 @@ public static string ToWords(this int number)
}

/// <summary>
/// for Russian locale
/// 1.ToWords(GrammaticalGender.Masculine) -> "один"
/// 1.ToWords(GrammaticalGender.Feminine) -> "одна"
/// For locales that support gender-specific forms
/// </summary>
/// <example>
/// Russian:
/// <code>
/// 1.ToWords(GrammaticalGender.Masculine) -> "один"
/// 1.ToWords(GrammaticalGender.Feminine) -> "одна"
/// </code>
/// Hebrew:
/// <code>
/// 1.ToWords(GrammaticalGender.Masculine) -> "אחד"
/// 1.ToWords(GrammaticalGender.Feminine) -> "אחת"
/// </code>
/// </example>
///
/// <param name="number">Number to be turned to words</param>
/// <param name="gender">The grammatical gender to use for output words. Defaults to masculine.</param>
/// <param name="gender">The grammatical gender to use for output words</param>
/// <returns></returns>
public static string ToWords(this int number, GrammaticalGender gender)
{
Expand Down