newsletter

Humanizer in .NET: Turn Strings, Dates, and Numbers Into Human-Friendly Text

4 min read

Software stores data the way machines like it. Users want to read it the way humans like it.

So you write this boilerplate code every day.

You manually split a PascalCase enum into words. You turn 2 into "2nd" with a few if statements.

You format a timestamp as "3 hours ago". You pluralize "item" only when the count is not 1.

It is a few lines here, a helper method there - and it piles up across a codebase.

Humanizer is a small, free .NET library that does all of it for you.

It turns developer-shaped data into human-friendly text with a single method call.

In this post, we will explore:

  • How to Humanize and transform strings
  • How to Humanize enums
  • How to Humanize dates and times
  • How to Turn numbers into words
  • How to Work with culture
  • How to Pluralize and quantify
  • How to Format byte sizes and large numbers
  • Bonus: Roman numerals, collections, and fluent dates

Let's dive in.

Copied

How to Humanize and Transform Strings

First, add Humanizer to your project by installing the following NuGet package:

bash
dotnet add package Humanizer

The core of Humanizer is Humanize(), which turns a developer string into a readable sentence.

It splits strings in PascalCase, camelCase, snake_case, kebab-case, all in lower or UPPER case, into words and applies sentence casing:

csharp
"PascalCaseInputStringIsTurnedIntoSentence".Humanize(); // => "Pascal case input string is turned into sentence" "Underscored_input_string_is_turned_into_sentence".Humanize(); // => "Underscored input string is turned into sentence"

Transform changes the casing without splitting words. It takes one or more strategies:

csharp
"Sentence casing".Transform(To.TitleCase); // => "Sentence Casing" "Sentence casing".Transform(To.LowerCase); // => "sentence casing" "Sentence casing".Transform(To.SentenceCase); // => "Sentence casing"

Dehumanize is the reverse of Humanize - it turns a sentence back into a PascalCase identifier:

csharp
"Pascal case input string is turned into sentence".Dehumanize(); // => "PascalCaseInputStringIsTurnedIntoSentence"

And Truncate shortens text to a length, adding an ellipsis:

csharp
"Long text to truncate".Truncate(10); // => "Long text…"

These four cover most of the string formatting you write by hand: generating labels, display names, and identifiers from code.

Copied

How to Humanize Enums

Enum names are in PascalCase, and your UI needs readable labels.

Normally, you map each enum member to a label by hand:

