Skip to content

ToTitleCase inserts a space before punctuation: "hello, world" → "Hello , World", "don't stop" → "Don 'T Stop" #75

Description

@matt-edmondson

What's wrong

ToTitleCase (CaseConverter/CaseConverter.cs, around line 204) runs SplitOnCaseChange directly on the input. Unlike ToPascalCase and ToMacroCase, it doesn't first replace non-alphanumerics. SplitOnCaseChange treats letter → non-letter as a word boundary (around line 121), so it inserts a space before every punctuation mark. TextInfo.ToTitleCase then sees the punctuation as the start of a new word and capitalises the letter after it.

The README describes ToTitleCase as "Each Word Capitalized", and ordinary prose is its natural input. The existing tests use only space-separated letters, so this isn't caught.

Reproduced

[TestMethod] public void TitleComma() => Assert.AreEqual("Hello, World", "hello, world".ToTitleCase());
[TestMethod] public void TitleApostrophe() => Assert.AreEqual("Don't Stop", "don't stop".ToTitleCase());
[TestMethod] public void TitleUnderscore() => Assert.AreEqual("Foo Bar", "foo_bar".ToTitleCase());
Input Expected Actual
hello, world Hello, World Hello , World
don't stop Don't Stop Don 'T Stop
foo_bar Foo Bar Foo _Bar

Titles such as "Rock 'N' Roll", "What's New?", or "Part 1: Setup" all come out mangled.

Suggested fix

In the ToTitleCase path, split only on case transitions (lower → upper, acronym → word) and on letter ↔ digit, not on letter → punctuation. One way is a parameter or a separate helper on SplitOnCaseChange. If _ and - should act as word separators in title case, map them to spaces explicitly. Keep ToPascalCase working: it calls ToTitleCase after ReplaceNonAlphaNumericWithSpace, so it shouldn't be affected. Its tests should keep passing.

Acceptance: the three tests above pass, and the existing TitleCase and PascalCase tests are unchanged.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions