Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# R8 — the generation and credentials views
# R8 — the generation, credentials and honorifics views

## Standing

- `ParsedName.generation` and `ParsedName.credentials` partition `suffix` by the reading each word took, a generation or an agnomen in the first and every other word in the second; they are views, not roles, so `suffix`, `as_dict()`, `replace()`'s fields and the repr are unchanged, item 4 of decisions/vocabulary-model's #754 output-model entry — 2026-10-29 #754, sub-phase 4b.
- A listed suffix word's kind is the lexicon's, compiled with its sets (`Lexicon._sets.generation_words`, `credential_words`), which on the default is the shipped vocabulary's — 2026-11-02 #754, sub-phase 4i-b, superseding 4b's read from the reading index.
- `ParsedName.generation` and `ParsedName.credentials` partition `suffix` by the reading each word took, a generation or an agnomen in the first and every other word in the second (the honorifics leaving the second for a view of their own since 2026-11-03, the bullet below); they are views, not roles, so `suffix`, `as_dict()`, `replace()`'s fields and the repr are unchanged, item 4 of decisions/vocabulary-model's #754 output-model entry — 2026-10-29 #754, sub-phase 4b.
- The honorifics, a listed suffix word whose kind is `honorific` or `professional_honorific` (`さん`, `씨`, `先生`), leave `credentials` for a third view, `ParsedName.honorifics`, and read as `ReadingKind.HONORIFIC`; a courtesy (`Esq.`) and a status (`ret.`) stay in `credentials`, read as `post-nominal`, and the offices CLDR files as credentials (`議員`, `国会議員`, `太平紳士`) are credentials by kind — 2026-11-03 #326 (Derek).
- A listed suffix word's kind is the lexicon's, compiled with its sets (`Lexicon._sets.generation_words`, `credential_words`, and since 2026-11-03 `honorific_words`), which on the default is the shipped vocabulary's — 2026-11-02 #754, sub-phase 4i-b, superseding 4b's read from the reading index.

Elsewhere: the field's composition and the 2026-08-16 decision to keep it one field, which still stands, decisions/suffix-field-composition; whether `credentials` should leave the honorifics out, CLDR having no third field, is a value question under the RFC's Phase 4, Left open (docs/design/vocabulary-model-rfc.md).
Elsewhere: the field's composition and the 2026-08-16 decision to keep it one field, which still stands, decisions/suffix-field-composition.

Elsewhere: the gated credential words `kc`, `pc` and `dl`, a credential written as a word beside a name reading, read as credentials, decisions/S11's 2026-10-30 #965 entry.

