Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -633,10 +633,11 @@ You can combine `wordForm` argument with gender but passing this argument in whe

#### Pascalize

`Pascalize` converts the input words to UpperCamelCase, also removing underscores and spaces:
`Pascalize` converts the input words to UpperCamelCase, also removing underscores and spaces, with optional capability to preserve acronym casing:

```C#
"some_title for something".Pascalize() => "SomeTitleForSomething"
"some_title for some SKU".Pascalize() => "SomeTitleForSomeSKU"
"NBA-Basketball MLS-baseball".Pascalize(false) => "NbaBasketballMlsBaseball"
```


Expand All @@ -648,6 +649,14 @@ You can combine `wordForm` argument with gender but passing this argument in whe
"some_title for something".Camelize() => "someTitleForSomething"
```

#### CamelizeV2

`Camelize` camelizes acronyms correctly, it is backwards incompatible with the way current Camelize works which is why introducing a new version

```C#
"HTTP IO module".Camelize() => "httpIoModule"
```


#### Underscore

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,10 +765,11 @@ namespace Humanizer
public static class InflectorExtensions
{
public static string Camelize(this string input) { }
public static string CamelizeV2(this string input) { }
public static string Dasherize(this string underscoredWord) { }
public static string Hyphenate(this string underscoredWord) { }
public static string Kebaberize(this string input) { }
public static string Pascalize(this string input) { }
public static string Pascalize(this string input, bool preserveAcronym = true) { }
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("word")]
public static string? Pluralize(this string? word, bool inputIsKnownToBeSingular = true) { }
public static string Singularize(this string word, bool inputIsKnownToBePlural = true, bool skipSimpleWords = false) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,10 +765,11 @@ namespace Humanizer
public static class InflectorExtensions
{
public static string Camelize(this string input) { }
public static string CamelizeV2(this string input) { }
public static string Dasherize(this string underscoredWord) { }
public static string Hyphenate(this string underscoredWord) { }
public static string Kebaberize(this string input) { }
public static string Pascalize(this string input) { }
public static string Pascalize(this string input, bool preserveAcronym = true) { }
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("word")]
public static string? Pluralize(this string? word, bool inputIsKnownToBeSingular = true) { }
public static string Singularize(this string word, bool inputIsKnownToBePlural = true, bool skipSimpleWords = false) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,10 +546,11 @@ namespace Humanizer
public static class InflectorExtensions
{
public static string Camelize(this string input) { }
public static string CamelizeV2(this string input) { }
public static string Dasherize(this string underscoredWord) { }
public static string Hyphenate(this string underscoredWord) { }
public static string Kebaberize(this string input) { }
public static string Pascalize(this string input) { }
public static string Pascalize(this string input, bool preserveAcronym = true) { }
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("word")]
public static string? Pluralize(this string? word, bool inputIsKnownToBeSingular = true) { }
public static string Singularize(this string word, bool inputIsKnownToBePlural = true, bool skipSimpleWords = false) { }
Expand Down
38 changes: 37 additions & 1 deletion src/Humanizer.Tests/InflectorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,29 @@ public void Hyphenate(string input, string expectedOutput) =>
[InlineData("customer-first-name", "CustomerFirstName")]
[InlineData("_customer-first-name", "CustomerFirstName")]
[InlineData(" customer__first--name", "CustomerFirstName")]
public void Pascalize(string input, string expectedOutput) =>
[InlineData("HTTP-method IO module RESTful", "HTTPMethodIOModuleRESTful")]
[InlineData("HTTP SSL IO module RESTful", "HTTPSSLIOModuleRESTful")]
public void PascalizeWithAcronymPreserved(string input, string expectedOutput) =>
Assert.Equal(expectedOutput, input.Pascalize());


[Theory]
[InlineData("customer", "Customer")]
[InlineData("CUSTOMER", "Customer")]
[InlineData("CUStomer", "Customer")]
[InlineData("customer_name", "CustomerName")]
[InlineData("customer_first_name", "CustomerFirstName")]
[InlineData("customer_first_name goes here", "CustomerFirstNameGoesHere")]
[InlineData("customer name", "CustomerName")]
[InlineData("customer name", "CustomerName")]
[InlineData("customer-first-name", "CustomerFirstName")]
[InlineData("_customer-first-name", "CustomerFirstName")]
[InlineData(" customer__first--name", "CustomerFirstName")]
[InlineData("HTTP-method IO module RESTful ", "HttpMethodIoModuleRestful")]
[InlineData("HTTP SSL IO module RESTful ", "HttpSslIoModuleRestful")]
public void PascalizeWithoutAcronymPreserved(string input, string expectedOutput) =>
Assert.Equal(expectedOutput, input.Pascalize(false));

// Same as pascalize, except first char is lowercase
[Theory]
[InlineData("customer", "customer")]
Expand All @@ -127,6 +147,22 @@ public void Pascalize(string input, string expectedOutput) =>
public void Camelize(string input, string expectedOutput) =>
Assert.Equal(expectedOutput, input.Camelize());


// Handles acronyms uniformly, preserving the case of the first letter
[Theory]
[InlineData("customer", "customer")]
[InlineData("CUSTOMER", "customer")]
[InlineData("CUStomer", "customer")]
[InlineData("customer_name", "customerName")]
[InlineData("customer_first_name", "customerFirstName")]
[InlineData("customer_first_name goes here", "customerFirstNameGoesHere")]
[InlineData("customer name", "customerName")]
[InlineData("customer name", "customerName")]
[InlineData("HTTP-method IO module RESTful ", "httpMethodIoModuleRestful")]
[InlineData("", "")]
public void CamelizeV2(string input, string expectedOutput) =>
Assert.Equal(expectedOutput, input.CamelizeV2());

//Makes an underscored lowercase string
[Theory]
[InlineData("SomeTitle", "some_title")]
Expand Down
69 changes: 68 additions & 1 deletion src/Humanizer/InflectorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,64 @@ public static string Titleize(this string input) =>

/// <summary>
/// By default, pascalize converts strings to UpperCamelCase also removing underscores
/// For a word that are all upper case, this function by default assumes its an acronym and preserves the casing
Copy link

Copilot AI Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The XML doc comment has a grammar issue: change 'For a word that are all upper case' to 'For words that are all uppercase' or 'For a word that is all uppercase'.

Suggested change
/// For a word that are all upper case, this function by default assumes its an acronym and preserves the casing
/// For a word that is all uppercase, this function by default assumes it's an acronym and preserves the casing

Copilot uses AI. Check for mistakes.
/// of all letters in that word
/// In order to treat it as a word and not acronym, set preserveAcronym to false
/// </summary>
public static string Pascalize(this string input) =>
public static string Pascalize(this string input, bool preserveAcronym = true)
{
if (preserveAcronym)
return input.PascalizeWithAcronymPreservation();
else
return input.PascalizeWithoutAcronymPreservation();
}

private static string PascalizeWithAcronymPreservation(this string input) =>
Regex.Replace(input, @"(?:[ _-]+|^)([a-zA-Z])", match => match
.Groups[1]
.Value.ToUpper());


private static string PascalizeWithoutAcronymPreservation(this string input)
{
if (string.IsNullOrEmpty(input))
return string.Empty;

var span = input.AsSpan();
var sb = new StringBuilder(input.Length);

var wordStart = 0;
for (var index = 0; index <= span.Length; index++)
{
var isSeparator = index == span.Length || span[index] == ' ' || span[index] == '_' || span[index] == '-';

if (!isSeparator)
continue;

var wordSpan = span.Slice(wordStart, index - wordStart);

if (wordSpan.Length > 0)
{
// Otherwise, capitalize the first letter and append the rest
sb.Append(char.ToUpper(wordSpan[0]));
for (var k = 1; k < wordSpan.Length; k++)
{
sb.Append(char.ToLower(wordSpan[k]));
}
}

while (index < span.Length && (span[index] == ' ' || span[index] == '_' || span[index] == '-'))
Copy link

Copilot AI Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Repeated checks for the same separator characters could be extracted into a helper method or constant to improve maintainability and reduce duplication.

Suggested change
while (index < span.Length && (span[index] == ' ' || span[index] == '_' || span[index] == '-'))
while (index < span.Length && IsSeparator(span[index]))

Copilot uses AI. Check for mistakes.
{
index++;
}

wordStart = index;
}

return sb.ToString();
}


/// <summary>
/// Same as Pascalize except that the first character is lower case
/// </summary>
Expand All @@ -71,6 +123,21 @@ public static string Camelize(this string input)
: word;
}

/// <summary>
/// Adheres to Google camel case definition https://google.github.io/styleguide/jsguide.html#naming-camel-case-defined
/// that appropriately handles acronyms.
/// </summary>
public static string CamelizeV2(this string input)
{
var word = input.Pascalize(false);
return word.Length > 0
? StringHumanizeExtensions.Concat(
char.ToLower(word[0]),
word.AsSpan(1))
: word;

}

/// <summary>
/// Separates the input words with underscore
/// </summary>
Expand Down