From 736cad7eb45328489ded89f156dabee8031e5a29 Mon Sep 17 00:00:00 2001 From: Rocky Date: Wed, 7 Jan 2026 21:12:14 +1100 Subject: [PATCH 1/2] Fix TitleCase: always capitalize the first word --- src/Humanizer.Tests/TransformersTests.cs | 7 +++++++ src/Humanizer/Transformer/ToTitleCase.cs | 13 +++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Humanizer.Tests/TransformersTests.cs b/src/Humanizer.Tests/TransformersTests.cs index a93992c33..4bcb96228 100644 --- a/src/Humanizer.Tests/TransformersTests.cs +++ b/src/Humanizer.Tests/TransformersTests.cs @@ -34,4 +34,11 @@ public void TransformToSentenceCase(string input, string expectedOutput) => [InlineData("Title Case", "TITLE CASE")] public void TransformToUpperCase(string input, string expectedOutput) => Assert.Equal(expectedOutput, input.Transform(To.UpperCase)); + + [Theory] + [InlineData("a great article", "A Great Article")] + [InlineData("yet another conjunction", "Yet Another Conjunction")] + [InlineData("by this preposition", "By This Preposition")] + public void TransformToTitleCase_FirstWordExceptions(string input, string expectedOutput) => + Assert.Equal(expectedOutput, input.Transform(To.TitleCase)); } \ No newline at end of file diff --git a/src/Humanizer/Transformer/ToTitleCase.cs b/src/Humanizer/Transformer/ToTitleCase.cs index df506d9ea..6dcb48524 100644 --- a/src/Humanizer/Transformer/ToTitleCase.cs +++ b/src/Humanizer/Transformer/ToTitleCase.cs @@ -10,7 +10,7 @@ public string Transform(string input) => #if NET7_0_OR_GREATER [GeneratedRegex(WordPattern)] private static partial Regex WordRegexGenerated(); - + private static Regex WordRegex() => WordRegexGenerated(); #else private static readonly Regex WordRegexDefinition = new(WordPattern, RegexOptions.Compiled); @@ -23,13 +23,22 @@ public string Transform(string input, CultureInfo culture) var matches = WordRegex().Matches(input); var builder = new StringBuilder(input); var textInfo = culture.TextInfo; + var isFirstWord = true; + foreach (Match word in matches) { var value = word.Value; - if (AllCapitals(value) || IsArticleOrConjunctionOrPreposition(value)) + + if(AllCapitals(value)) { continue; } + if (!isFirstWord && IsArticleOrConjunctionOrPreposition(value)) + { + continue; + } + + isFirstWord = false; builder[word.Index] = textInfo.ToUpper(value[0]); Overwrite(builder, word.Index + 1, textInfo.ToLower(value[1..])); From 5dc6850e05d6833236c20dac5d29f7332b3cfbac Mon Sep 17 00:00:00 2001 From: Rocky Date: Tue, 13 Jan 2026 20:49:21 +1100 Subject: [PATCH 2/2] Updating commit to incorporate suggested copilot changes --- src/Humanizer.Tests/TransformersTests.cs | 1 + src/Humanizer/Transformer/ToTitleCase.cs | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Humanizer.Tests/TransformersTests.cs b/src/Humanizer.Tests/TransformersTests.cs index 4bcb96228..6ffe4b159 100644 --- a/src/Humanizer.Tests/TransformersTests.cs +++ b/src/Humanizer.Tests/TransformersTests.cs @@ -39,6 +39,7 @@ public void TransformToUpperCase(string input, string expectedOutput) => [InlineData("a great article", "A Great Article")] [InlineData("yet another conjunction", "Yet Another Conjunction")] [InlineData("by this preposition", "By This Preposition")] + [InlineData("NASA and the future", "NASA and the Future")] public void TransformToTitleCase_FirstWordExceptions(string input, string expectedOutput) => Assert.Equal(expectedOutput, input.Transform(To.TitleCase)); } \ No newline at end of file diff --git a/src/Humanizer/Transformer/ToTitleCase.cs b/src/Humanizer/Transformer/ToTitleCase.cs index 6dcb48524..477354600 100644 --- a/src/Humanizer/Transformer/ToTitleCase.cs +++ b/src/Humanizer/Transformer/ToTitleCase.cs @@ -28,18 +28,18 @@ public string Transform(string input, CultureInfo culture) foreach (Match word in matches) { var value = word.Value; + var currentIsFirst = isFirstWord; + isFirstWord = false; - if(AllCapitals(value)) + if (AllCapitals(value)) { continue; } - if (!isFirstWord && IsArticleOrConjunctionOrPreposition(value)) + if (!currentIsFirst && IsArticleOrConjunctionOrPreposition(value)) { continue; } - isFirstWord = false; - builder[word.Index] = textInfo.ToUpper(value[0]); Overwrite(builder, word.Index + 1, textInfo.ToLower(value[1..])); }