Introduction to the open source project Humanizer

Posted by mastermike707 on Thu, 19 Mar 2020 04:44:26 +0100

Humanizer meets all your.Net needs for operations and displays of the following types, including strings, enumerations, dates, times, time spans, numbers, and quantities.It uses MIT for authorized distribution.


1. Humanized string

Humanized string extensions allow you to convert strings that were originally processed by your computer into human-friendly strings that are more readable.It is based on a BDDfy framework in which class names, method names, and properties are converted into easy-to-read sentences.

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

"Underscored_input_String_is_turned_INTO_sentence".Humanize() => "Underscored input String is turned INTO sentence"

Note that strings containing only uppercase letters and only one word are always considered initials regardless of their length.To ensure that any string will always be human, you must use conversion (see the Transform method below):

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

// any unbroken upper case string is treated as an acronym
"HUMANIZER".Transform(To.LowerCase, To.TitleCase) => "Humanizer"

You can also specify the letter case you want:

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

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

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

"CanHumanizeIntoUpperCase".Humanize(LetterCasing.AllCaps) => "CAN HUMANIZE INTO UPPER CASE"

The LetterCasing API and the way it was accepted are legacies of the V0.2 era and will not be recommended in the future.Instead, you can use the Transform method described below.


2. Inhuman Strings

Just as you can humanize a computer-friendly string into a human-friendly string, you can also humanize a human-friendly string into a computer-friendly string:

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


3. Convert String

There is a Transform method that replaces LetterCasing, ApplyCase, and Humanize overloads.The transformation method signature is as follows:

string Transform(this string input, params IStringTransformer[] transformers)

For letter case, there are also existing implementations of IStringTransformer:

"Sentence casing".Transform(To.LowerCase) => "sentence casing"
"Sentence casing".Transform(To.SentenceCase) => "Sentence casing"
"Sentence casing".Transform(To.TitleCase) => "Sentence Casing"
"Sentence casing".Transform(To.UpperCase) => "SENTENCE CASING"

LowerCase is a public static property of the To class that returns an instance of the private ToLowerCase class that implements IStringTransformer and knows how to convert strings to lowercase.

The advantage of using Transform and IStringTransformer over ApplyCase and LetterCasing is that LetterCasing is an enumeration, and you can only use content from the framework, while IStringTransformer is an interface that can be implemented in the code base at once and used with the Transform method, making it easy to extend.


4. Truncate string

You can truncate strings using the Truncate method:

"Long text to truncate".Truncate(10) => "Long text..."

By default, the'...'character is used to truncate strings.The advantage of using the'...'character instead of'...' is that it only uses one character, so it allows more text to be displayed before truncation.You can also provide your own truncated string if you want:

"Long text to truncate".Truncate(10, "---") => "Long te---"

The default truncation policy, Truncator.FixedLength, is to truncate the input string to a specific length, including the length of the truncated string.There are two other truncator strategies: one for a fixed number of (alphanumeric) characters and another for a fixed number of words.To use a specific truncator when truncating, both Truncate methods shown in the previous example have overloads that allow you to specify an ITruncator instance for truncation.The following are examples of how to use the three truncators provided:

"Long text to truncate".Truncate(10, Truncator.FixedLength) => "Long text..."
"Long text to truncate".Truncate(10, "---", Truncator.FixedLength) => "Long te---"

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

"Long text to truncate".Truncate(2, Truncator.FixedNumberOfWords) => "Long text..."
"Long text to truncate".Truncate(2, "---", Truncator.FixedNumberOfWords) => "Long text---"

Note that you can also create your own truncators by implementing the ITruncator interface.

There is also an option to truncate the string from the beginning (TruncateFrom.Left) or the end (TruncateFrom.Right).As shown in the example above, the default is set to the right.The following example shows how to truncate from the beginning of a string:

"Long text to truncate".Truncate(10, Truncator.FixedLength, TruncateFrom.Left) => "... truncate"
"Long text to truncate".Truncate(10, "---", Truncator.FixedLength, TruncateFrom.Left) => "---runcate"

"Long text to truncate".Truncate(10, Truncator.FixedNumberOfCharacters, TruncateFrom.Left) => "...o truncate"
"Long text to truncate".Truncate(16, "---", Truncator.FixedNumberOfCharacters, TruncateFrom.Left) => "---ext to truncate"

"Long text to truncate".Truncate(2, Truncator.FixedNumberOfWords, TruncateFrom.Left) => " truncate"
"Long text to truncate".Truncate(2, "---", Truncator.FixedNumberOfWords, TruncateFrom.Left) => "---to truncate"


5. Formatted string

You can format the string using the FormatWith() method:

"To be formatted -> {0}/{1}.".FormatWith(1, "A") => "To be formatted -> 1/A."

This is an extension based on String.Format, so the exact rules apply to it.If format is null, ArgumentNullException is raised.A String.FormatException exception is thrown if the number of parameters passed is small.

You can also specify a culture to explicitly use as the first parameter of the FormatWith() method:

"{0:N2}".FormatWith(new CultureInfo("ru-RU"), 6666.66) => "6 666,66"

If no culture is specified, the current culture of the current thread is used.


6. Human Enumeration

Calling ToString directly on an enumeration member often results in undesirable output for the user.The solution is usually to use the DescriptionAttribute data comment and read it at runtime for more friendly output.That's a good solution.But in general, we only need to place some spaces between the words of the enumerated members - that's the advantage of String.Humanize().For enumerations like this:

public enum EnumUnderTest
    [Description("Custom description")]

You will get:

// DescriptionAttribute is honored
EnumUnderTest.MemberWithDescriptionAttribute.Humanize() => "Custom description"

// In the absence of Description attribute string.Humanizer kicks in
EnumUnderTest.MemberWithoutDescriptionAttribute.Humanize() => "Member without description attribute"

// Of course you can still apply letter casing
EnumUnderTest.MemberWithoutDescriptionAttribute.Humanize().Transform(To.TitleCase) => "Member Without Description Attribute"

You are not limited to DescriptionAttribute as a custom description.Any attributes applied to enumerated members with a string Description attribute will be counted.This is to help platforms that lack a DescriptionAttribute and allow subclasses of DescriptionAttributes to be used.

You can even configure the name of the attibute attribute to be used as a description.

Configurator.EnumDescriptionPropertyLocator = p => p.Name == "Info"

If you need to provide a localized description, you can use the DisplayAttribute data comment instead.

public enum EnumUnderTest
    [Display(Description = "EnumUnderTest_Member", ResourceType = typeof(Project.Resources))]

You will get:

EnumUnderTest.Member.Humanize() => "content" // from Project.Resources found under "EnumUnderTest_Member" resource key

Hopefully this will help avoid confusing definitions of enumerations with unnecessary attributes!


7. Make enumerations non-human

Humanize the string so that it is a human enumeration!The API is as follows:

public static TTargetEnum DehumanizeTo<TTargetEnum>(this string input)

Usage is:

"Member without description attribute".DehumanizeTo<EnumUnderTest>() => EnumUnderTest.MemberWithoutDescriptionAttribute

Like the Humanize API, it uses the Description property.You don't need to provide the shells provided in the humanization process: it can be made clear.

When the original Enum is not known at compile time, there is also a non-generic counterpart:

public static Enum DehumanizeTo(this string input, Type targetEnum, NoMatch onNoMatch = NoMatch.ThrowsException)

It can be used like this:

"Member without description attribute".DehumanizeTo(typeof(EnumUnderTest)) => EnumUnderTest.MemberWithoutDescriptionAttribute

By default, neither method throws a NoMatchFoundException when matching the supplied input to the target enumeration.In non-generic methods, you can also ask the method to return null by setting the second optional parameter to NoMatch.ReturnsNull.


8. Human DateTime

You can humanize an instance of DateTime or DateTimeOffset and return a string that tells you when to go backward or forward in time:

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"

DateTimeOffset.UtcNow.AddHours(1).Humanize() => "an hour from now"

Humanizer supports local and UTC dates as well as dates with offsets (DateTimeOffset).You can also provide a date to compare with the input date.If null, it will use the current date as the basis for comparison.In addition, you can explicitly specify the culture you want to use.If not, use the current UI culture of the current thread.This is an API signature:

public static string Humanize(this DateTime input, bool utcDate = true, DateTime? dateToCompareAgainst = null, CultureInfo culture = null)
public static string Humanize(this DateTimeOffset input, DateTimeOffset? dateToCompareAgainst = null, CultureInfo culture = null)

There are many localized versions of this method.Here are some examples:

// In ar culture
DateTime.UtcNow.AddDays(-1).Humanize() => "أمس"
DateTime.UtcNow.AddDays(-2).Humanize() => "منذ يومين"
DateTime.UtcNow.AddDays(-3).Humanize() => "منذ 3 أيام"
DateTime.UtcNow.AddDays(-11).Humanize() => "منذ 11 يوم"

// In ru-RU culture
DateTime.UtcNow.AddMinutes(-1).Humanize() => "минуту назад"
DateTime.UtcNow.AddMinutes(-2).Humanize() => "2 минуты назад"
DateTime.UtcNow.AddMinutes(-10).Humanize() => "10 минут назад"
DateTime.UtcNow.AddMinutes(-21).Humanize() => "21 минуту назад"
DateTime.UtcNow.AddMinutes(-22).Humanize() => "22 минуты назад"
DateTime.UtcNow.AddMinutes(-40).Humanize() => "40 минут назад"

DateTime.Humanize has two strategies: the default strategy described above and the precision-based strategy.To use a precision-based strategy, you need to configure it:

