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()}
};
///