Skip to content

Commit

Permalink
Merge pull request #300 from dmitry-gokun/master
Browse files Browse the repository at this point in the history
Added optional Culture parameter to NumberToWords
  • Loading branch information
MehdiK committed Jun 22, 2014
2 parents cacb30b + 7ccfd20 commit 08e76e9
Show file tree
Hide file tree
Showing 24 changed files with 273 additions and 138 deletions.
14 changes: 14 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,13 @@ The possible values are `GrammaticalGender.Masculine`, `GrammaticalGender.Femini

Obviously this only applies to some cultures. For others passing gender in doesn't make any difference in the result.

Also, culture to use can be specified explicitly. If it is not, current thread's current UI culture is used. Here's an example:

```C#
11.ToWords(new CultureInfo("en")) => "eleven"
1.ToWords(GrammaticalGender.Masculine, new CultureInfo("ru")) => "один"
```

###<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! For example:

Expand Down Expand Up @@ -592,6 +599,13 @@ The possible values are `GrammaticalGender.Masculine`, `GrammaticalGender.Femini

Obviously this only applies to some cultures. For others passing gender in doesn't make any difference in the result.

Also, culture to use can be specified explicitly. If it is not, current thread's current UI culture is used. Here's an example:

```C#
10.ToOrdinalWords(new CultureInfo("en-US")) => "tenth"
1.ToOrdinalWords(GrammaticalGender.Masculine, new CulureInfo("pt-BR")) => "primeiro"
```

###<a id="roman-numerals">Roman numerals</a>
Humanizer can change numbers to Roman numerals using the `ToRoman` extension. The numbers 1 to 10 can be expressed in Roman numerals as follows:

Expand Down
1 change: 1 addition & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [#278](https://github.com/MehdiK/Humanizer/pull/278): Changed DefaultDateTimeHumanizeStrategy to turn 60 min to one hour not 45
- [#283](https://github.com/MehdiK/Humanizer/pull/283): Added Neutral nb support for DateTime and TimeSpan Humanize
- [#286](https://github.com/MehdiK/Humanizer/pull/286): Added optional Culture parameter to DateTime.Humanize & TimeSpan.Humanize
- [#295](https://github.com/MehdiK/Humanizer/pull/295): Added optional Culture parameter to NumberToWords

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ public class Configurator
public class LocaliserRegistry`1
{
public LocaliserRegistry`1(TLocaliser defaultLocaliser) { }
public LocaliserRegistry`1(System.Func<, > defaultLocaliser) { }
public void Register(string localeCode, TLocaliser localiser) { }
public void Register(string localeCode, System.Func<, > localiser) { }
public TLocaliser ResolveForCulture(System.Globalization.CultureInfo culture) { }
public TLocaliser ResolveForUiCulture() { }
}
Expand Down Expand Up @@ -340,10 +342,10 @@ public class NumberToTimeSpanExtensions

public class NumberToWordsExtension
{
public string ToOrdinalWords(int number) { }
public string ToOrdinalWords(int number, Humanizer.GrammaticalGender gender) { }
public string ToWords(int number) { }
public string ToWords(int number, Humanizer.GrammaticalGender gender) { }
public string ToOrdinalWords(int number, System.Globalization.CultureInfo culture) { }
public string ToOrdinalWords(int number, Humanizer.GrammaticalGender gender, System.Globalization.CultureInfo culture) { }
public string ToWords(int number, System.Globalization.CultureInfo culture) { }
public string ToWords(int number, Humanizer.GrammaticalGender gender, System.Globalization.CultureInfo culture) { }
}

public class On
Expand Down
21 changes: 20 additions & 1 deletion src/Humanizer.Tests/NumberToWordsTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Xunit;
using System.Globalization;
using Xunit;
using Xunit.Extensions;

namespace Humanizer.Tests
Expand Down Expand Up @@ -92,5 +93,23 @@ public void ToOrdinalWords(int number, string words)
{
Assert.Equal(words, number.ToOrdinalWords());
}

[Theory]
[InlineData(11, "en-US", "eleven")]
[InlineData(22, "ar", "اثنان و عشرون")]
[InlineData(40, "ru", "сорок")]
public void ToWords_CanSpecifyCultureExplicitly(int number, string culture, string expected)
{
Assert.Equal(expected, number.ToWords(new CultureInfo(culture)));
}

[Theory]
[InlineData(1021, "en-US", "thousand and twenty-first")]
[InlineData(21, "ar", "الحادي و العشرون")]
[InlineData(1112, "ru", "одна тысяча сто двенадцатый")]
public void ToOrdinalWords_CanSpecifyCultureExplicitly(int number, string culture, string expected)
{
Assert.Equal(expected, number.ToOrdinalWords(new CultureInfo(culture)));
}
}
}
8 changes: 3 additions & 5 deletions src/Humanizer/Configuration/Configurator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,10 @@ internal static IFormatter GetFormatter(CultureInfo culture)
/// <summary>
/// The converter to be used
/// </summary>
internal static INumberToWordsConverter NumberToWordsConverter
/// <param name="culture">The culture to retrieve number to words converter for. Null means that current thread's UI culture should be used.</param>
internal static INumberToWordsConverter GetNumberToWordsConverter(CultureInfo culture)
{
get
{
return NumberToWordsConverters.ResolveForUiCulture();
}
return NumberToWordsConverters.ResolveForCulture(culture);
}

/// <summary>
Expand Down
50 changes: 36 additions & 14 deletions src/Humanizer/Configuration/LocaliserRegistry.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Globalization;

namespace Humanizer.Configuration
Expand All @@ -7,16 +8,26 @@ namespace Humanizer.Configuration
/// A registry of localised system components with their associated locales
/// </summary>
/// <typeparam name="TLocaliser"></typeparam>
public class LocaliserRegistry<TLocaliser>
public class LocaliserRegistry<TLocaliser>
where TLocaliser : class
{
private readonly IDictionary<string, TLocaliser> _localisers = new Dictionary<string, TLocaliser>();
private readonly TLocaliser _defaultLocaliser;
private readonly IDictionary<string, Func<CultureInfo, TLocaliser>> _localisers = new Dictionary<string, Func<CultureInfo, TLocaliser>>();
private readonly Func<CultureInfo, TLocaliser> _defaultLocaliser;

/// <summary>
/// Creates a localiser registry with the default localiser set to the provided value
/// </summary>
/// <param name="defaultLocaliser"></param>
public LocaliserRegistry(TLocaliser defaultLocaliser)
{
_defaultLocaliser = (culture) => defaultLocaliser;
}

/// <summary>
/// Creates a localiser registry with the default localiser factory set to the provided value
/// </summary>
/// <param name="defaultLocaliser"></param>
public LocaliserRegistry(Func<CultureInfo, TLocaliser> defaultLocaliser)
{
_defaultLocaliser = defaultLocaliser;
}
Expand All @@ -35,9 +46,28 @@ public TLocaliser ResolveForUiCulture()
/// <param name="culture">The culture to retrieve localiser for. If not specified, current thread's UI culture is used.</param>
public TLocaliser ResolveForCulture(CultureInfo culture)
{
culture = culture ?? CultureInfo.CurrentUICulture;
return FindLocaliser(culture ?? CultureInfo.CurrentUICulture)(culture);
}

TLocaliser localiser;
/// <summary>
/// Registers the localiser for the culture provided
/// </summary>
public void Register(string localeCode, TLocaliser localiser)
{
_localisers[localeCode] = (culture) => localiser;
}

/// <summary>
/// Registers the localiser factory for the culture provided
/// </summary>
public void Register(string localeCode, Func<CultureInfo, TLocaliser> localiser)
{
_localisers[localeCode] = localiser;
}

private Func<CultureInfo, TLocaliser> FindLocaliser(CultureInfo culture)
{
Func<CultureInfo, TLocaliser> localiser;

if (_localisers.TryGetValue(culture.Name, out localiser))
return localiser;
Expand All @@ -47,13 +77,5 @@ public TLocaliser ResolveForCulture(CultureInfo culture)

return _defaultLocaliser;
}

/// <summary>
/// Registers the localiser for the culture provided
/// </summary>
public void Register(string localeCode, TLocaliser localiser)
{
_localisers[localeCode] = localiser;
}
}
}
8 changes: 4 additions & 4 deletions src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ namespace Humanizer.Configuration
{
internal class NumberToWordsConverterRegistry : LocaliserRegistry<INumberToWordsConverter>
{
public NumberToWordsConverterRegistry() : base(new DefaultNumberToWordsConverter())
public NumberToWordsConverterRegistry() : base((culture) => new DefaultNumberToWordsConverter(culture))
{
Register("en", new EnglishNumberToWordsConverter());
Register("ar", new ArabicNumberToWordsConverter());
Register("fa", new FarsiNumberToWordsConverter());
Register("es", new SpanishNumberToWordsConverter());
Register("pl", new PolishNumberToWordsConverter());
Register("pl", (culture) => new PolishNumberToWordsConverter(culture));
Register("pt-BR", new BrazilianPortugueseNumberToWordsConverter());
Register("ru", new RussianNumberToWordsConverter());
Register("fr", new FrenchNumberToWordsConverter());
Register("nl", new DutchNumberToWordsConverter());
Register("he", new HebrewNumberToWordsConverter());
Register("sl", new SlovenianNumberToWordsConverter());
Register("he", (culture) => new HebrewNumberToWordsConverter(culture));
Register("sl", (culture) => new SlovenianNumberToWordsConverter(culture));
Register("de", new GermanNumberToWordsConverter());
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Humanizer/Humanizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
<Compile Include="GrammaticalGender.cs" />
<Compile Include="Localisation\GrammaticalNumber\RussianGrammaticalNumber.cs" />
<Compile Include="Localisation\GrammaticalNumber\RussianGrammaticalNumberDetector.cs" />
<Compile Include="Localisation\NumberToWords\GenderedNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\GenderlessNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\SlovenianNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\DutchNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\DefaultNumberToWordsConverter.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Humanizer.Localisation.NumberToWords
{
internal class ArabicNumberToWordsConverter : DefaultNumberToWordsConverter
internal class ArabicNumberToWordsConverter : GenderlessNumberToWordsConverter
{
private static readonly string[] Groups = { "مئة", "ألف", "مليون", "مليار", "تريليون", "كوادريليون", "كوينتليون", "سكستيليون" };
private static readonly string[] AppendedGroups = { "", "ألفاً", "مليوناً", "ملياراً", "تريليوناً", "كوادريليوناً", "كوينتليوناً", "سكستيليوناً" };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Humanizer.Localisation.NumberToWords
{
internal class BrazilianPortugueseNumberToWordsConverter : DefaultNumberToWordsConverter
internal class BrazilianPortugueseNumberToWordsConverter : GenderedNumberToWordsConverter
{
private static readonly string[] PortugueseUnitsMap = { "zero", "um", "dois", "três", "quatro", "cinco", "seis", "sete", "oito", "nove", "dez", "onze", "doze", "treze", "quatorze", "quinze", "dezesseis", "dezessete", "dezoito", "dezenove" };
private static readonly string[] PortugueseTensMap = { "zero", "dez", "vinte", "trinta", "quarenta", "cinquenta", "sessenta", "setenta", "oitenta", "noventa" };
Expand Down Expand Up @@ -81,11 +81,6 @@ public override string Convert(int number, GrammaticalGender gender)
return string.Join(" ", parts.ToArray());
}

public override string Convert(int number)
{
return Convert(number, GrammaticalGender.Masculine);
}

public override string ConvertToOrdinal(int number, GrammaticalGender gender)
{
// N/A in Portuguese ordinal
Expand Down Expand Up @@ -139,12 +134,7 @@ public override string ConvertToOrdinal(int number, GrammaticalGender gender)
return string.Join(" ", parts.ToArray());
}

public override string ConvertToOrdinal(int number)
{
return ConvertToOrdinal(number, GrammaticalGender.Masculine);
}

private string ApplyGender(string toWords, GrammaticalGender gender)
private static string ApplyGender(string toWords, GrammaticalGender gender)
{
if (gender != GrammaticalGender.Feminine)
return toWords;
Expand All @@ -161,11 +151,11 @@ private string ApplyGender(string toWords, GrammaticalGender gender)
return toWords;
}

private string ApplyOrdinalGender(string toWords, GrammaticalGender gender)
private static string ApplyOrdinalGender(string toWords, GrammaticalGender gender)
{
if (gender == GrammaticalGender.Feminine)
return toWords.TrimEnd('o') + 'a';

return toWords;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,51 +1,38 @@
namespace Humanizer.Localisation.NumberToWords
using System.Globalization;

namespace Humanizer.Localisation.NumberToWords
{
internal class DefaultNumberToWordsConverter : INumberToWordsConverter
internal class DefaultNumberToWordsConverter : GenderlessNumberToWordsConverter
{
/// <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>
public virtual string Convert(int number, GrammaticalGender gender)
{
return Convert(number);
}
private readonly CultureInfo _culture;

/// <summary>
/// 3501.ToWords() -> "three thousand five hundred and one"
/// Constructor.
/// </summary>
/// <param name="number">Number to be turned to words</param>
/// <returns></returns>
public virtual string Convert(int number)
/// <param name="culture">Culture to use.</param>
public DefaultNumberToWordsConverter(CultureInfo culture)
{
return number.ToString();
_culture = culture;
}

/// <summary>
/// for Brazilian Portuguese
/// 1.ToOrdinalWords(GrammaticalGender.Masculine) -> "primeiro"
/// 1.ToOrdinalWords(GrammaticalGender.Feminine) -> "primeira"
/// 3501.ToWords() -> "three thousand five hundred and one"
/// </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>
public virtual string ConvertToOrdinal(int number, GrammaticalGender gender)
public override string Convert(int number)
{
return ConvertToOrdinal(number);
return number.ToString(_culture);
}

/// <summary>
/// 1.ToOrdinalWords() -> "first"
/// </summary>
/// <param name="number">Number to be turned to ordinal words</param>
/// <returns></returns>
public virtual string ConvertToOrdinal(int number)
public override string ConvertToOrdinal(int number)
{
return number.ToString();
return number.ToString(_culture);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Humanizer.Localisation.NumberToWords
/// Used the rules as stated here.
/// http://www.beterspellen.nl/website/?pag=110
/// </summary>
internal class DutchNumberToWordsConverter : DefaultNumberToWordsConverter
internal class DutchNumberToWordsConverter : GenderlessNumberToWordsConverter
{
private static readonly string[] UnitsMap = { "nul", "een", "twee", "drie", "vier", "vijf", "zes", "zeven", "acht", "negen", "tien", "elf", "twaalf", "dertien", "veertien", "vijftien", "zestien", "zeventien", "achttien", "negentien" };
private static readonly string[] TensMap = { "nul", "tien", "twintig", "dertig", "veertig", "vijftig", "zestig", "zeventig", "tachtig", "negentig" };
Expand All @@ -24,12 +24,12 @@ class Fact
}

private static readonly Fact[] Hunderds =
{
new Fact {Value = 1000000000, Name = "miljard", Prefix = " ", Postfix = " ", DisplayOneUnit = true},
new Fact {Value = 1000000, Name = "miljoen", Prefix = " ", Postfix = " ", DisplayOneUnit = true},
new Fact {Value = 1000, Name = "duizend", Prefix = "", Postfix = " ", DisplayOneUnit = false},
new Fact {Value = 100, Name = "honderd", Prefix = "", Postfix = "", DisplayOneUnit = false}
};
{
new Fact {Value = 1000000000, Name = "miljard", Prefix = " ", Postfix = " ", DisplayOneUnit = true},
new Fact {Value = 1000000, Name = "miljoen", Prefix = " ", Postfix = " ", DisplayOneUnit = true},
new Fact {Value = 1000, Name = "duizend", Prefix = "", Postfix = " ", DisplayOneUnit = false},
new Fact {Value = 100, Name = "honderd", Prefix = "", Postfix = "", DisplayOneUnit = false}
};

public override string Convert(int number)
{
Expand Down Expand Up @@ -88,7 +88,7 @@ public override string Convert(int number)
};

private static readonly char[] EndingCharForSte = {'t', 'g', 'd'};

public override string ConvertToOrdinal(int number)
{
var word = Convert(number);
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 : DefaultNumberToWordsConverter
internal class EnglishNumberToWordsConverter : GenderlessNumberToWordsConverter
{
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 Down
Loading

0 comments on commit 08e76e9

Please sign in to comment.