Scott Hanselman

NuGet Package of the Week: Humanizer makes .NET data types more human

April 9, '14 Comments [38] Posted in NuGetPOW | Open Source
Sponsored By

The .NET BCL (Base Class Library) moves too slow, IMHO. That's why NuGet and NuGet packages are so nice. It's a joy to find a "rightly sized" library like NodaTime, for example. It's a better date and time API for .NET. Microsoft should just use it, it's lovely.

Another nicely-sized Open Source library, and the focus for today's blog post, is Humanizer from http://humanizr.net. They say "Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities" and it does just that.

Install-Package Humanizer

It's a lovely joy of a library and you should check it out. Just make yourself a console app now and install-package Humanizer, and explore.

Note: Be sure to check out all the NuGet packages of the week! There's more!

The word "Humanize" evokes, for me, a feeling of making something simpler, more accessible, more useful, more fluent, more flexible.

Here's some great examples of problems that Humanizer solves.

First, Humanzer has a LOT of extension methods to the string type. If you've got a funky string, it will turn it into as close to a normally-cased sentence as possible. This is even more useful if you're using BDD frameworks like BDDfy.

"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"

// acronyms are left intact
"HTML".Humanize() => "HTML"

"Can_return_title_Case".Humanize(LetterCasing.Title) => "Can Return Title Case"

"CanReturnLowerCase".Humanize(LetterCasing.LowerCase) => "can return lower case"

There's dozens of great string methods, but it's the DateTime extension methods that really shine. These will feel familiar to Rails developers and Twitter users.

DateTime.UtcNow.AddHours(-30).Humanize() => "yesterday"
DateTime.UtcNow.AddHours(-2).Humanize() => "2 hours ago"

DateTime.UtcNow.AddHours(30).Humanize() => "tomorrow"
DateTime.UtcNow.AddHours(2).Humanize() => "2 hours from now"

TimeSpan.FromMilliseconds(1299630020).Humanize() => "2 weeks"
TimeSpan.FromMilliseconds(1299630020).Humanize(3) => "2 weeks, 1 day, 1 hour"

Pluralization libraries are always fun, as I've blogged before and Humanizer doesn't disappoint. You can pluralize and singularlize strings, but the "To Quantity" methods are fantastic, doing exactly what you'd expect, even if you do something odd:

"men".ToQuantity(2) => "2 men"
"process".ToQuantity(2) => "2 processes"
"process".ToQuantity(1) => "1 process"
"processes".ToQuantity(1) => "1 process"
"case".ToQuantity(0) => "0 cases"
"case".ToQuantity(1) => "1 case"

Even as words!

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

The Number to Words family of methods go even further:

3501.ToWords() => "three thousand five hundred and one"
121.ToOrdinalWords() => "hundred and twenty first"
8.ToRoman() => "VIII"

We've all written a truncate method to take a long string and add "..." at the end.

"Long text to truncate".Truncate(6, Truncator.FixedNumberOfCharacters) => "Long t…"
"Long text to truncate".Truncate(6, "---", Truncator.FixedNumberOfCharacters) => "Lon---"

I'm just scratching the surface of Humanizer, going through it's Getting Started. There's also samples where you can plug it into your own frameworks or deep within ASP.NET MVC and humanize enums, type names, and properties as a way to keep your code DRY.

Humanizer is even starting to support localization to non-English languages, and helping with the localization of Humanizer is a GREAT OPPORTUNITY if you're looking to get into Open Source as well as learning Git and GitHub Flow.

I like open source libraries like this that look like the code we've all written before...except tested and complete. ;) We've all written utilities like this, except as one-of functions. I look forward to NOT writing methods like this in the future, and instead using libraries like Humanizer. I fully plan to use this library in my next project (a startup I'm working on.)

Go take a look at Humanizer and thank the author on Twitter while you're at it!


Sponsor: Big thanks to Novalys for sponsoring the blog feed this week! Check out their security solution that combines authentication and user permissions. Secure access to features and data in most applications & architectures (.NET, Java, C++, SaaS, Web SSO, Cloud...). Try Visual Guard for FREE.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Wednesday, April 09, 2014 5:22:08 AM UTC
Awesome (Y)
Wednesday, April 09, 2014 5:39:06 AM UTC
Great post as usual Scott

Looks like you pasted the wrong thing into the NodaTime link in the first paragraph. :-)

Rasmus
Wednesday, April 09, 2014 5:50:27 AM UTC
This look like funny ;) thanks.
selçuk gural
Wednesday, April 09, 2014 6:09:44 AM UTC
Rasmus - Thanks, fixed!
Wednesday, April 09, 2014 6:57:14 AM UTC
Hey Scott, the link to NodaTime at the beginning is broken, it looks like there's some double escaping going on.
Wednesday, April 09, 2014 7:26:04 AM UTC
Lovely library, nice post...

I've needed at least a couple of these functions in the past for sure...

Quick typo near the end ... "one-off" rather than "one-of" (Sorry, nit-picking!)
Wednesday, April 09, 2014 8:00:30 AM UTC
Do you have any information if this could work with other languages than English? I couldn't find a website.
Jan D Schuitemaker
Wednesday, April 09, 2014 8:37:35 AM UTC
@Jan D Schuitemaker
the link is in the post.
https://github.com/MehdiK/Humanizer/tree/master/src/Humanizer/Properties

Your name sounds dutch, so: https://github.com/MehdiK/Humanizer/blob/master/src/Humanizer/Properties/Resources.nl.resx
Wednesday, April 09, 2014 10:15:06 AM UTC
Now thats a amazing post..Thanks for posting

Ryan
Wednesday, April 09, 2014 10:59:18 AM UTC
Excellent stuff! It kinda reminds me of MomentJs which does nice things to dates via JS.
PB
Wednesday, April 09, 2014 11:14:56 AM UTC
Awesome project. Forking it to make localisations!!
Luiz Adilson
Wednesday, April 09, 2014 11:15:48 AM UTC
HTML is not a acronym, it is an initialism.
</Pedantic>

Always a good read!
ScottM
Wednesday, April 09, 2014 11:15:53 AM UTC
Great post. Thanks!
Daniel Mackay
Wednesday, April 09, 2014 12:08:50 PM UTC
Thanks a lot for the post Scott. It's very much appreciated!

I cannot take credit for the whole thing: there are 26 contributors who've contributed a lot of goodness to the framework. Oh and while we're at it I am very open to feature requests or even better pull requests :p

There are quite a few active contributors on Humanizer who're always happy to lend a hand or guide you, should you need that. Please check out the contribution guide where I explain how you can contribute and how your code is reviewed.

@JP - thanks for the correct and prompt response.

@Jan - as JP mentioned there are already quite a lot of localisations in there; but if one is missing it's very easy to add it in. After this post I've received a PR for Swedish translation and two PRs for fixing and completing Norwegian and Finnish translations :) Here is a guide on how you can create a new translation.

P.S. I hope I am not double posting this. The first submission kinda evaporated into the thin air.
Wednesday, April 09, 2014 2:05:27 PM UTC
Awesome library!! thanks for sharing
Wednesday, April 09, 2014 2:37:59 PM UTC
This looks awesome! Thanks for sharing!
Fabio Cavalcante
Wednesday, April 09, 2014 3:33:53 PM UTC
Great! Thanks!
John
Wednesday, April 09, 2014 5:58:06 PM UTC
Fantastic. MVC's T4 templates (and others :) could benefit from this. My "ModelName" could intelligently be converted into "Model name" so that I don't have to go back and manually tweak things. I've modified T4s do that in past with large projects, but it would be great if MVC vNext came with it baked in.
Jeremy Cook
Wednesday, April 09, 2014 6:13:25 PM UTC
Grammar Nazi alert: "one-of functions", didn't you mean "one-off functions" ?
Neil N
Wednesday, April 09, 2014 7:20:56 PM UTC
Hi Scott,
I appreciate your Dev Tool List for years.
Now, the comments to your last tools post are closed. It's a pity, because I'd like to add something. Zip is a much faster format than 7z. I've done a lot of measurements and posted about it.
http://99-developer-tools.com/why-zip-is-better-than-7z/
I hope you can put this comment to your Dev Tool List.
Best Regards, Andreas
Wednesday, April 09, 2014 7:46:30 PM UTC
Huh, their Roman Numeral conversions (https://github.com/MehdiK/Humanizer/blob/master/src/Humanizer/RomanNumeralExtensions.cs) seem to come almost directly from something I wrote over three two years before it (https://stackoverflow.com/questions/271398/what-are-your-favorite-extension-methods-for-c-codeplex-com-extensionoverflow/291402#291402). Looks like it also made it into a library called Magpie.

Two things:

1) I should feel honored.
2) Stuff I wrote even a mere three years ago could benefit from some serious refactoring.
Wednesday, April 09, 2014 7:59:28 PM UTC
I use to write such extension methods for my projects. but this package is great. I will definitely use it in my next project.
Thursday, April 10, 2014 5:23:38 AM UTC
@Jesse - Thanks a lot for pointing that out. That was done completely as a contribution & I didn't search for the code online. It's very important for me to provide the attribution & have done that, including licenses, for everything I've added. I will add the attribution and will try to prevent this in the future.