Configurator.DateTimeHumanizeStrategy = new PrecisionDateTimeHumanizeStrategy(precision: .75);
Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeOffsetHumanizeStrategy(precision: .75); // configure when humanizing DateTimeOffset

The default precision is set to.75, but you can also pass in the required precision.Set the precision to 0.75:

44 seconds => 44 seconds ago/from now
45 seconds => one minute ago/from now
104 seconds => one minute ago/from now
105 seconds => two minutes ago/from now

25 days => a month ago/from now

Dates are not inhuman because humanization is a detrimental conversion and human-friendly dates are irreversible.


9. Time span of humanization

You can call Humanize on TimeSpan for a human-friendly representation:

TimeSpan.FromMilliseconds(1).Humanize() => "1 millisecond"
TimeSpan.FromMilliseconds(2).Humanize() => "2 milliseconds"
TimeSpan.FromDays(1).Humanize() => "1 day"
TimeSpan.FromDays(16).Humanize() => "2 weeks"

TimeSpan.Humanize has an optional precision parameter that allows you to specify the precision of the returned value.The default precision value is 1, which means that only the largest unit of time is returned, as you can see in TimeSpan.FromDays(16).Humanize().Here are some examples of specified precision:

TimeSpan.FromDays(1).Humanize(precision:2) => "1 day" // no difference when there is only one unit in the provided TimeSpan
TimeSpan.FromDays(16).Humanize(2) => "2 weeks, 2 days"

// the same TimeSpan value with different precision returns different results
TimeSpan.FromMilliseconds(1299630020).Humanize() => "2 weeks"
TimeSpan.FromMilliseconds(1299630020).Humanize(3) => "2 weeks, 1 day, 1 hour"
TimeSpan.FromMilliseconds(1299630020).Humanize(4) => "2 weeks, 1 day, 1 hour, 30 seconds"
TimeSpan.FromMilliseconds(1299630020).Humanize(5) => "2 weeks, 1 day, 1 hour, 30 seconds, 20 milliseconds"

By default, when precision parameters are used, empty time units do not count in the accuracy of the returned value.If you do not need this behavior, you can use the overloaded TimeSpan.Humanize method with the countEmptyUnits parameter.Leading empty time units never count.This is an example of displaying the difference between null unit counts:

TimeSpan.FromMilliseconds(3603001).Humanize(3) => "1 hour, 3 seconds, 1 millisecond"
TimeSpan.FromMilliseconds(3603001).Humanize(3, countEmptyUnits:true) => "1 hour, 3 seconds"

There are many localized versions of this approach:

// in de-DE culture
TimeSpan.FromDays(1).Humanize() => "Ein Tag"
TimeSpan.FromDays(2).Humanize() => "2 Tage"

// in sk-SK culture
TimeSpan.FromMilliseconds(1).Humanize() => "1 milisekunda"
TimeSpan.FromMilliseconds(2).Humanize() => "2 milisekundy"
TimeSpan.FromMilliseconds(5).Humanize() => "5 milisekúnd"

You can explicitly specify the culture to be used.If not, use the current UI culture of the current thread.Example:

TimeSpan.FromDays(1).Humanize(culture: "ru-RU") => "один день"

In addition, you can specify the shortest unit of time to avoid scrolling to smaller units.For example:

TimeSpan.FromMilliseconds(122500).Humanize(minUnit: TimeUnit.Second) => "2 minutes, 2 seconds"    // instead of 2 minutes, 2 seconds, 500 milliseconds
TimeSpan.FromHours(25).Humanize(minUnit: TimeUnit.Day) => "1 Day"   //instead of 1 Day, 1 Hour

In addition, you can specify a maximum time unit to avoid accumulating to the next maximum unit.For example:

TimeSpan.FromDays(7).Humanize(maxUnit: TimeUnit.Day) => "7 days"    // instead of 1 week
TimeSpan.FromMilliseconds(2000).Humanize(maxUnit: TimeUnit.Millisecond) => "2000 milliseconds"    // instead of 2 seconds

The default maxUnit is TimeUnit.Week because it provides accurate results.You can increase this value to TimeUnit.Month or TimeUnit.Year, which will give you an approximation based on 365.2425 days a year and 30.436875 days a month.Therefore, the interval between months is 30 and 31 days, 366 days every four years.

TimeSpan.FromDays(486).Humanize(maxUnit: TimeUnit.Year, precision: 7) => "1 year, 3 months, 29 days" // One day further is 1 year, 4 month
TimeSpan.FromDays(517).Humanize(maxUnit: TimeUnit.Year, precision: 7) => "1 year, 4 months, 30 days" // This month has 30 days and one day further is 1 year, 5 months

If there are multiple time units, use the','string to combine them:

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

When TimeSpan is zero, the default behavior returns "0" plus the minimum unit of time.However, if true is assigned to toWords when Humanize is called, the method returns "no time".For example:

