diff --git a/src/Humanizer.Tests/NumberToWordsTests.cs b/src/Humanizer.Tests/NumberToWordsTests.cs index 931eb18cf..d77dddf3c 100644 --- a/src/Humanizer.Tests/NumberToWordsTests.cs +++ b/src/Humanizer.Tests/NumberToWordsTests.cs @@ -13,6 +13,7 @@ public NumberToWordsTests() [InlineData(1, "one")] [InlineData(10, "ten")] [InlineData(11, "eleven")] + [InlineData(20, "twenty")] [InlineData(122, "one hundred and twenty-two")] [InlineData(3501, "three thousand five hundred and one")] [InlineData(100, "one hundred")] @@ -65,8 +66,8 @@ public void ToWords(int number, string expected) [InlineData(18, "eighteenth")] [InlineData(19, "nineteenth")] [InlineData(20, "twentieth")] - [InlineData(21, "twenty first")] - [InlineData(22, "twenty second")] + [InlineData(21, "twenty-first")] + [InlineData(22, "twenty-second")] [InlineData(30, "thirtieth")] [InlineData(40, "fortieth")] [InlineData(50, "fiftieth")] @@ -74,16 +75,17 @@ public void ToWords(int number, string expected) [InlineData(70, "seventieth")] [InlineData(80, "eightieth")] [InlineData(90, "ninetieth")] - [InlineData(95, "ninety fifth")] - [InlineData(96, "ninety sixth")] + [InlineData(95, "ninety-fifth")] + [InlineData(96, "ninety-sixth")] [InlineData(100, "hundredth")] + [InlineData(112, "hundred and twelfth")] [InlineData(120, "hundred and twentieth")] - [InlineData(121, "hundred and twenty first")] + [InlineData(121, "hundred and twenty-first")] [InlineData(1000, "thousandth")] - [InlineData(1001, "thousand first")] - [InlineData(1021, "thousand and twenty first")] + [InlineData(1001, "thousand and first")] + [InlineData(1021, "thousand and twenty-first")] [InlineData(10000, "ten thousandth")] - [InlineData(10121, "ten thousand one hundred and twenty first")] + [InlineData(10121, "ten thousand one hundred and twenty-first")] [InlineData(100000, "hundred thousandth")] [InlineData(1000000, "millionth")] public void ToOrdinalWords(int number, string words) diff --git a/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs index 8d23f6082..1887f2cf4 100644 --- a/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs @@ -21,9 +21,19 @@ internal class EnglishNumberToWordsConverter : DefaultNumberToWordsConverter }; public override string Convert(int number) + { + return Convert(number, false); + } + + public override string ConvertToOrdinal(int number) + { + return Convert(number, true); + } + + private string Convert(int number, bool isOrdinal) { if (number == 0) - return "zero"; + return GetUnitValue(0, isOrdinal); if (number < 0) return string.Format("minus {0}", Convert(-number)); @@ -60,61 +70,50 @@ public override string Convert(int number) parts.Add("and"); if (number < 20) - parts.Add(UnitsMap[number]); + parts.Add(GetUnitValue(number, isOrdinal)); else { var lastPart = TensMap[number / 10]; if ((number % 10) > 0) - lastPart += string.Format("-{0}", UnitsMap[number % 10]); + lastPart += string.Format("-{0}", GetUnitValue(number % 10, isOrdinal)); + else if (isOrdinal) + lastPart = lastPart.TrimEnd('y') + "ieth"; parts.Add(lastPart); } } + else if (isOrdinal) + parts[parts.Count - 1] += "th"; - return string.Join(" ", parts.ToArray()); - } - - public override string ConvertToOrdinal(int number) - { - string towords; - // 9 => ninth - if (ExceptionNumbersToWords(number, out towords)) - return towords; + string toWords = string.Join(" ", parts.ToArray()); - // 21 => twenty first - if (number > 20) - { - string exceptionPart; - if (ExceptionNumbersToWords(number%10, out exceptionPart)) - { - var normalPart = number - number%10; - towords = RemoveOnePrefix(Convert(normalPart)); - return towords + " " + exceptionPart; - } - } + if (isOrdinal) + toWords = RemoveOnePrefix(toWords); - return NormalNumberToWords(number); + return toWords; } - private string NormalNumberToWords(int number) + private static string GetUnitValue(int number, bool isOrdinal) { - string towords = Convert(number).Replace('-', ' '); - - towords = RemoveOnePrefix(towords); - // twenty => twentieth - if (towords.EndsWith("y")) - towords = towords.TrimEnd('y') + "ie"; - - return towords + "th"; + if (isOrdinal) + { + string exceptionString; + if (ExceptionNumbersToWords(number, out exceptionString)) + return exceptionString; + else + return UnitsMap[number] + "th"; + } + else + return UnitsMap[number]; } - private static string RemoveOnePrefix(string towords) + private static string RemoveOnePrefix(string toWords) { // one hundred => hundredth - if (towords.IndexOf("one", StringComparison.Ordinal) == 0) - towords = towords.Remove(0, 4); + if (toWords.IndexOf("one", StringComparison.Ordinal) == 0) + toWords = toWords.Remove(0, 4); - return towords; + return toWords; } private static bool ExceptionNumbersToWords(int number, out string words)