Skip to content

HumanName('JOHN SMITH MBA').capitalize() gives John Smith Mba — a credential acronym is title-cased as an ordinary word #459

Description

@derek73
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.

Metadata

Metadata

Assignees

标签

项目

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions