Skip to main content
Version: 4.0 (next)

Write numbers and ordinals in words

Orientation

Use ToWords for cardinal words, ToOrdinalWords for word ordinals, and Ordinalize when the number should remain numeric with a localized ordinal form. Fractionalize approximates a decimal as a common fraction. ToChineseFinancialCharacters emits formal Chinese integer characters. Select culture explicitly and add grammatical gender or word form only when the selected locale advertises that capability. Use ToIndianWords when Indian English output needs an explicit named-scale or crore-based vocabulary.

Example

The shared number example verifies cardinal words, locale-specific magnitude names, 64-bit ordinals, common fractions, simplified and traditional Chinese financial characters, and the established number-formatting helpers:

Program.cs
using System.Globalization;
using Humanizer;

var culture = CultureInfo.GetCultureInfo("en-US");
var estonian = CultureInfo.GetCultureInfo("et");
var albanian = CultureInfo.GetCultureInfo("sq");
var simplifiedChinese = CultureInfo.GetCultureInfo("zh-CN");
var traditionalChinese = CultureInfo.GetCultureInfo("zh-TW");
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;

AssertEqual("forty-two", 42.ToWords(culture));
AssertEqual("twenty-first", 21.ToOrdinalWords(culture));
AssertEqual("21st", 21.Ordinalize(culture));
AssertEqual("2147483651st", 2_147_483_651L.Ordinalize(culture));
AssertEqual("-9223372036854775808th", long.MinValue.Ordinalize(culture));
AssertEqual("biljard", 1_000_000_000_000_000L.ToWords(estonian));
AssertEqual("dy biliarë", 2_000_000_000_000_000L.ToWords(albanian));
AssertEqual("triljon", 1_000_000_000_000_000_000L.ToWords(estonian));
AssertNotEmpty(long.MaxValue.ToWords(estonian));
AssertNotEmpty(long.MinValue.ToWords(estonian));
AssertEqual("one arab", 1_000_000_000L.ToIndianWords());
AssertEqual(
"one hundred crore",
1_000_000_000L.ToIndianWords(IndianScaleStyle.CroreBased));
AssertEqual("1 1/4", 1.25m.Fractionalize(5, 0m));
AssertEqual("1/3", 0.34m.Fractionalize(5, 0.01m));
AssertEqual("¾", 0.75m.Fractionalize(4, 0m, useUnicode: true));
AssertEqual("0.11", 0.11m.Fractionalize(10, 0m));
AssertEqual("壹拾", 10L.ToChineseFinancialCharacters(simplifiedChinese));
AssertEqual("壹拾", 10L.ToChineseFinancialCharacters(traditionalChinese));
AssertEqual(
"负玖佰贰拾贰京叁仟叁佰柒拾贰兆零叁佰陆拾捌亿伍仟肆佰柒拾柒万伍仟捌佰零捌",
long.MinValue.ToChineseFinancialCharacters(simplifiedChinese));
AssertThrows<NotSupportedException>(
() => 10L.ToChineseFinancialCharacters(CultureInfo.GetCultureInfo("zh")));
AssertEqual("XIV", 14.ToRoman());
AssertEqual("1.5 KB", 1536.Bytes().Humanize("0.0", culture));
AssertEqual("two people", "person".ToQuantity(2, ShowQuantityAs.Words));

Console.WriteLine("forty-two; 2147483651st; 1 1/4; 壹拾; XIV; 1.5 KB");

static void AssertEqual<T>(T expected, T actual)
{
if (!EqualityComparer<T>.Default.Equals(actual, expected))
throw new InvalidOperationException($"Expected '{expected}', got '{actual}'.");
}

static void AssertNotEmpty(string actual)
{
if (string.IsNullOrWhiteSpace(actual))
throw new InvalidOperationException("Expected localized number words.");
}

