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
1 change: 1 addition & 0 deletions docs/design/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,7 @@ Closes #342 (a wordlist question) and #454 (a rules.md question) together, becau
- **The accepted cost, stated rather than discovered.** "John Smith RAI" reads family RAI with Smith a middle name, and "Ahmad Jayadi, CHA" reads given CHA, family "Ahmad Jayadi" — with no credential after the comma, C1 reads an ordinary family comma. Both are case rows, so the reversal is one measurement away for anyone who wants it back. The judgment behind accepting it: a wrong credential reading destroys a real person's surname on every record they appear in, and a missed credential reading leaves the letters in a name field where a human can still see them.
- **The roman-numeral accident, recorded because it looks like a survival and is not.** "John Smith R.A.I." still reads suffix R.A.I. after the removal — but by ACCIDENT, not by vocabulary: rules.md#S3 splits a token with interior periods on those periods and reads the chunks (`_pipeline._vocab.period_joined_vocab`), and the last chunk `i` is a Roman numeral in SUFFIX_WORDS, so "John Smith X.Y.I." and "John Smith R.A.V." read suffix while "John Smith R.A.X." does not — measured 2026-09-07, `period_joined_vocab('R.A.I.')` returning "suffix" and the chunks being r, a, i, never `ra`. "John Smith C.H.A." shows the other half: it read suffix BEFORE the removal, the lookup fold stripping the periods to reach the cha entry, and reads family C.H.A. after. Nothing is pinned on the accident; it is evidence for the parking lot below.
- **ba joined SUFFIX_ACRONYMS_AMBIGUOUS.** BA is a common credential and Ba a real surname (Vietnamese; Senegalese Fula) — the ma/Ma shape exactly. Measured: "Anna Ba" reads family Ba with a suffix-or-name flag (1.4.0's reading plus the flag), "John Smith BA" keeps suffix BA and is newly flagged, "John Smith B.A." is an unflagged suffix through the period gate, and "Smith, BA" reads given BA — the "Smith, Ed" cost, already Accepted under S2 and not reopened here. No corpus name carries a trailing ba, so nothing moves in the differential.
- **mai left SUFFIX_ACRONYMS (2026-10-16, #687).** The relative-frequency criterion above, asked of a third word and answered as rai and cha were. MAI is "Member, Appraisal Institute", a specialized US real-estate designation; Mai is a Vietnamese given and family name, a Chinese surname (麥) and a Japanese given name. `tools/name_usage.py Mai`: US Census 2010 17,445 family-name bearers (rank 2,075), SSA 5,697+ given-name bearers, Wikidata 822+ family and 458+ given. The entry arrived in the 2019-12-11 bulk Wikipedia post-nominal import (af5bdab, #93) and was never reviewed. Measured: `Tran Thi Mai` under `FAMILY_FIRST_GIVEN_LAST` read suffix `Mai` with no given name (it now reads given `Mai`, middle `Thi`, family `Tran`), and `Nguyen Mai` read suffix `Mai` under every order (family `Mai` by default, 1.4.0's reading). The accepted cost is `John Smith MAI`, which reads middle `Smith`, family `MAI`, as `John Smith RAI` does; `Smith, John, MAI` still reads suffix (reported `comma-structure`) and `John Smith M.A.I.` still reads suffix by the dotted shape (rules.md#S3). A caller who needs it back adds it: `Lexicon.default().add(suffix_acronyms={"mai"})`. Marking it ambiguous, the `ba` outcome, was the alternative and was not taken: a trailing `Mai` is a name far more often than a credential. One corpus name moves, `Tran Thi Mai` under `FAMILY_FIRST` (snapshot, 2026-10-16).
- **#454 is BY DESIGN, and the words are mc and vd.** mc has no vowel and is not a borne name — which reverses #342's comment, where mc is listed among the entries "borne as surnames" and is the one member of that list its model-recall hedge does not cover; #360 measured it: mc and ste are contractions of Mac and Sainte, borne in neither position, which is why both left the ambiguous particle half. The Scottish prefix never detaches from the name it belongs to, so "Donald Mc" is not a name anyone writes and its suffix reading costs nothing real. vd needs a surname after it, so a bare trailing vd is the decoration. Neither joins the ambiguous subset and the `fix(suffix-routing)` ledger rule — a two-token name ending in a credential acronym keeps it in `suffix` — stays. What #454's example actually turns on is a different membership: "Mc Donald" reads family "Mc Donald" and "John van Mc" reads family "van Mc" because mc is a never-given PARTICLE (#360), which is why the trailing bare shape is the only one at issue.
- **What #454 got right, and it is a rules.md defect that is now fixed.** rules.md#P6's Accepted clause said that under the default order a comma-less name keeps its positional reading, with "Jong Anke de" → family "de" as its only example. Measured, that holds for de and for do and fails for exactly the two words that are in BOTH the particle vocabulary and the UNAMBIGUOUS suffix vocabulary: "Donald mc" and "Smith vd" lose the family entirely to `suffix`. The clause is repaired to say so, and the two places that already conceded it in prose — the `'Donald mc'` bullet of #differential-ledger's fields-only arc and the corresponding comment in expected_since_1.4.0.toml — are amended from "recorded here rather than fixed" to point at the repair. No example line moves: the clause's own example is de-shaped and stays true, which is why no test caught the defect and why regenerating corpus_rules.jsonl is a no-op here. Recompute the membership with `L = Parser().lexicon; sorted(L.particles & (L.suffix_acronyms - L.suffix_acronyms_ambiguous))`, which gives ['mc', 'vd'] on 2026-09-07, and `sorted(L.particles & L.suffix_acronyms_ambiguous)`, which gives ['do'].
- **Parking lot: the shape-plus-position credential heuristic.** #490's idea without the "collides with vocabulary" part, and what would recover "John Smith RAI" without any wordlist at all. Two shapes: an ALL-CAPS acronym standing in the suffix position of a MIXED-CASE name ("John Smith XYZ", "John Smith, XYZ"), where the case contrast is the signal; and a DOTTED acronym in that position regardless of case ("John Smith X.Y.Z.", "john smith x.y.z."), where the periods are. Measured 2026-09-07, none of the four is read as a suffix today — the bare form gives family XYZ, the comma form given XYZ with family "John Smith", and the second two family X.Y.Z. — while the roman-numeral accident above reads three dotted forms as suffixes on a fork that is about generations, not credentials. That contrast is the evidence the dotted shape is unhandled: the parser already produces the wanted answer for X.Y.I. and R.A.V. by coincidence and the unwanted one for R.A.X. A follow-up issue carries it; nothing in this entry depends on it.
Expand Down
1 change: 1 addition & 0 deletions docs/release_log.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Release Log
**Behavior Changes**

- **Add junior and senior in other languages as suffixes.** ``Antonio Pavlović mlađi`` gives last ``Pavlović``, suffix ``mlađi``, where 1.4.0 through 2.4.2 gave middle ``Pavlović``, last ``mlađi``. The words come from 45 languages and are read when written as a separate word after the name. :issue:`661`, :issue:`679`
- **Remove mai from the default post-nominal acronyms, so a trailing Mai keeps the name.** ``Tran Thi Mai`` read in Vietnamese order (``FAMILY_FIRST_GIVEN_LAST``) gives given ``Mai``, middle ``Thi``, family ``Tran``, where 2.0.0 through 2.4.2 gave suffix ``Mai`` and no given name; ``Nguyen Mai`` gives family ``Mai``, as 1.4.0 did. The cost is that ``John Smith MAI`` gives last ``MAI`` instead of suffix; ``Smith, John, MAI`` and ``John Smith M.A.I.`` still read as credentials. A caller who needs it back adds it: ``Lexicon.default().add(suffix_acronyms={"mai"})``. See the ``suffix-acronym-collisions`` entry of ``docs/design/decisions.md`` (:issue:`687`)

* 2.4.2 - October 13, 2026

Expand Down
1 change: 0 additions & 1 deletion nameparser/config/suffixes.py
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,6 @@
'lvt',
'ma',
'maaa',
'mai',
'mba',
'mbe',
'mbs',
Expand Down
26 changes: 26 additions & 0 deletions tests/v2/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,32 @@ def _check_cjk_shape_purity(self) -> None:
"join the ambiguous class by shape and the words-to-"
"spare count reads this one a credential too",
shape=1),
# -- #687: 'mai' left SUFFIX_ACRONYMS by the same criterion. Mai is
# a Vietnamese given and family name, a Chinese surname and a
# Japanese given name; MAI (Member, Appraisal Institute) is a
# specialized US credential. The name reading dominates, so the
# entry is removed rather than marked.
Case("trailing_given_name_mai_is_not_a_credential", "Tran Thi Mai",
{"given": "Mai", "middle": "Thi", "family": "Tran"},
policy=Policy(name_order=FAMILY_FIRST_GIVEN_LAST),
classification="fix(#687)",
notes="every release read suffix 'Mai' here, so a Vietnamese "
"name in its own declared order lost the name the "
"person is addressed by. 1.4.0 has no name_order and "
"read suffix 'Mai' for the default order too"),
Case("bare_family_name_mai_keeps_the_family", "Nguyen Mai",
{"given": "Nguyen", "family": "Mai"},
notes="1.4.0's reading, which 2.0.0 through 2.4.2 lost to the "
"MAI entry"),
Case("removed_mai_credential_loses_its_suffix_reading",
"John Smith MAI",
{"given": "John", "middle": "Smith", "family": "MAI"},
classification="fix(#687)",
notes="the accepted cost, as 'John Smith RAI' above: with no "
"comma or periods the all-caps shape carries no signal "
"the parser reads. 'Smith, John, MAI' and 'John Smith "
"M.A.I.' still read suffix, by the comma and the "
"dotted shape"),
# -- #342: 'ba' is the other half of the same decision. BA is a
# common credential and Ba a real surname (Vietnamese; Senegalese
# Fula), which is the ma/Ma shape exactly, so it takes the
Expand Down
2 changes: 2 additions & 0 deletions tests/v2/test_facade_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
# #296's bundle: the positional read of the pre-comma name under a
# declared family-first order -- v1 has no name order to declare
"family_comma_no_name_word_family_first",
# #687: a declared Vietnamese order, which v1 cannot express
"trailing_given_name_mai_is_not_a_credential",
"family_comma_no_name_word_family_first_given_last",
"family_comma_no_name_word_family_first_plain",
"leading_never_given_particle_two_leftovers_family_first",
Expand Down
11 changes: 0 additions & 11 deletions tools/differential/conventions_agreement.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,17 +254,6 @@
},
"text": "Szabó Péter úr"
},
"conventions/Tran Thi Mai/FAMILY_FIRST": {
"given_names": {
"expected": "Thi Mai",
"got": "Thi"
},
"suffix": {
"expected": "",
"got": "Mai"
},
"text": "Tran Thi Mai"
},
"conventions/U Kyaw Win/default": {
"family": {
"expected": "",
Expand Down
2 changes: 1 addition & 1 deletion tools/differential/snapshot.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -3005,7 +3005,7 @@
{"name": "Torres, Ana", "given": "Ana", "family": "Torres", "initials": "A. T."}
{"name": "Tounens, Antoine de", "given": "Antoine", "family": "de Tounens", "initials": "A. T."}
{"name": "Traberth, Julie", "given": "Julie", "family": "Traberth", "initials": "J. T."}
{"name": "Tran Thi Mai", "order": "FAMILY_FIRST", "given": "Thi", "family": "Tran", "suffix": "Mai", "initials": "T. T."}
{"name": "Tran Thi Mai", "order": "FAMILY_FIRST", "given": "Thi", "middle": "Mai", "family": "Tran", "initials": "T. M. T."}
{"name": "Tschagribeg", "given": "Tschagribeg", "ambiguities": ["GIVEN_OR_FAMILY"], "initials": "T."}
{"name": "Tschurtschenthaler, Anton von", "given": "Anton", "family": "von Tschurtschenthaler", "ambiguities": ["PARTICLE_OR_GIVEN"], "initials": "A. T."}
{"name": "Tunde Seun Okeke", "given": "Tunde", "middle": "Seun", "family": "Okeke", "initials": "T. S. O."}
Expand Down
Loading