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:
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 output | API |
|---|---|
forty-two | 42.ToWords(culture) |
twenty-first | 21.ToOrdinalWords(culture) |
21st | 21.Ordinalize(culture) |
2147483651st | 2_147_483_651L.Ordinalize(culture) |
one hundred crore | 1_000_000_000L.ToIndianWords(IndianScaleStyle.CroreBased) |
1 1/4 | 1.25m.Fractionalize(5, 0m) |
壹拾 | 10.ToChineseFinancialCharacters(simplifiedChinese) |
| A locale-specific tuple word | number.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.