static void AssertThrows<TException>(Action action)
where TException : Exception
{
try
{
action();
}
catch (TException)
{
return;
}

throw new InvalidOperationException($"Expected {typeof(TException).Name}.");
}

Choose the output

Desired outputAPI
forty-two42.ToWords(culture)
twenty-first21.ToOrdinalWords(culture)
21st21.Ordinalize(culture)
2147483651st2_147_483_651L.Ordinalize(culture)
one hundred crore1_000_000_000L.ToIndianWords(IndianScaleStyle.CroreBased)
1 1/41.25m.Fractionalize(5, 0m)
壹拾10.ToChineseFinancialCharacters(simplifiedChinese)
A locale-specific tuple wordnumber.ToTuple(culture)

ToWords supports int and long cardinal values. Word ordinals are int-based. Some overloads accept GrammaticalGender, WordForm, or addAnd; use named arguments so intent remains clear across overload changes.

Choose an Indian scale vocabulary

ToIndianWords is specific to Indian English and accepts int or long. IndianScaleStyle.NamedScales is the default when no style is supplied. IndianScaleStyle.NamedScales uses names such as arab, kharab, and padma; IndianScaleStyle.CroreBased uses common expressions such as one hundred crore and one lakh crore, then falls back to named scales rather than inventing repeated-crore phrases. This choice is per call and does not change ToWords, configured converters, or other locales.

Format 64-bit ordinals

In Humanizer 4, numeric Ordinalize overloads accept the full long range and retain the selected culture, gender, and WordForm rules. String Ordinalize remains int-bounded; parse to long first when the input can exceed that range.

Built-in ordinalizers support 64-bit values. A custom ordinalizer registered through Configurator.Ordinalizers can continue to implement IOrdinalizer, but it is limited to int values. Calling a long overload outside that range throws NotSupportedException unless the custom implementation also implements ILongOrdinalizer.

Approximate a decimal as a common fraction

Fractionalize(maxDenominator, tolerance) returns the closest reduced fraction whose denominator does not exceed maxDenominator. The tolerance is an inclusive maximum absolute error. If no candidate is close enough, the method returns the original decimal formatted with CurrentCulture.

Set useUnicode: true to use a Unicode vulgar-fraction character when an exact glyph exists, such as ¾; otherwise slash notation is retained. Fraction components use invariant digits. maxDenominator must be at least 1 and tolerance cannot be negative.

Emit Chinese financial characters

ToChineseFinancialCharacters accepts int or long, including the full signed 64-bit range. A culture in the zh-Hans hierarchy (for example zh-CN) selects simplified characters, while a culture in the zh-Hant hierarchy (for example zh-TW) selects traditional characters. Neutral zh and non-Chinese cultures throw NotSupportedException.

The result is an integer numeral only. Humanizer does not add currency names or units such as yuan, jiao, or fen; applications handling money must apply that domain policy separately.

Use the full long range

In Humanizer 4, every built-in number-word locale supports the full signed long range. Estonian follows its authored scale names through triljon; Albanian uses miliar, bilion, biliar, and trilion, with their plural forms. The executable above calls ToWords for both long.MaxValue and long.MinValue; supported built-in locales no longer use magnitude ceilings or NotImplementedException as a partial-coverage boundary. Custom converters can retain narrower ranges when their profiles do not define the required scale words.

Pitfall

Grammar features differ by locale. Do not infer gender, abbreviation, Eifeler, or ordinal-word support from cardinal support. String Ordinalize parses the input using culture and can throw for malformed or out-of-range text; prefer the numeric overload when the application already has a number.

Version notes

The basic number-word and ordinal APIs span the documented corpus, but overloads and locale grammar have changed. Fractionalize, Chinese financial numerals, 64-bit numeric ordinals, Indian scale vocabulary selection, and the Estonian and Albanian scale corrections described above are available in the Humanizer 4 NuGet package. The verified source runs against the selected package. Historical snapshots link to their own capability data and signatures.