diff --git a/release_notes.md b/release_notes.md index 99d156139..1ef033e67 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,5 +1,6 @@ ###In Development - + - [#166](https://github.com/MehdiK/Humanizer/pull/166): Added Dutch (NL) Number to words and ordinals + [Commits](https://github.com/MehdiK/Humanizer/compare/v1.21.15...master) ###v1.21.1 - 2014-04-12 @@ -35,6 +36,7 @@ - [#143](https://github.com/MehdiK/Humanizer/pull/143): Added Russian translation for future DateTime, TimeSpan and Now - [#144](https://github.com/MehdiK/Humanizer/pull/144): Added Danish localization (strings, tests) - [#146](https://github.com/MehdiK/Humanizer/pull/146): Added Spanish translation for future DateTime, TimeSpan and Now + [Commits](https://github.com/MehdiK/Humanizer/compare/v1.18.1...v1.19.1) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index e3d9b134e..917e70a53 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -97,6 +97,8 @@ + + diff --git a/src/Humanizer.Tests/Localisation/nl/NumberToOrdinalWordsTests.cs b/src/Humanizer.Tests/Localisation/nl/NumberToOrdinalWordsTests.cs new file mode 100644 index 000000000..8a6fe00b1 --- /dev/null +++ b/src/Humanizer.Tests/Localisation/nl/NumberToOrdinalWordsTests.cs @@ -0,0 +1,67 @@ +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Localisation.nl +{ + public class NumberToOrdinalWordsTests : AmbientCulture + { + public NumberToOrdinalWordsTests() : base("nl-NL") { } + + [Theory] + [InlineData(0, "nulde")] + [InlineData(1, "eerste")] + [InlineData(2, "tweede")] + [InlineData(3, "derde")] + [InlineData(4, "vierde")] + [InlineData(5, "vijfde")] + [InlineData(6, "zesde")] + [InlineData(7, "zevende")] + [InlineData(8, "achtste")] + [InlineData(9, "negende")] + [InlineData(10, "tiende")] + [InlineData(11, "elfde")] + [InlineData(12, "twaalfde")] + [InlineData(13, "dertiende")] + [InlineData(14, "veertiende")] + [InlineData(15, "vijftiende")] + [InlineData(16, "zestiende")] + [InlineData(17, "zeventiende")] + [InlineData(18, "achttiende")] + [InlineData(19, "negentiende")] + [InlineData(20, "twintigste")] + [InlineData(21, "eenentwintigste")] + [InlineData(22, "tweeëntwintigste")] + [InlineData(30, "dertigste")] + [InlineData(40, "veertigste")] + [InlineData(50, "vijftigste")] + [InlineData(60, "zestigste")] + [InlineData(70, "zeventigste")] + [InlineData(80, "tachtigste")] + [InlineData(90, "negentigste")] + [InlineData(95, "vijfennegentigste")] + [InlineData(96, "zesennegentigste")] + [InlineData(100, "honderdste")] + [InlineData(101, "honderdeerste")] + [InlineData(106, "honderdzesde")] + [InlineData(108, "honderdachtste")] + [InlineData(112, "honderdtwaalfde")] + [InlineData(120, "honderdtwintigste")] + [InlineData(121, "honderdeenentwintigste")] + [InlineData(1000, "duizendste")] + [InlineData(1001, "duizend eerste")] + [InlineData(1005, "duizend vijfde")] + [InlineData(1008, "duizend achtste")] + [InlineData(1012, "duizend twaalfde")] + [InlineData(1021, "duizend eenentwintigste")] + [InlineData(10000, "tienduizendste")] + [InlineData(10121, "tienduizend honderdeenentwintigste")] + [InlineData(100000, "honderdduizendste")] + [InlineData(100001, "honderdduizend eerste")] + [InlineData(1000000, "een miljoenste")] + [InlineData(1000001, "een miljoen eerste")] + public void ToOrdinalWords(int number, string words) + { + Assert.Equal(words, number.ToOrdinalWords()); + } + } +} diff --git a/src/Humanizer.Tests/Localisation/nl/NumberToWordsTests.cs b/src/Humanizer.Tests/Localisation/nl/NumberToWordsTests.cs new file mode 100644 index 000000000..764b57f4e --- /dev/null +++ b/src/Humanizer.Tests/Localisation/nl/NumberToWordsTests.cs @@ -0,0 +1,57 @@ +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Localisation.nl +{ + public class NumberToWordsTests : AmbientCulture + { + public NumberToWordsTests() : base("nl-NL") { } + + [InlineData(0, "nul")] + [InlineData(1, "een")] + [InlineData(-10, "min tien")] + [InlineData(10, "tien")] + [InlineData(11, "elf")] + [InlineData(122, "honderdtweeëntwintig")] + [InlineData(3501, "drieduizend vijfhonderdeen")] + [InlineData(100, "honderd")] + [InlineData(1000, "duizend")] + [InlineData(100000, "honderdduizend")] + [InlineData(1000000, "een miljoen")] + [InlineData(10000000, "tien miljoen")] + [InlineData(100000000, "honderd miljoen")] + [InlineData(1000000000, "een miljard")] + [InlineData(111, "honderdelf")] + [InlineData(1111, "duizend honderdelf")] + [InlineData(111111, "honderdelfduizend honderdelf")] + [InlineData(1111111, "een miljoen honderdelfduizend honderdelf")] + [InlineData(11111111, "elf miljoen honderdelfduizend honderdelf")] + [InlineData(111111111, "honderdelf miljoen honderdelfduizend honderdelf")] + [InlineData(1111111111, "een miljard honderdelf miljoen honderdelfduizend honderdelf")] + [InlineData(123, "honderddrieëntwintig")] + [InlineData(124, "honderdvierentwintig")] + [InlineData(1234, "duizend tweehonderdvierendertig")] + [InlineData(12345, "twaalfduizend driehonderdvijfenveertig")] + [InlineData(123456, "honderddrieëntwintigduizend vierhonderdzesenvijftig")] + [InlineData(1234567, "een miljoen tweehonderdvierendertigduizend vijfhonderdzevenenzestig")] + [InlineData(12345678, "twaalf miljoen driehonderdvijfenveertigduizend zeshonderdachtenzeventig")] + [InlineData(123456789, "honderddrieëntwintig miljoen vierhonderdzesenvijftigduizend zevenhonderdnegenentachtig")] + [InlineData(1234567890, "een miljard tweehonderdvierendertig miljoen vijfhonderdzevenenzestigduizend achthonderdnegentig")] + [InlineData(1234567899, "een miljard tweehonderdvierendertig miljoen vijfhonderdzevenenzestigduizend achthonderdnegenennegentig")] + + [InlineData(108, "honderdacht")] + [InlineData(678, "zeshonderdachtenzeventig")] + [InlineData(2013, "tweeduizend dertien")] + [InlineData(2577, "tweeduizend vijfhonderdzevenenzeventig")] + [InlineData(17053980, "zeventien miljoen drieënvijftigduizend negenhonderdtachtig")] + + [InlineData(415618, "vierhonderdvijftienduizend zeshonderdachttien")] + [InlineData(16415618, "zestien miljoen vierhonderdvijftienduizend zeshonderdachttien")] + [InlineData(322, "driehonderdtweeëntwintig")] + [Theory] + public void ToWords(int number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + } +} diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index 885a4de1a..2747348e3 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -70,6 +70,7 @@ + diff --git a/src/Humanizer/Localisation/NumberToWords/DutchNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/DutchNumberToWordsConverter.cs new file mode 100644 index 000000000..225c49e5a --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/DutchNumberToWordsConverter.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Humanizer.Localisation.NumberToWords +{ + /// + /// Dutch spelling of numbers is not really officially regulated. + /// There are a few different rules that can be applied. + /// Used the rules as stated here. + /// http://www.beterspellen.nl/website/?pag=110 + /// + internal class DutchNumberToWordsConverter : DefaultNumberToWordsConverter + { + 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" }; + + class Fact + { + public int Value { get; set; } + public string Name { get; set; } + public string Prefix { get; set; } + public string Postfix { get; set; } + public bool DisplayOneUnit { get; set; } + } + + 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} + }; + + public override string Convert(int number) + { + if (number == 0) + return UnitsMap[0]; + + if (number < 0) + return string.Format("min {0}", Convert(-number)); + + var word = ""; + + foreach (var m in Hunderds) + { + var divided = number / m.Value; + + if (divided <= 0) continue; + + if (divided == 1 && !m.DisplayOneUnit) + { + word += m.Name; + } + else + { + word += Convert(divided) + m.Prefix + m.Name; + } + + number %= m.Value; + if (number > 0) + { + word += m.Postfix; + } + } + + if (number > 0) + { + if (number < 20) + { + word += UnitsMap[number]; + } + else + { + var tens = TensMap[number / 10]; + var unit = number % 10; + if (unit > 0) + { + var units = UnitsMap[unit]; + var trema = units.EndsWith("e"); + word += units + (trema ? "ën" : "en") + tens; + } + else + { + word += tens; + } + } + } + + return word; + } + + private static readonly Dictionary OrdinalExceptions = new Dictionary + { + {"een", "eerste"}, + {"drie", "derde"}, + {"miljoen", "miljoenste"}, + }; + + private static readonly char[] EndingCharForSte = {'t', 'g', 'd'}; + + public override string ConvertToOrdinal(int number) + { + var word = Convert(number); + + // exceptions + foreach (var kv in OrdinalExceptions.Where(kv => word.EndsWith(kv.Key))) + { + // replace word with exception + return word.Substring(0, word.Length - kv.Key.Length) + kv.Value; + } + + // achtste + // twintigste, dertigste, veertigste, ... + // honderdste, duizendste, ... + if (word.LastIndexOfAny(EndingCharForSte) == (word.Length - 1)) + { + return word + "ste"; + } + + // anything else + return word + "de"; + } + } +} \ No newline at end of file diff --git a/src/Humanizer/NumberToWordsExtension.cs b/src/Humanizer/NumberToWordsExtension.cs index 7d8fc2e44..eae615f55 100644 --- a/src/Humanizer/NumberToWordsExtension.cs +++ b/src/Humanizer/NumberToWordsExtension.cs @@ -20,7 +20,8 @@ public static class NumberToWordsExtension {"pl", () => new PolishNumberToWordsConverter()}, {"pt-BR", () => new BrazilianPortugueseNumberToWordsConverter()}, {"ru", () => new RussianNumberToWordsConverter()}, - {"fr", () => new FrenchNumberToWordsConverter()} + {"fr", () => new FrenchNumberToWordsConverter()}, + {"nl", () => new DutchNumberToWordsConverter()} }; ///