---
title: Title-case a string
summary: Convert a string to title case using acronym/word-list rules.
status: draft
owner: saif-docgen
last_reviewed: 2026-09-15
library: SAIF.Framework.Common
reference_type: package
operation_id: OP-001
trigger: Caller invokes `someString.ToTitleCase()`
---

# Title-case a string

Convert a string to title case using acronym/word-list rules, triggered when caller invokes `someString.ToTitleCase()`.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-001 |
| **Trigger** | Caller invokes `someString.ToTitleCase()` |
| **Inputs** | `string inString` |
| **Outputs** | Title-cased `string` |
| **Primary components** | ``SAIF.Common.Utility.TitleCase.ToTitleCase(string)`, reached publicly via `SAIF.Common.Extensions.StringExtensions.ToTitleCase(this string)`` |
| **Entry point** | `Common/SAIF.Common/Utility/TitleCase.cs` |

## Steps

1. If the input is null or empty, return it unchanged (guard via `StringExtensions.IsNullOrEmpty`). ([BR-014](../business-rules.md#br-014-title-casing-of-a-null-or-empty-string-is-a-no-op))
2. Trim the input and split it on any non-letter, non-digit character using `Regex.Split(inString.Trim(), @"[^\d\w]")`. Delimiter positions survive as empty entries.
3. Branch: if no split entry contains a non-blank word, skip casing entirely and use the original string as the output.
4. Otherwise map each split word through `TitleCaseSingleWord` (OP-002) and reassemble via the recursive `Reassemblestring` helper, re-inserting the original delimiter characters by index. ([BR-015](../business-rules.md#br-015-a-word-that-already-contains-both-uppercase-and-lowercase-characters-is-assumed-intentionally-cased), [BR-016](../business-rules.md#br-016-words-in-the-approved-acronym-list), [BR-017](<../business-rules.md#br-017-minor-words-(is%2C-in%2C-the%2C-an%2C-of%2C-on%2C-and%2C-de)-are-rendered-fully-lowercase>), [BR-018](<../business-rules.md#br-018-known-mixed-case-words-(phd%2C-lacombe)-keep-their-exact-registered-casing>), [BR-019](<../business-rules.md#br-019-a-numeric-word-with-an-ordinal-suffix-(1st%2C-2nd%2C-3rd%2C-4th)-keeps-the-suffix-lowercase>))
5. `Reassemblestring` locates the index of the first non-empty word; if non-zero it clips the leading delimiters off the source string, appends them verbatim, and recurses on the remainder.
6. Split the reassembled output on spaces, apply `ApplyExactSuffixList` to each word (re-casing a trailing `'s` to its canonical form), and rejoin with a single space via `IEnumerableExtensions.ToStringAll(" ")`. ([BR-020](../business-rules.md#br-020-a-trailing-possessive-'s-is-normalized-to-lowercase-after-casing))
7. Uppercase the first character of the result unconditionally and return. ([BR-021](../business-rules.md#br-021-the-first-character-of-any-title-cased-result-is-always-uppercase%2C-regardless-of-every-other-rule))

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start(["Caller invokes<br>someString.ToTitleCase()"]) --> D1{"Input is null or empty?"}
	D1 -->|Yes| R1(["Return input unchanged"])
	D1 -->|No| S2["Trim input and split on any<br>non-letter, non-digit character<br>via regex"]
	S2 --> D2{"Any split entry contains<br>a non-blank word?"}
	D2 -->|No| R2["Use the original string<br>as the output"]
	D2 -->|Yes| S3["Map each split word through<br>TitleCaseSingleWord (OP-002)"]
	S3 --> S4["Reassemblestring: locate first<br>non-empty word; clip and<br>re-append leading delimiters;<br>recurse on the remainder"]
	S4 --> S5["Split reassembled output on<br>spaces; apply ApplyExactSuffixList<br>to each word; rejoin with a space"]
	S5 --> S6["Uppercase the first character<br>of the result unconditionally"]
	R2 --> End(["Title-cased string returned"])
	S6 --> End
	R1 --> End
```

## Failure and edge-case behavior

- No try/catch. A null input returns null (step 1). An input of only delimiters returns the original string (step 3).

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

## Sources
- `Common/SAIF.Common/Utility/TitleCase.cs`
- `Common/SAIF.Common/Extensions/StringExtensions.cs`
