Skip to content
Closed
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
7 changes: 7 additions & 0 deletions src/Humanizer.Tests/InflectorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ public void PluralizeWordsWithUnknownPlurality(string singular, string plural)
Assert.Equal(plural, singular.Pluralize(false));
}

[Fact]
public void Pluralize_Preserves_AllCaps_Suffix()
{
Assert.Equal("SINGULAR TYPE NAMES", "SINGULAR TYPE NAME".Pluralize());
Assert.Equal("SINGULAR TYPE NAMES", "singular type name".Humanize(LetterCasing.AllCaps).Pluralize());
}

[Theory]
[ClassData(typeof(PluralTestSource))]
public void Singularize(string singular, string plural) =>
Expand Down
33 changes: 30 additions & 3 deletions src/Humanizer/Inflections/Vocabulary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,36 @@
bool IsUncountable(string word) =>
uncountables.Contains(word);

static string MatchUpperCase(string word, string replacement) =>
char.IsUpper(word[0]) &&
char.IsLower(replacement[0]) ? StringHumanizeExtensions.Concat(char.ToUpper(replacement[0]), replacement.AsSpan(1)) : replacement;
static string MatchUpperCase(string word, string replacement)
{
var lettersCount = 0;
var allLettersUpper = true;
foreach (var c in word)
{
if (char.IsLetter(c))
{
lettersCount++;
if (!char.IsUpper(c))
{
allLettersUpper = false;
break;
}
}
}
Comment on lines +202 to +213

Check notice

Code scanning / CodeQL

Missed opportunity to use All Note

This foreach loop looks as if it might be testing whether every sequence element satisfies a predicate - consider using '.All(...)'.

// If the input contains multiple letters and all of them are uppercase
// (e.g. an ALL CAPS phrase or acronym of length > 1), return the replacement
// fully uppercased so the plural suffix matches the input casing.
if (lettersCount > 1 && allLettersUpper)
{
return replacement.ToUpperInvariant();
Comment on lines +218 to +220

Choose a reason for hiding this comment

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

P2 Badge Use culture-aware uppercasing for ALL CAPS

On all-caps inputs, replacement.ToUpperInvariant() ignores the current culture. In locales with special casing (e.g., tr-TR), pluralizing an all-caps word containing i will yield ISIMLER instead of İSİMLER, so the output no longer respects locale-specific casing rules. Consider using a culture-aware uppercasing method (consistent with other casing in this class) to preserve correct localization behavior.

Useful? React with 👍 / 👎.

}

return char.IsUpper(word[0]) && char.IsLower(replacement[0])
? StringHumanizeExtensions.Concat(char.ToUpper(replacement[0]), replacement.AsSpan(1))
: replacement;
}


/// <summary>
/// If the word is the letter s, singular or plural, return the letter s singular
Expand Down
Loading