From 3e9a65a1ac79e2c0830bff7fe2945f38cd34cf16 Mon Sep 17 00:00:00 2001 From: Will Ray Date: Thu, 29 Jan 2026 10:50:13 +1100 Subject: [PATCH] Preserve ALL-CAPS pluralization for multi-letter inputs; add test --- src/Humanizer.Tests/InflectorTests.cs | 7 ++++++ src/Humanizer/Inflections/Vocabulary.cs | 33 ++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/Humanizer.Tests/InflectorTests.cs b/src/Humanizer.Tests/InflectorTests.cs index 1c6bed24e..84e299b20 100644 --- a/src/Humanizer.Tests/InflectorTests.cs +++ b/src/Humanizer.Tests/InflectorTests.cs @@ -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) => diff --git a/src/Humanizer/Inflections/Vocabulary.cs b/src/Humanizer/Inflections/Vocabulary.cs index 08f516f9e..47496a0e7 100644 --- a/src/Humanizer/Inflections/Vocabulary.cs +++ b/src/Humanizer/Inflections/Vocabulary.cs @@ -195,9 +195,36 @@ public void AddSingular(string rule, string replacement) => 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; + } + } + } + + // 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(); + } + + return char.IsUpper(word[0]) && char.IsLower(replacement[0]) + ? StringHumanizeExtensions.Concat(char.ToUpper(replacement[0]), replacement.AsSpan(1)) + : replacement; + } + /// /// If the word is the letter s, singular or plural, return the letter s singular