---
title: Title-case a string
summary: Converts free text to title case with domain-specific abbreviation, prefix, suffix, and numeric-ordinal handling.
status: draft
owner: <owner>
last_reviewed: 2026-07-30
library: SAIF.Business.Utilities
reference_type: package
operation_id: OP-BU-01
trigger: Caller calls TitleCase.ToTitleCase or the String.ToTitleCase() extension method
---

# Title-case a string

This operation converts a free-text string to title case using a domain-specific algorithm rather than .NET's built-in proper-case conversion. It renders configured abbreviations, minor words, and mixed-case exceptions in a fixed casing, preserves already-mixed-case words, formats numeric ordinals, and applies a possessive-suffix pass — producing more accurate casing for names, addresses, and organization names than a naive proper-case conversion.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-BU-01 |
| **Trigger** | Caller calls `TitleCase.ToTitleCase(inString)` directly, or `inString.ToTitleCase()` via the extension method, which simply forwards to `TitleCase.ToTitleCase` |
| **Inputs** | `inString As String` |
| **Outputs** | Title-cased `String` |
| **Primary components** | `TitleCase.ToTitleCase`, `TitleCase.TitleCaseSingleWord`, `TitleCase.ReassembleString`, `TitleCase.ApplyExactSuffixList`, `TitleCase.HasNumberSuffix`, `TitleCase.GetNumberSuffixParts`, `TitleCaseExtensions.ToTitleCase` |
| **Entry point** | `Business\SAIF.Business.Utilities\TitleCase.vb` |

## Steps

1. **Handle empty input.** If `inString` is `Nothing` or trims to zero length, return it unchanged with no further processing.
2. **Split the string into words.** Split `inString.Trim` on any character that is not a letter or digit (`Regex.Split(inString.Trim, "[^\d\w]")`), preserving the delimiter characters for later reassembly.
3. **Classify each split word**, in order of precedence:
   - If the word already contains both an uppercase and a lowercase character, return it unchanged. ([BR-003](../business-rules.md#br-003-already-mixed-case-words-bypass-title-casing))
   - Else if the word (uppercased) matches the configured `UpperCaseWordList` (abbreviations such as "SAIF", "LLC", "MD", state codes), render it fully uppercase. ([BR-001](../business-rules.md#br-001-fixed-case-rendering-of-configured-abbreviations))
   - Else if the word (lowercased) matches the configured `LowerCaseWordList` (minor words such as "is", "in", "the", "of"), render it fully lowercase. ([BR-001](../business-rules.md#br-001-fixed-case-rendering-of-configured-abbreviations))
   - Else if the word (lowercased) matches the configured `MixedCaseWordList` (exceptions such as "PhD", "LaCombe"), render it using the configured mixed-case spelling. ([BR-001](../business-rules.md#br-001-fixed-case-rendering-of-configured-abbreviations))
   - Else if the word is a number followed by a recognized ordinal suffix (`st`, `nd`, `rd`, `th`), render the numeral with a lowercase suffix. ([BR-004](../business-rules.md#br-004-numeric-ordinal-words-bypass-word-list-casing))
   - Else, apply the default proper-case algorithm (`word.ToProperCase`).
4. **Apply mixed-case prefix handling.** If the classified word starts with a configured prefix (`Mc`, `O'`), proper-case only the portion after the prefix and keep the prefix as configured.
5. **Reassemble the string.** Recombine the classified words with the original non-alphanumeric delimiter characters from the source string, preserving any leading delimiters.
6. **Apply the exact-suffix pass.** Split the reassembled string on spaces and, for each word ending in a configured suffix (currently only the possessive `'s`), force that suffix to its configured lowercase form. ([BR-005](../business-rules.md#br-005-configured-possessive-suffixes-are-forced-to-fixed-casing))
7. **Force the leading character to uppercase.** Regardless of the outcome of the prior steps, the first character of the final output string is always uppercased before it is returned.*

## Process flow

The following diagram visualizes the word-classification precedence and reassembly used by this operation.

```mermaid
flowchart TD
	Start([Start: ToTitleCase]) --> Empty{Input null or blank?}
	Empty -->|Yes| ReturnAsIs([Return input unchanged])
	Empty -->|No| Split[Split input into words<br>on non-alphanumeric delimiters]
	Split --> Classify{Classify each word}
	Classify -->|Already mixed case| Unchanged[Return word unchanged]
	Classify -->|Uppercase word list| U[Render fully uppercase]
	Classify -->|Lowercase word list| L[Render fully lowercase]
	Classify -->|Mixed-case word list| M[Render per configured spelling]
	Classify -->|Numeric ordinal| N[Render numeral with lowercase suffix]
	Classify -->|No match| P[Apply default proper-case algorithm]
	Unchanged --> Prefix{Starts with configured prefix?}
	U --> Prefix
	L --> Prefix
	M --> Prefix
	N --> Prefix
	P --> Prefix
	Prefix -->|Yes| PrefixApply[Proper-case only the text after the prefix]
	Prefix -->|No| Reassemble[Reassemble string with original delimiters]
	PrefixApply --> Reassemble
	Reassemble --> Suffix[Force configured possessive<br>suffix to lowercase]
	Suffix --> Upper[Force leading character to uppercase]
	Upper --> End([Return title-cased string])
```

## Failure and edge-case behavior

- Empty or `Nothing` input is returned unchanged without throwing.
- Words that are already a mix of uppercase and lowercase characters are never re-cased, even if they also happen to match a configured word list. ([BR-003](../business-rules.md#br-003-already-mixed-case-words-bypass-title-casing))
- The final output's leading character is always uppercased, even if word classification would otherwise have rendered it lowercase (e.g., a minor word at the start of the string).*
- The exact-suffix pass operates on the string split by single spaces; the delimiter-preservation from step 2 could still leave non-space delimiters embedded in a "word" as split for this pass, which is not exercised by the possessive-suffix case currently configured.*

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

## Sources
- `Business\SAIF.Business.Utilities\TitleCase.vb`
