HumanName('JOHN SMITH MBA').capitalize() → 'John Smith Mba' ← now
HumanName('john smith jd').capitalize() → 'John Smith Jd'
HumanName('QC MP').capitalize() → 'Qc Mp'
HumanName('john smith md').capitalize() → 'John Smith M.D.' ← correct, and the reason why is the problem
The parser has already decided these are credential acronyms — mba, jd, qc and mp are in suffix_acronyms, and each is assigned the SUFFIX role. Case repair consults neither the role nor the vocabulary. It falls through to str.capitalize() and title-cases an initialism as though it were a name word.
md and phd escape only because they are hand-listed in CAPITALIZATION_EXCEPTIONS, which holds exactly {ii, iii, iv, md, phd} and has been byte-identical since the 2011 svn import (45a1539). Its docstring says "Any pieces that are not capitalized by capitalizing the first letter." 577 of the 579 shipped alphabetic suffix acronyms have no entry (measured 2026-08-29).
Rationale
An acronym is written in capitals because each letter stands for a word. Capitalizing only the first letter discards that, and the parser already knows which words are acronyms — it put them in the suffix role from the acronym vocabulary.
Case repair decides which letters are capital. It should not add or remove characters: what the writer punctuated stays punctuated, and what they left bare stays bare. Two of the five existing map entries break that — md → M.D. and phd → Ph.D. insert periods the writer never typed. That is substitution, not capitalization, and it is also why the map never grew: you cannot hand-write 577 dotted forms.
Statement
Where the parser has assigned the SUFFIX role to a word from suffix_acronyms, case repair capitalizes every letter of it and leaves every other character exactly as written.
'john smith jd' → capitalized="John Smith JD"
'john smith j.d.' → capitalized="John Smith J.D."
'john smith mba' → capitalized="John Smith MBA"
'JOHN SMITH MA' → capitalized="John Smith MA"
'john smith md' → capitalized="John Smith MD"
'john smith jr' → capitalized="John Smith Jr" · boundary
The boundary is the generational half of the suffix vocabulary: jr, sr and esquire are abbreviations, not initialisms, and title case is right for them.
CAPITALIZATION_EXCEPTIONS values become letter masks
Some acronyms are conventionally written in mixed case, and the existing config surface is the right home for them once its values are read as which letters are capital rather than as replacement strings:
phd → PhD bsc → BSc msc → MSc
Applied as a mask over whatever the writer punctuated, this gives phd → PhD and ph.d. → Ph.D. from one entry, with no period added or removed either way. A caller who ships dphil adds dphil → DPhil themselves.
This is not a one-off for phd: bsc and msc are shipped acronyms whose conventional casing is mixed, and Dr. med. univ. Margit Popp, MSc is already a differential corpus name.
md leaves the map — the all-caps default covers it, and there is no reason for it to behave differently from jd or lcpc. ii, iii and iv stay: they are generational, not acronyms, and need the map because str.capitalize() would give Ii.
Two vocabulary entries should go
esq leaves suffix_acronyms. It is the only word in both suffix_acronyms and suffix_words, and it abbreviates "Esquire" rather than initializing anything — without this it would capitalize to ESQ. An acronym reading (Environmental, Safety & Quality) is a special case a caller adds to their own lexicon.
ph leaves suffix_acronyms. It exists to prop up the space-separated Ph. D., and it is what would produce PH. D. under the rule above. Removing it moves 0 of 1094 corpus names across all seven fields — the dotted-abbreviation handling carries that spelling, not this entry. With it gone the fragment capitalizes as an ordinary initial and Ph. D. is left as written.
Accepted consequences
Measured over the 1094-name differential corpus, 2026-08-29:
- On the default path, 4 names change:
JOHN SMITH MA, MD - DO - DDS, MD, DO, DDS, QC MP. Every other affected name is mixed case, so the case-repair gate (rules.md#R5) already returns it untouched.
- Under
force=True, 75 more change.
- All of it is a deliberate deviation from 1.4.0, which produces the same title-cased output as 2.1.0 today — inherited, not a regression.
Three of the changed names make an existing defect louder rather than causing one: Aishwarya Rai → Aishwarya RAI, Donald mc → Donald MC and John Smith Mc V → John Smith MC V are read as suffixes today when they should be name words (#342, #454). Rendering a wrong parse more visibly is arguably a benefit.
Same principle as #458
#458 says the render views should honor what the parser decided instead of re-deciding from the word. This is that principle in a second view: the parse classified the token as a credential acronym, and case repair renders it as though it had not. Keyed on the role and the acronym vocabulary, the fix needs no new wordlist.
Not proposed here
- Adding periods to suffixes.
md → M.D. is a separate concern from capitalization and would belong in a separate method if it is wanted at all; phd would be its exception. This issue removes period insertion from case repair rather than extending it.
- Normalizing
Ph. D. to Ph.D.. Nothing does this today, all 16 spaced spellings in the corpus are mixed case so the gate never reaches them, and the space is not valid grammar for the degree. Left as written.
- Adding 577 entries to
CAPITALIZATION_EXCEPTIONS — that is the wordlist approach, and it would leave every caller-added acronym broken.
The parser has already decided these are credential acronyms —
mba,jd,qcandmpare insuffix_acronyms, and each is assigned theSUFFIXrole. Case repair consults neither the role nor the vocabulary. It falls through tostr.capitalize()and title-cases an initialism as though it were a name word.mdandphdescape only because they are hand-listed inCAPITALIZATION_EXCEPTIONS, which holds exactly{ii, iii, iv, md, phd}and has been byte-identical since the 2011 svn import (45a1539). Its docstring says "Any pieces that are not capitalized by capitalizing the first letter." 577 of the 579 shipped alphabetic suffix acronyms have no entry (measured 2026-08-29).Rationale
An acronym is written in capitals because each letter stands for a word. Capitalizing only the first letter discards that, and the parser already knows which words are acronyms — it put them in the suffix role from the acronym vocabulary.
Case repair decides which letters are capital. It should not add or remove characters: what the writer punctuated stays punctuated, and what they left bare stays bare. Two of the five existing map entries break that —
md → M.D.andphd → Ph.D.insert periods the writer never typed. That is substitution, not capitalization, and it is also why the map never grew: you cannot hand-write 577 dotted forms.Statement
Where the parser has assigned the
SUFFIXrole to a word fromsuffix_acronyms, case repair capitalizes every letter of it and leaves every other character exactly as written.The boundary is the generational half of the suffix vocabulary:
jr,srandesquireare abbreviations, not initialisms, and title case is right for them.CAPITALIZATION_EXCEPTIONSvalues become letter masksSome acronyms are conventionally written in mixed case, and the existing config surface is the right home for them once its values are read as which letters are capital rather than as replacement strings:
Applied as a mask over whatever the writer punctuated, this gives
phd → PhDandph.d. → Ph.D.from one entry, with no period added or removed either way. A caller who shipsdphiladdsdphil → DPhilthemselves.This is not a one-off for
phd:bscandmscare shipped acronyms whose conventional casing is mixed, andDr. med. univ. Margit Popp, MScis already a differential corpus name.mdleaves the map — the all-caps default covers it, and there is no reason for it to behave differently fromjdorlcpc.ii,iiiandivstay: they are generational, not acronyms, and need the map becausestr.capitalize()would giveIi.Two vocabulary entries should go
esqleavessuffix_acronyms. It is the only word in bothsuffix_acronymsandsuffix_words, and it abbreviates "Esquire" rather than initializing anything — without this it would capitalize toESQ. An acronym reading (Environmental, Safety & Quality) is a special case a caller adds to their own lexicon.phleavessuffix_acronyms. It exists to prop up the space-separatedPh. D., and it is what would producePH. D.under the rule above. Removing it moves 0 of 1094 corpus names across all seven fields — the dotted-abbreviation handling carries that spelling, not this entry. With it gone the fragment capitalizes as an ordinary initial andPh. D.is left as written.Accepted consequences
Measured over the 1094-name differential corpus, 2026-08-29:
JOHN SMITH MA,MD - DO - DDS,MD, DO, DDS,QC MP. Every other affected name is mixed case, so the case-repair gate (rules.md#R5) already returns it untouched.force=True, 75 more change.Three of the changed names make an existing defect louder rather than causing one:
Aishwarya Rai → Aishwarya RAI,Donald mc → Donald MCandJohn Smith Mc V → John Smith MC Vare read as suffixes today when they should be name words (#342, #454). Rendering a wrong parse more visibly is arguably a benefit.Same principle as #458
#458says the render views should honor what the parser decided instead of re-deciding from the word. This is that principle in a second view: the parse classified the token as a credential acronym, and case repair renders it as though it had not. Keyed on the role and the acronym vocabulary, the fix needs no new wordlist.Not proposed here
md → M.D.is a separate concern from capitalization and would belong in a separate method if it is wanted at all;phdwould be its exception. This issue removes period insertion from case repair rather than extending it.Ph. D.toPh.D.. Nothing does this today, all 16 spaced spellings in the corpus are mixed case so the gate never reaches them, and the space is not valid grammar for the degree. Left as written.CAPITALIZATION_EXCEPTIONS— that is the wordlist approach, and it would leave every caller-added acronym broken.