csharp
var label = status switch { ShipmentStatus.OutForDelivery => "Out for delivery", ShipmentStatus.InTransit => "In transit", // ... a line for every member };

Calling Humanize() on the enum value does the same thing for free. It splits the name into words, so the switch disappears:

csharp
public enum ShipmentStatus { Pending, InTransit, OutForDelivery, Delivered } ShipmentStatus.OutForDelivery.Humanize(); // => "Out for delivery" ShipmentStatus.InTransit.Humanize(); // => "In transit"

When you need a label that does not match the member name, add a [Description] attribute and Humanizer uses it:

csharp
public enum ShipmentStatus { [Description("Awaiting pickup")] Pending, // ... } ShipmentStatus.Pending.Humanize(); // => "Awaiting pickup"

You can also go the other way, turning a display string back into the enum value with DehumanizeTo:

csharp
"Out for delivery".DehumanizeTo<ShipmentStatus>(); // => ShipmentStatus.OutForDelivery

This alone removes a surprising amount of mapping code from the boundary between your domain and your UI.

Copied

How to Humanize Dates and Times

"2 hours ago" reads better than a raw timestamp. Humanizer produces it from a DateTime:

csharp
DateTime.UtcNow.AddHours(-2).Humanize(); // => "2 hours ago" DateTimeOffset.UtcNow.AddHours(1).Humanize(); // => "an hour from now"

It compares the date to now and describes the difference in plain language - the relative timestamps you see on every social feed and dashboard.

TimeSpan works the same way, and a precision argument controls how many units it shows:

csharp
TimeSpan.FromDays(1).Humanize(); // => "1 day" TimeSpan.FromDays(16).Humanize(precision: 2); // => "2 weeks, 2 days"

So a shipment's estimated delivery window or a job's run time becomes readable without date math on your end.

Copied

How to Turn Numbers Into Words

Sometimes a number needs to be spelled out - on an invoice, a cheque, or a generated document.

ToWords does exactly that:

csharp
3501.ToWords(); // => "three thousand five hundred and one"

For positions rather than amounts, Ordinalize adds the suffix and ToOrdinalWords spells it out:

csharp
1.Ordinalize(); // => "1st" 5.Ordinalize(); // => "5th" 21.Ordinalize(); // => "21st" 1.ToOrdinalWords(); // => "first" 2.ToOrdinalWords(); // => "second"

Getting "1st", "2nd", "3rd", and "21st" right by hand is fiddly - the rules have exceptions. Humanizer knows them.

Copied

How to Work with Culture

Humanizer is culture-aware. It ships with localizations for dozens of languages, so date humanization, ToWords, and number formatting follow the current culture.

When you need a specific culture, pass a CultureInfo explicitly:

csharp
using System.Globalization; var culture = CultureInfo.GetCultureInfo("es"); DateTime.UtcNow.AddHours(-2).Humanize(culture: culture); // => "hace 2 horas" 3501.ToWords(culture: culture); // => "tres mil quinientos uno"

If you only need neutral English resources, you can install the smaller Humanizer.Core package. For other languages, install the matching Humanizer language package.

Copied

How to Pluralize and Quantify

Grammar around counts is full of edge cases: "1 item" but "2 items"; "1 person" but "2 people".

Pluralize and Singularize handle the irregular forms English is famous for:

csharp
"Man".Pluralize(); // => "Men" "Men".Singularize(); // => "Man"

Without ToQuantity, you end up writing the familiar ternary by hand:

csharp
var text = $"{count} {(count == 1 ? "case" : "cases")}";

ToQuantity is the one you will reach for most. Give it a number, and it both pluralizes the word and prefixes the count:

csharp
"case".ToQuantity(0); // => "0 cases" "case".ToQuantity(1); // => "1 case" "case".ToQuantity(5); // => "5 cases"

Ask for the number as words, and it spells out the count too:

csharp
"case".ToQuantity(5, ShowQuantityAs.Words); // => "five cases"

This is the clean replacement for the count == 1 ? "item" : "items" ternaries scattered across your views.

Copied

How to Format Byte Sizes and Large Numbers

File sizes and big counts have their own human formatting, and Humanizer covers both.

Its byte-size helpers turn a raw number into the right unit:

csharp
(10).Kilobytes().Humanize(); // => "10 KB" (0.5).Kilobytes().Humanize(); // => "512 B" (1024).Kilobytes().Humanize(); // => "1 MB"

For large counts, ToMetric produces the compact "k / M / B" form you see on dashboards and social buttons:

csharp
1230d.ToMetric(); // => "1.23k" "1.23k".FromMetric(); // => 1230

Display a download size or a follower count without writing your own unit-scaling logic.

Copied

Bonus: Roman Numerals, Collections, and Fluent Dates

Roman numerals convert both ways:

csharp
2024.ToRoman(); // => "MMXXIV" "XIV".FromRoman(); // => 14

A collection of strings becomes a readable list - the items separated by commas, with the last joined by "and":

csharp
var carriers = new[] { "FedEx", "UPS", "DHL" }; carriers.Humanize(); // => "FedEx, UPS and DHL"

And the fluent date API builds TimeSpan and DateTime values that read like English:

csharp
var dueDate = 2.Days().FromNow(); // the DateTime two days from now var window = 2.Days() + 3.Hours(); // a TimeSpan of 2 days and 3 hours

None of these are things you reach for daily, but when you need them, each one saves you a helper class.

Copied

Summary

Humanizer is a small dependency that removes an entire category of code you would otherwise write and maintain by hand.

Let's recap the key takeaways:

  • Readable text from code shapes. Humanize, Transform, and Dehumanize convert PascalCase strings and enum members into display labels and back.
  • Human-friendly dates and numbers. DateTime.Humanize() gives you "2 hours ago", TimeSpan.Humanize() reads timespans in plain words, and ToWords and Ordinalize spell numbers out.
  • Correct grammar around counts. Pluralize, Singularize, and ToQuantity handle irregular plurals and the "1 item vs 2 items" problem for you.
  • Sizes and big numbers. The byte-size helpers and ToMetric format file sizes and large counts the way users expect.

None of this is hard to write yourself (because of edge cases).

The point is that you should not have to - it is a solved problem, the rules have edge cases, and Humanizer has them covered.

Reach for it the next time you find yourself formatting data for a human to read.

Hope you find this newsletter useful. See you next time.

Whenever you're ready, here's how I can help you:

The .NET Senior Playbook is built to:

  • Fast-track you from junior or mid-level to senior
  • Keep you growing as a senior
  • Help you beat any .NET interview

Covers everything: C#, ASP.NET Core, EF Core, system design — answer each question first, reveal the solution, and a test after every chapter proves it stuck. Finish, and you earn a verifiable certificate for your LinkedIn.

The .NET Senior Playbook
Join 500+ developers

Not sure where you stand? Take the free .NET interview test:

  • Find out your real level — Junior to Senior+
  • A realistic mock .NET interview — across 13 areas of C#, .NET, ASP.NET Core and System Design

No credit card required. When you finish, you get a personalized report: your level, your strongest and weakest areas, and where to focus next — the perfect way to benchmark yourself before diving into the Playbook.

Start the free test

Enjoyed this article? Share it with your network

Improve Your .NET and Architecture Skills

Join my community of 27,000+ developers and architects.

Each week you will get 1 practical tip with best practices and real-world examples.

Learn how to craft better software with source code available for my newsletter.

Join 27,000+ developers already reading
No spam. Unsubscribe any time.