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

Handle hyphenation and large numbers ending in twelve for English ordinal words #281

Closed
wants to merge 1 commit into from
Closed
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
18 changes: 10 additions & 8 deletions src/Humanizer.Tests/NumberToWordsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -65,25 +66,26 @@ 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")]
[InlineData(60, "sixtieth")]
[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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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)
Expand Down