Not to use as an excuse; but I receive a lot of PRs on Humanizer which is already very time consuming to review/fix/rebase/release and this is going to make it harder. So this makes me wonder, how do OSS leaders deal with this?
Thursday, April 10, 2014 6:22:50 AM UTC
Really nice post, thanks (hansel)man.
Haresh Ambaliya
Thursday, April 10, 2014 1:51:57 PM UTC
Not good enough.

Why do this:

DateTime.UtcNow.AddHours(2).Humanize()

when you can have an extension method on Integer which could do this:

2.HoursFromNow()

Just a thought.

- Mark
Thursday, April 10, 2014 2:24:17 PM UTC
@Mark Richman - I invite you to take a look at the fluent date extensions. For your convenience I am going to copy a few of them here:

There's some goodness for TimeSpan


2.Milliseconds() => TimeSpan.FromMilliseconds(2)
2.Seconds() => TimeSpan.FromSeconds(2)
2.Minutes() => TimeSpan.FromMinutes(2)
2.Hours() => TimeSpan.FromHours(2)
2.Days() => TimeSpan.FromDays(2)
2.Weeks() => TimeSpan.FromDays(14)


And also


In.TheYear(2010) // Returns the first of January of 2010
In.January // Returns 1st of January of the current year
In.FebruaryOf(2009) // Returns 1st of February of 2009

In.One.Second // DateTime.UtcNow.AddSeconds(1)
In.Three.Minutes // With corresponding From method
In.Three.Hours // With corresponding From method
In.Three.Days // With corresponding From method
In.Three.Weeks // With corresponding From method
In.Three.Months // With corresponding From method
In.Three.Years // With corresponding From method

On.January.The4th // Returns 4th of January of the current year
On.February.The(12) // Returns 12th of Feb of the current year

Thursday, April 10, 2014 3:25:55 PM UTC
Wow, That is amazing, saved lots of my work. Thanks for sharing.
Milind
Thursday, April 10, 2014 4:41:56 PM UTC
@Mehdi I appreciate the attribution; though it wasn't strictly necessary. I was just a bit surprised to find it. Pleasantly, in fact. With the exception noted in my second point that my goodness I would like to refactor it now!

I've commented on the github page as per your tweet to me.

You're doing great work with Humanizer (and thank you for not calling it Humnizr or some such social-networky name!) and I appreciate your attention to all its details.
Thursday, April 10, 2014 7:31:23 PM UTC
I'm was wondering if there was going to be a DeHumanize() function or perhaps Computerize(). Taking strings and turning them into other objects. Specifically for dates it might be cool to parse specific phrases to easily express the intent and so you don't have to fight with some of the DateTime functions. In any case it is pretty nice, I imagine is usually used for displaying things to end users in UIs. Great Work!
Adam Wright
Thursday, April 10, 2014 8:38:24 PM UTC
@Jesse - glad this is sorted :) Hey - you can send me a PR with your refactored ToRoman code :) That would be awesome.

@Adam - there are a few `Dehumanize` methods; e.g. Dehumanize string and enums. Please check out the project home page for them. WRT dehumanizing date and times, that's a bit impractical as date humanize is a lossy transformation. You can see more details here.
Thursday, April 10, 2014 11:17:53 PM UTC
Is somebody working on the french localization ? I can do that.
Guillaume
Friday, April 11, 2014 5:20:12 PM UTC
I did some tests, very interesting stuff...nice work, folks! Very useful in some kind of scenarios.

ps: I'll try NodaTime as well ;)
Thiago Lemos
Friday, April 11, 2014 9:01:27 PM UTC
I can see it as one more step towards robotics and auto-intelligance
shweta kanarkar
Saturday, April 12, 2014 3:52:10 AM UTC
How do you keep track of all the libraries you find useful? Do you have a list of problem to nuget packages somewhere?
Sunday, April 13, 2014 2:55:36 PM UTC
Great post. Really helpful for T4.
Abu Ali Muhammad Sharjeel
Tuesday, April 22, 2014 11:48:16 AM UTC
A very useful library.
Thanks ...
Hitesh Pandya
Tuesday, April 22, 2014 5:22:12 PM UTC
Wow. Thanks for sharing that! Very helpful.
Vitalii
Sunday, May 04, 2014 6:40:58 AM UTC
It's amazing library, thanks for sharing..
Fayez Barbari
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.