TimeSpan.Zero.Humanize(1) => "0 milliseconds"
TimeSpan.Zero.Humanize(1, toWords: true) => "no time"
TimeSpan.Zero.Humanize(1, minUnit: Humanizer.Localisation.TimeUnit.Second) => "0 seconds"

Using the collectionSeparator parameter, you can specify your own delimiter string:

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

You can also use the current culture's set formatter to group time units.To do this, specify null as the collectionSeparator parameter:

// in en-US culture
TimeSpan.FromMilliseconds(1299630020).Humanize(3, collectionSeparator: null) => "2 weeks, 1 day, and 1 hour"

// in de-DE culture
TimeSpan.FromMilliseconds(1299630020).Humanize(3, collectionSeparator: null) => "2 Wochen, Ein Tag und Eine Stunde"

If words take precedence over numbers, you can set the toWords:true parameter to convert numbers in a human-friendly TimeSpan into words:

TimeSpan.FromMilliseconds (1299630020). Humanize (3, toWords:true) => "Two weeks, one day, one hour"


10. Human Collection

You can call Humanize on any IEnumerable to get a properly formatted string that represents objects in the collection.By default, ToString() is called on each project to get its representation, but the formatting function can be passed to Humanize.In addition, a default delimiter (and in English) is provided, but other delimiters can be passed to Humanize.

For example:

class SomeClass
    public string SomeString;
    public int SomeInt;
    public override string ToString()
        return "Specific String";

string FormatSomeClass(SomeClass sc)
    return string.Format("SomeObject #{0} - {1}", sc.SomeInt, sc.SomeString);

var collection = new List<SomeClass>
    new SomeClass { SomeInt = 1, SomeString = "One" },
    new SomeClass { SomeInt = 2, SomeString = "Two" },
    new SomeClass { SomeInt = 3, SomeString = "Three" }

collection.Humanize()                                    // "Specific String, Specific String, and Specific String"
collection.Humanize("or")                                // "Specific String, Specific String, or Specific String"
collection.Humanize(FormatSomeClass)                     // "SomeObject #1 - One, SomeObject #2 - Two, and SomeObject #3 - Three"
collection.Humanize(sc => sc.SomeInt.Ordinalize(), "or") // "1st, 2nd, or 3rd"

Trim the item and skip the NullOrWhitespace item.This results in clean comma punctuation.(If you have a custom formatter function, this function only applies to the output of the formatter.)

You can provide your own collection formatter by implementing ICollectionFormatter and registering it with Configurator.CollectionFormatters.


11. Inflector methods

There are also inflector methods:



Pluralize the input provided, taking into account irregular and non-countable words:

"Man".Pluralize() => "Men"
"string".Pluralize() => "strings"

Typically, you will call Pluralize on a single word, but if you are unsure of the singularity of the word, you can call it with the optional inputIsKnownToBeSingular parameter:

"Men".Pluralize(inputIsKnownToBeSingular: false) => "Men"
"Man".Pluralize(inputIsKnownToBeSingular: false) => "Men"
"string".Pluralize(inputIsKnownToBeSingular: false) => "strings"

Pluralize overloads with complex parameters are obsolete and have been removed in version 2.0.



Singularity singularizes the input provided, taking into account irregular and uncountable words:

"Men".Singularize() => "Man"
"strings".Singularize() => "string"

Normally, you will call singularization on a plural word, but if you are not sure about the plural form of the word, you can call the method with the optional inputIsKnownToBePlural parameter:

"Men".Singularize(inputIsKnownToBePlural: false) => "Man"
"Man".Singularize(inputIsKnownToBePlural: false) => "Man"
"strings".Singularize(inputIsKnownToBePlural: false) => "string"

Singularize overloads with complex parameters are obsolete and removed in version 2.0.


12. Add Words

Sometimes you may need to add a rule from the singular/plural vocabulary (the following example is already in the default vocabulary used by Inflector):

// Adds a word to the vocabulary which cannot easily be pluralized/singularized by RegEx.
// Will match both "salesperson" and "person".
Vocabularies.Default.AddIrregular("person", "people");

// To only match "person" and not "salesperson" you would pass false for the 'matchEnding' parameter.
Vocabularies.Default.AddIrregular("person", "people", matchEnding: false);

// Adds an uncountable word to the vocabulary.  Will be ignored when plurality is changed:

// Adds a rule to the vocabulary that does not follow trivial rules for pluralization:
Vocabularies.Default.AddPlural("bus", "buses");

// Adds a rule to the vocabulary that does not follow trivial rules for singularization
// (will match both "vertices" -> "vertex" and "indices" -> "index"):
Vocabularies.Default.AddSingular("(vert|ind)ices$", "$1ex");

To quantity

Many times, you want to call singularization and pluralization to add numbers to words.For example,'2 requests','3 men'.ToQuantity prefixes the supplied word with numbers and pluralizes or singularizes the word accordingly:

"case".ToQuantity(0) => "0 cases"
"case".ToQuantity(1) => "1 case"
"case".ToQuantity(5) => "5 cases"
"man".ToQuantity(0) => "0 men"
"man".ToQuantity(1) => "1 man"
"man".ToQuantity(2) => "2 men"

ToQuantity can determine if the input word is singular or plural and, if necessary, singular or plural:

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

You can also pass the second parameter ShowQuantityAs to ToQuantity to specify how you want to output the quantity provided.The default value is ShowQuantityAs.Numeric, which is what we see above.The other two values are ShowQuantityAs.Words and ShowQuantityAs.None.

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

There is also an overload that allows you to format numbers.You can pass on the format and culture you want to use.

"dollar".ToQuantity(2, "C0", new CultureInfo("en-US")) => "$2 dollars"
"dollar".ToQuantity(2, "C2", new CultureInfo("en-US")) => "$2.00 dollars"
"cases".ToQuantity(12000, "N0") => "12,000 cases"


Ordinalization converts a number to an ordinal string that represents the position in an ordered sequence such as 1st, 2nd, 3rd, 4th:

1.Ordinalize() => "1st"
5.Ordinalize() => "5th"

You can also call Ordinalize on a number string and get the same result:'21'. Ordinalize()=>'21'

Ordinalization also supports two forms of grammatical gender.You can pass a parameter to Ordinalize to specify which gender the number should be output in.Possible values are GrammaticalGender.Masculine, GrammmicalGender.Feminine, and GrammaticalGender.Neuter:

// for Brazilian Portuguese locale
1.Ordinalize(GrammaticalGender.Masculine) => ""
1.Ordinalize(GrammaticalGender.Feminine) => ""
1.Ordinalize(GrammaticalGender.Neuter) => ""
"2".Ordinalize(GrammaticalGender.Masculine) => ""
"2".Ordinalize(GrammaticalGender.Feminine) => ""
"2".Ordinalize(GrammaticalGender.Neuter) => ""

Obviously, this only applies to certain cultures.The results are no different for other people who pass by gender or fail at all.


Titleize converts the input word to Title case; equivalent to "some titles".Humanize (LetterCasing.Title)


Pascalize converts the entered word to UpperCamelCase, removing underscores and spaces:

"some_title for something".Pascalize() => "SomeTitleForSomething"


Camelize behaves the same as Pascalize, but the first character is lowercase:

"some_title for something".Camelize() => "someTitleForSomething"


Underscore underlines the words you enter:

"SomeTitle".Underscore() => "some_title"

Dasherize & Hyphenate

Dasherize and Hyphenate replace the underscores with underscores:

"some_title".Dasherize() => "some-title"
"some_title".Hyphenate() => "some-title"
Kebaberize hyphens the words you enter, and all words are lowercase
"SomeText".Kebaberize() => "some-text"


13. Fluent Dates

Humanizer provides a fluent API for handling DateTime and TimeSpan as follows:

TimeSpan method:

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)

There is no fluent API per month or year, as a month may have 28 to 31 days, and a year may have 365 or 366 days.

You can use these methods to replace



DateTime.Now + 2.Days() + 3.Hours() - 5.Minutes()

There are also three fluent ways to work with DateTime:

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.Two.SecondsFrom(DateTime dateTime)
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

And some extension methods:

var someDateTime = new DateTime(2011, 2, 10, 5, 25, 45, 125);

// Returns new DateTime(2008, 2, 10, 5, 25, 45, 125) changing the year to 2008

// Returns new DateTime(2011, 2, 10, 2, 25, 45, 125) changing the hour to 2:25:45.125

// Returns new DateTime(2011, 2, 10, 2, 20, 15, 125) changing the time to 2:20:15.125
someDateTime.At(2, 20, 15)

// Returns new DateTime(2011, 2, 10, 12, 0, 0) changing the time to 12:00:00.000

// Returns new DateTime(2011, 2, 10, 0, 0, 0) changing the time to 00:00:00.000

Obviously, you can also link these methods together.For example, November 13, 2010 at noon + 5 minutes ()


14. Number to Number

Humanizer provides a smooth API that generates (usually very large) numbers in a clearer way:

1.25.Billions() => 1250000000
3.Hundreds().Thousands() => 300000


15. Number to Word

Humanizer can use the ToWords extension to change numbers to words:

1.ToWords() => "one"
10.ToWords() => "ten"
11.ToWords() => "eleven"
122.ToWords() => "one hundred and twenty-two"
3501.ToWords() => "three thousand five hundred and one"

You can also pass the second parameter, GrammaticalGender, to ToWords to specify the gender of the number that should be output.Possible values are GrammaticalGender.Masculine, GrammaticalGender.Feminine, and GrammaticalGender.Neuter:

// for Russian locale
1.ToWords(GrammaticalGender.Masculine) => "один"
1.ToWords(GrammaticalGender.Feminine) => "одна"
1.ToWords(GrammaticalGender.Neuter) => "одно"
// for Arabic locale
1.ToWords(GrammaticalGender.Masculine) => "واحد"
1.ToWords(GrammaticalGender.Feminine) => "واحدة"
1.ToWords(GrammaticalGender.Neuter) => "واحد"
(-1).ToWords() => "ناقص واحد"

Obviously, this only applies to certain cultures.For others who pass on gender, the results are no different.

In addition, you can explicitly specify the culture you want to use.If not, use the current UI culture of the current thread.This is an example:

11.ToWords(new CultureInfo("en")) => "eleven"
1.ToWords(GrammaticalGender.Masculine, new CultureInfo("ru")) => "один"


16. Numbers to ordinal words

This is a mix of ToWords and Ordinalize.You can call ToOrdinalWords on the number to get the ordinal representation of the words in the number!For example:

0.ToOrdinalWords() => "zeroth"
1.ToOrdinalWords() => "first"
2.ToOrdinalWords() => "second"
8.ToOrdinalWords() => "eighth"
10.ToOrdinalWords() => "tenth"
11.ToOrdinalWords() => "eleventh"
12.ToOrdinalWords() => "twelfth"
20.ToOrdinalWords() => "twentieth"
21.ToOrdinalWords() => "twenty first"
121.ToOrdinalWords() => "hundred and twenty first"

ToOrdinalWords also supports grammatical gender.You can pass the second parameter to ToOrdinalWords to specify the gender of the output.Possible values are GrammaticalGender.Masculine, GrammmicalGender.Feminine, and GrammaticalGender.Neuter:

// for Brazilian Portuguese locale
1.ToOrdinalWords(GrammaticalGender.Masculine) => "primeiro"
1.ToOrdinalWords(GrammaticalGender.Feminine) => "primeira"
1.ToOrdinalWords(GrammaticalGender.Neuter) => "primeiro"
2.ToOrdinalWords(GrammaticalGender.Masculine) => "segundo"
2.ToOrdinalWords(GrammaticalGender.Feminine) => "segunda"
2.ToOrdinalWords(GrammaticalGender.Neuter) => "segundo"
// for Arabic locale
1.ToOrdinalWords(GrammaticalGender.Masculine) => "الأول"
1.ToOrdinalWords(GrammaticalGender.Feminine) => "الأولى"
1.ToOrdinalWords(GrammaticalGender.Neuter) => "الأول"
2.ToOrdinalWords(GrammaticalGender.Masculine) => "الثاني"
2.ToOrdinalWords(GrammaticalGender.Feminine) => "الثانية"
2.ToOrdinalWords(GrammaticalGender.Neuter) => "الثاني"

Obviously, this only applies to certain cultures.For others who pass on gender, the results are no different.

In addition, you can explicitly specify the culture you want to use.If not, use the current UI culture of the current thread.This is an example:

10.ToOrdinalWords(new CultureInfo("en-US")) => "tenth"
1.ToOrdinalWords(GrammaticalGender.Masculine, new CulureInfo("pt-BR")) => "primeiro"


17. Date Time to Ordinal Words

This is an extension of Ordinalize

// for English UK locale
new DateTime(2015, 1, 1).ToOrdinalWords() => "1st January 2015"
new DateTime(2015, 2, 12).ToOrdinalWords() => "12th February 2015"
new DateTime(2015, 3, 22).ToOrdinalWords() => "22nd March 2015"
// for English US locale
new DateTime(2015, 1, 1).ToOrdinalWords() => "January 1st, 2015"
new DateTime(2015, 2, 12).ToOrdinalWords() => "February 12th, 2015"
new DateTime(2015, 3, 22).ToOrdinalWords() => "March 22nd, 2015"

ToOrdinalWords also supports syntax case.You can pass the second parameter to ToOrdinalWords to specify the case of the output.Possible values are GrammaticalCase.Nominative, GrammmicalCase.Genitive, GrammmicalCase.Dative, GrammmicalCase.Accusative, GrammmicalCase.Instrumental, and GraammaticalGender.Preposition:

Obviously, this only applies to certain cultures.For others, passing a case has no impact on the outcome.


18. Roman Numbers

Humanizer can use the ToRoman extension to change numbers to Roman numbers.Numbers 1 to 10 can be expressed as Roman numerals as follows:

1.ToRoman() => "I"
2.ToRoman() => "II"
3.ToRoman() => "III"
4.ToRoman() => "IV"
5.ToRoman() => "V"
6.ToRoman() => "VI"
7.ToRoman() => "VII"
8.ToRoman() => "VIII"
9.ToRoman() => "IX"
10.ToRoman() => "X"

You can also use the FromRoman extension for reverse operations.