Expand All @@ -19,6 +20,11 @@ Open: none in this file.
3. **Each word is placed by its own reading, and separated as the writer separated it.** A `joined` mark is not only the second word of a split credential (`Ph.` `D.`, which takes the first word's reading) but every word of a suffix run written without commas (`Jr. III PhD`), so a view places each word by its reading. Two words of one view are separated by a comma where the suffix holds one anywhere between them, R1's separator (a typed comma, or a semicolon, hyphen, slash or delimiter C10 drops: `MD / Jr. / PhD` gives credentials 'MD, PhD'), and by a space where not, so `MD Jr. PhD` gives credentials 'MD PhD'; the first draft started a new comma-joined entry wherever a word of the other view stood between, writing a comma nobody typed ('MD, PhD'), found by the design-docs review. A connective goes with the word before it (`PhD and MD`, `Jr. and III`), so one joining a generation to a credential dangles at the end of the generation (`Jr. and MD` gives generation 'Jr. and'), accepted rather than given a rule of its own, no corpus name holding the shape.
4. **A suffix word with no reading is a credential.** `replace()` and `Parser.revise()` write tokens with no `Token.reading` (4a), and a `ParsedName` unpickled from 2.x has none either, so `replace(suffix="Jr.")` reads credentials 'Jr.' and the 2.8 pickle of `Dr. Juan Q. de la Vega III` credentials 'III': nothing records what the word was read as, and the catch-all is the view that claims least. A name word a run took into the suffix by position is a credential too, 4a's reading of it (`Smith, Jr., John` gives credentials 'John').
5. **The views stay out of `as_dict()`, `--json` and the repr**, as `family_base` and the other derived views do: `as_dict()` is the seven fields `replace()` takes back. They are `render()` keys and assertable fields of a rule's example lines.
6. **The snapshot records them** (`tools/differential/snapshot.py`, a `RECORDED_LATER` pair): `generation` where the suffix holds one, and `credentials` beside it where the suffix holds both, so a row with neither is a suffix of credentials only, or none. No other key moved on recording.
6. **The snapshot records them** (since 2026-11-03 it records `honorifics` too, so a row with neither generation nor credentials holds credentials and honorifics only, or none: the 2026-11-03 #326 entry below) (`tools/differential/snapshot.py`, a `RECORDED_LATER` pair): `generation` where the suffix holds one, and `credentials` beside it where the suffix holds both, so a row with neither is a suffix of credentials only, or none. No other key moved on recording.
- Measured 2026-10-29 on the 4b branch over the committed snapshot (`tools/differential/snapshot.jsonl`, 6,570 readings, rules/R8's own examples included): 1,077 rows hold a suffix, 488 of them a generation, 142 a generation and credentials both. Recompute: count rows with `suffix`, `generation` and `credentials` in the snapshot. Over `cldr_fields.jsonl`, 576 distinct (text, order) renderings carry CLDR's `generation` or `credentials`; 65 parse to exactly CLDR's suffix words, and all 65 split into CLDR's two fields. Recompute: parse each distinct text under its order (`givenFirst` as `GIVEN_FIRST`, `surnameFirst` as `FAMILY_FIRST`, any other order the default), keep those whose `suffix` words, commas removed, equal CLDR's `generation` and `credentials` words together, and compare each view's words with its field's. The RFC's 615 and 75 (2026-10-28) counted renderings, not distinct (text, order) pairs.
- 2026-11-02 #754, sub-phase 4i-b — A LISTED SUFFIX WORD'S KIND IS THE LEXICON'S. SUPERSEDES items 1 and 2 of the 4b entry above. A caller now states a word's kind (`add("secundus", kind="generation")`), and a view reading the index instead would ignore it in silence, so the compile gives the kinds the sets do not record, `GENERATION_WORDS` and `CREDENTIAL_WORDS`, and classify reads them off the lexicon (rules/R8 amended). On the default they are the index's by the compile invariant (`test_the_default_kind_sets_are_the_shipped_index_s`), so no default parse moves; one test's caller-configured reading did: `Lexicon.default().add("st", kind="suffix")` reads `St` as a post-nominal where 4b read the cs file's generation, written `default = false`. An agnomen needs a name reading, as the build's check 11 requires of a file, so `add(w, kind="agnomen")` alone is refused. decisions/vocabulary-model's 2026-11-02 #754, sub-phase 4i-b entry has the rest.
- 2026-11-03 #326 (Derek) — THE HONORIFICS HAVE A VIEW OF THEIR OWN. #326's last open question was whether the honorifics after a name belong in `credentials`, CLDR's catch-all, CLDR having no third field. Three options were weighed: keep them in `credentials`; give them a third view and narrow `credentials`; or move them out of `suffix` into `title`, rules/W3's "no part of the name" argument. The third would move 2.x callers' `suffix` text, and the render default, which writes the title first, would render `김민준 씨` as `'씨 김민준'`, so it was declined. The second was chosen. It had to be decided before 3.0 ships `credentials`, the view being new there, so that narrowing it later is not a change to a released view; CLDR's `en` pattern writes `{surname} {generation}, {credentials}`, which with the honorific in the credentials would render `Kim Minjun, ssi` once a pack carries patterns. What was built:
1. **The kinds.** `honorific` (19 words on 2026-11-03: `さん`, `様`, `씨`, `님`, `氏`, `जी`, `साहब`, ...) and `professional_honorific` (11: `先生`, `教授`, `선생님`, `교수님`, `박사님` and the job titles `社長`, `課長`, `部長` with their simplified twins) compile into `HONORIFIC_WORDS`, `Lexicon._sets.honorific_words`, as `CREDENTIAL_WORDS` holds a credential written as a word (4i-b); classify tags such a word `vocab:honorific-word`, `_vocab.suffix_kind` reads it as `ReadingKind.HONORIFIC`, and assemble drops the tag from the published token, as it drops the other kind tags. `courtesy` (`esq`, `rinpoche`, `שליט"א`, `महाराज`) and `status` (`ret`, `vet`, `ז"ל`) stay `post-nominal` and in `credentials`: CLDR's own examples of its credentials field include `Esq.`, and a status says something about the bearer rather than addressing them. A courtesy said to the bearer (`Rinpoche`, `महाराज`, `মহারাজ`) stays there with `Esq.`, by its kind, accepted rather than re-kinded (rules/R8's Accepted).
1a. **The offices left the honorifics** (Derek, 2026-11-03, on the design-docs review's finding): the first build gave the whole `professional_honorific` kind (17 words) to the new view, and the rule's Because said CLDR's credentials field does not hold a form of address. `tools/differential/cldr_fields.jsonl` (CLDR release 48) holds both sorts: it files `先生` (12 renderings), `教授` (9), `さん` (3) and `님` (3) as `title`, a form of address, and `議員`, `议员`, `国会議員`, `太平紳士` and `太平绅士` (3 each) as `credentials` (`雅婷婷婷王二世先生,議員` credentials `議員`). So those six offices, the five and `國會議員`, the Diet member's traditional spelling, take the `credential` kind written as a word (`acronym = false`), as `博士` is, in `ja.toml` and `zh.toml` alike, a word having one suffix kind in every file (the build's check 4); their glued peel is the `attached` property's and is unchanged (`田中議員` suffix `議員`). The job titles `社長`, `課長` and `部長`, which CLDR's samples do not hold, stay `professional_honorific`, their notes calling each a form of address after a name. Recompute: for each word of `HONORIFIC_WORDS` and the six, count the fields of `cldr_fields.jsonl` whose value is the word or holds it as a comma-separated item.
2. **What moved.** No field of any parse: `suffix` keeps every word, and `as_dict()`, `replace()`'s fields and the repr are unchanged. In the committed snapshot (`tools/differential/snapshot.jsonl`, each name read under its declared order), 73 of 6,793 readings hold an honorific in the suffix and 21 of them other suffix words besides (`田中さん, PhD`: credentials `PhD`, honorifics `さん`), each moving its views only, and the snapshot records the new view where it is not empty (`RECORDED_LATER`). `Token.reading` of the 30 words reads `honorific` where it read `post-nominal`, and so does `Ambiguity.alternative` where a lone one reports the suffix-or-name fork: six on 2026-11-03, the Devanagari and Bengali `जी`, `साहब`, `साहिब`, `साहेब`, `বাবু` and `সাহেব`, which 2.7.0 and 2.8.0 named `post-nominal`. A caller's `add(w, kind="honorific")` puts the word in the view; `add(w, kind="courtesy")` keeps it in `credentials`.
3. **The rule.** rules/R8's Then names three views and its file is renamed for them; the decisions file follows it. Recompute the kind counts of item 1 by tallying `nameparser._reading_index.suffix_kind_of(w)` over `Lexicon.default()._sets.honorific_words`, and item 2's lone-word alternatives by parsing each word of that set alone and keeping the reports whose `alternative.kind` is `ReadingKind.HONORIFIC`. Recompute the snapshot counts: count the rows of `tools/differential/snapshot.jsonl` recording `honorifics`, and of those, the rows whose `suffix` holds words the `honorifics` value does not.
2 changes: 1 addition & 1 deletion docs/design/decisions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ Entry conventions:
- [R5 — the case-repair gate](R5-case-repair-gate.md)
- [R6 — the default rendering follows the written name](R6-default-rendering-follows-the-written-name.md)
- [R7 — no initials in a script that writes none](R7-no-initials-in-a-script-that-writes-none.md)
- [R8 — the generation and credentials views](R8-generation-and-credentials-views.md)
- [R8 — the generation, credentials and honorifics views](R8-generation-credentials-and-honorifics-views.md)
- [R9 — the patronymic view](R9-patronymic-view.md)
- [R10 — the second-surname view](R10-second-surname-view.md)

Expand Down
4 changes: 2 additions & 2 deletions docs/design/decisions/suffix-field-composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

- `suffix` stays one field holding generational words, credentials and postnominal honorifics; the composition is recorded, with the command that re-counts it, instead of splitting the field — 2026-08-16 #326.

Elsewhere: generation and credential are separate kinds in the vocabulary, though they still compile into the one field, decisions/vocabulary-model's 2026-10-15/16 entry (item 3). The field stays one, and since 3.0 two views partition it by the reading each word took, `generation` and `credentials`, the honorifics in the second (#326, decisions/R8).
Elsewhere: generation and credential are separate kinds in the vocabulary, though they still compile into the one field, decisions/vocabulary-model's 2026-10-15/16 entry (item 3). The field stays one, and since 3.0 three views partition it by the reading each word took, `generation`, `credentials` and `honorifics` (#326, decisions/R8).

Open: [#326](https://github.com/derek73/python-nameparser/issues/326) — whether the honorifics belong in `suffix` at all, deliberately undecided here (the views' side of it is decisions/R8's).
Open: none in this file. Whether the honorifics belong in `suffix` at all, left open here by the 2026-08-16 entry, was decided on 2026-11-03: they stay in the field and have a view of their own, decisions/R8's 2026-11-03 #326 entry.

## Log

Expand Down
Loading
Loading