"I".FromRoman() => 1
"II".FromRoman() => 2
"III".FromRoman() => 3
"IV".FromRoman() => 4
"V".FromRoman() => 5

Note that only integers less than 4000 can be converted to Roman numerals.


19. Metric Numbers
Humanizer can use the ToMetric extension to change numbers to metric numbers.Numbers 1, 1230, and 0.1 can be represented by metric numbers as follows:
1d.ToMetric() => "1"
1230d.ToMetric() => "1.23k"
0.1d.ToMetric() => "100m"

You can also use the FromMetric extension to do the opposite.

1d.ToMetric() => "1"
1230d.ToMetric() => "1.23k"
0.1d.ToMetric() => "100m"

"1".FromMetric() => 1
"1.23k".FromMetric() => 1230
"100m".FromMetric() => 0.1


20. Byte size

Humanizer includes ports for the excellent ByteSize library.Many changes and additions have been made to ByteSize to make it easier to interact with ByteSize and more consistent with the Humanizer API.These are some examples of how to convert from numbers to byte sizes and between size and amplitude:

var fileSize = (10).Kilobytes();

fileSize.Bits      => 81920
fileSize.Bytes     => 10240
fileSize.Kilobytes => 10
fileSize.Megabytes => 0.009765625
fileSize.Gigabytes => 9.53674316e-6
fileSize.Terabytes => 9.31322575e-9

There are several ways to extend the number to a ByteSize instance:


You can also add/subtract values using the +/- operator and the add/subtract method:

var total = (10).Gigabytes() + (512).Megabytes() - (2.5).Gigabytes();

The ByteSize object contains two attributes that represent the largest measure prefix symbol and value:

var maxFileSize = (10).Kilobytes();

maxFileSize.LargestWholeNumberSymbol;  // "KB"
maxFileSize.LargestWholeNumberValue;   // 10

If you want to use a string representation, you can call ToString or Humanize interchangeably on the ByteSize instance:

7.Bits().ToString();           // 7 b
8.Bits().ToString();           // 1 B
(.5).Kilobytes().Humanize();   // 512 B
(1000).Kilobytes().ToString(); // 1000 KB
(1024).Kilobytes().Humanize(); // 1 MB
(.5).Gigabytes().Humanize();   // 512 MB
(1024).Gigabytes().ToString(); // 1 TB

You can also choose to provide the format of the expected string representation.The formatter can contain symbols for the values to be displayed: b, B, KB, MB, GB, TB.The formatter uses the built-in double.ToString method to add #.## is the default format that rounds numbers to two decimal places:

var b = (10.505).Kilobytes();

// Default number format is #.##
b.ToString("KB");         // 10.52 KB
b.Humanize("MB");         // .01 MB
b.Humanize("b");          // 86057 b

// Default symbol is the largest metric prefix value >= 1
b.ToString("#.#");        // 10.5 KB

// All valid values of double.ToString(string format) are acceptable
b.ToString("0.0000");     // 10.5050 KB
b.Humanize("000.00");     // 010.51 KB

// You can include number format and symbols
b.ToString("#.#### MB");  // .0103 MB
b.Humanize("0.00 GB");    // 0 GB
b.Humanize("#.## B");     // 10757.12 B

If you want to use the string representation of the complete word, you can call ToFullWords on the ByteSize instance:

7.Bits().ToFullWords();           // 7 bits
8.Bits().ToFullWords();           // 1 byte
(.5).Kilobytes().ToFullWords();   // 512 bytes
(1000).Kilobytes().ToFullWords(); // 1000 kilobytes
(1024).Kilobytes().ToFullWords(); // 1 megabyte
(.5).Gigabytes().ToFullWords();   // 512 megabytes
(1024).Gigabytes().ToFullWords(); // 1 terabyte

There is no Dehumanize method to convert the string representation back to the ByteSize instance.But you can do this with Parse and TryParse for ByteSize.Like other TryParse methods, ByteSize.TryParse returns a Boolean value indicating whether the parsing was successful.If the value is parsed, it is output to the provided out parameter:


ByteSize output;
ByteSize.TryParse("1.5mb", out output);

// Invalid
ByteSize.Parse("1.5 b");   // Can't have partial bits

// Valid
ByteSize.Parse("1.55 kB "); // Spaces are trimmed
ByteSize.Parse("1.55 kb");
ByteSize.Parse("1.55 MB");
ByteSize.Parse("1.55 mB");
ByteSize.Parse("1.55 mb");
ByteSize.Parse("1.55 GB");
ByteSize.Parse("1.55 gB");
ByteSize.Parse("1.55 gb");
ByteSize.Parse("1.55 TB");
ByteSize.Parse("1.55 tB");
ByteSize.Parse("1.55 tb");

Finally, if you need to calculate the rate at which a certain number of bytes are transferred, you can use the Per method of ByteSize.The Per method accepts a parameter-byte measurement interval; this is the time it takes to transfer the bytes.

The Per method returns the ByteRate class with the Humanize method.By default, the rate is in seconds (for example, MB / s).However, you can pass TimeUnit to Humanize at another interval if you want.Valid intervals are TimeUnit.Second, TimeUnit.Minute, and TimeUnit.Hour.Below are examples of each interval and the byte rate.

var size = ByteSize.FromMegabytes(10);
var measurementInterval = TimeSpan.FromSeconds(1);

var text = size.Per(measurementInterval).Humanize();
// 10 MB/s

text = size.Per(measurementInterval).Humanize(TimeUnit.Minute);
// 600 MB/min

text = size.Per(measurementInterval).Humanize(TimeUnit.Hour);
// 35.15625 GB/hour

You can specify the format for the byte portion of the human output:

// 18.49 GB/s


21. Angle to Word

Humanizer includes methods for changing number titles to words.The title can be double precision, and the result will be a string.You can choose whether to return the full representation of the title (for example, north, east, south, or west), a short representation (for example, N, E, S, W), or a Unicode arrow character (for example,,,,).

// north
// north

To retrieve a short version of the title, you can use the following call:

// S
// N

Note that the maximum deviation of the text representation is 11.25 degrees.

Most importantly, these methods have an overload that you can use to provide a CultureInfo object to determine the localization results to return.

To retrieve an arrow that represents a title, use the following method:


The arrow of the title indicates a maximum deviation of 22.5 degrees.

To retrieve titles based on short text representations (such as N, E, S, W), the following methods can be used:

// 180
// 225


22. Tupleization

Humanizer can use Tupleize to change an integer to its tuple.For example:

// single
// triple
// centuple

Numbers 1-10, 100, and 1000 are converted to named tuples (i.e., single, double, and so on).Any other number "n" will be converted to "n tuple".


Humanizer blends into your framework to simplify your life

This is just a foundation that you can use to simplify your day-to-day work.For example, in Asp.Net MVC, we keep the Display property on the ViewModel property so that HtmlHelper can generate the correct tags for us.However, like enumerations, in most cases, we only need to leave a space between words in the attribute name - so why not use "string".Humanize?

You may find an Asp.Net MVC example in the code that performs this operation (although the project has been excluded from the solution file, making the nuget package available for.Net 3.5).

This is done by using the name HumanizerMetadataProvider The custom DataAnnotationsModelMetadataProvider implementation.It's small enough to repeat here; so let's start:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
using Humanizer;

namespace YourApp
    public class HumanizerMetadataProvider : DataAnnotationsModelMetadataProvider
        protected override ModelMetadata CreateMetadata(
            IEnumerable<Attribute> attributes,
            Type containerType,
            Func<object> modelAccessor,
            Type modelType,
            string propertyName)
            var propertyAttributes = attributes.ToList();
            var modelMetadata = base.CreateMetadata(propertyAttributes, containerType, modelAccessor, modelType, propertyName);

            if (IsTransformRequired(modelMetadata, propertyAttributes))
                modelMetadata.DisplayName = modelMetadata.PropertyName.Humanize();

            return modelMetadata;

        private static bool IsTransformRequired(ModelMetadata modelMetadata, IList<Attribute> propertyAttributes)
            if (string.IsNullOrEmpty(modelMetadata.PropertyName))
                return false;

            if (propertyAttributes.OfType<DisplayNameAttribute>().Any())
                return false;

            if (propertyAttributes.OfType<DisplayAttribute>().Any())
                return false;

            return true;

This class calls the base class to extract metadata and then humanizes the attribute name as needed.It is checking to see if the property already has a DisplayName or Display property, in which case the metadata provider will only accept the property and keep it.For other attributes, it will humanize the attribute name.That's all.

Now you need to register the metadata provider using Asp.Net MVC.Make sure you use System.Web.Mvc.ModelMetadataProviders instead of System.Web.ModelBinding.ModelMetadataProviders:

ModelMetadataProviders.Current = new HumanizerMetadataProvider(); you can replace:

public class RegisterModel
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Display(Name = "Email address")]
    public string EmailAddress { get; set; }

    [Display(Name = "Confirm password")]
    public string ConfirmPassword { get; set; }


public class RegisterModel
    public string UserName { get; set; }
    public string EmailAddress { get; set; }
    public string ConfirmPassword { get; set; }


... The Metadata Humanization Tool will be responsible for the rest of the work.

Needless to mention, if you want to add a title box to the tag, you can use the Transform link method:

modelMetadata.DisplayName = modelMetadata.PropertyName.Humanize().Transform(To.TitleCase);


Known installation problems and Solutions

Due to errors in the CLI tool, the main Humanizer packages and their language packages will not be installed.As a temporary solution, use Humanizer.xproj instead before fixing the error.It contains all languages.



GitHub address:

Topics: C# Attribute github less REST