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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ The compiled sets, read by `Lexicon` (mostly as the `_sets` field of the same na
- particles — `PARTICLES` (family-name particles, e.g. "de", "van") and `NON_GIVEN_NAME_PARTICLES`, the curated subset that is *never* a standalone given name (the particles whose word has no `given` reading, each stating `may_be_given = false`; a `given` reading on the word is what makes a particle possibly given, the phase 1 flag `may_be_given = true` refused since 2026-10-25). What the subset licenses is rules/P1's fold in `post_rules` (the "rule 1b" of older issue comments — decisions/legacy-rule-numbers maps the old numbers), and the fold has ONE site: the piece that OPENS the name. A lone never-given particle there, with at least one more name word behind it, starts the family. Titles do not move the opening position (rules/P4), so `Sir de Mesnil` reads family `de Mesnil` under every `name_order`, as `de Mesnil` does. How far the fold reaches depends on the order the name was read under: by default it takes the rest of the name (`de Mesnil Jean` → family `de Mesnil Jean`); under either family-first order it takes the particle run plus ONE name unit and lays the rest out by the order (`de Mesnil Jean` → family `de Mesnil`, given `Jean`); after a family comma it takes the rest of that part whatever the order (`Smith, de Mesnil Jean` → family `Smith de Mesnil Jean`). The leading particle stays a lone piece for the fold to find — `group` does not chain it forward, and the fold walks the particles after it itself — so `de la Vega` reads family `de la Vega` under all three orders. The GIVEN position is not a site. It was a second one until #467, which removed it because under a family-first order that slot holds what the caller DECLARED to be the given name: `Mesnil de` under `FAMILY_FIRST` or `FAMILY_FIRST_GIVEN_LAST` reads given `de`, family `Mesnil`. Nor is a particle inside the name: `Juan de la Vega` under a family-first order reads family `Juan`, given `de la Vega`, with no ambiguity recorded — what the vocabulary forbids is the bare particle as a given name, not a name part that begins with one. A bare `de` has nothing to fold into and is placed by position like any one-word name: given `de` by default, family `de` under both family-first orders. A leading particle OUTSIDE the set is left to position — `van Gogh` is given `van`, family `Gogh` by default and family `van`, given `Gogh` under both family-first orders — and records a `PARTICLE_OR_GIVEN` ambiguity under all three; a never-given particle opening the name records none (`de Mesnil`), and neither does an ambiguous one inside it (`Vincent van Gogh`). The internal `Lexicon._sets.particles_ambiguous`, the particles holding a `given` reading, is its complement within `PARTICLES`, so the two mark OPPOSITE sets — see the flip warning in `docs/migrate.rst` before translating either
- bound given names — `BOUND_GIVEN_NAMES` (`bound_given` readings, e.g. "abdul", "abu"; one that is also a particle needs a `given` reading, a build check); a group-stage rule joins the first non-title piece to its following piece before roles are assigned, reserving a family word unless a family comma has already fixed it or a given-name title stands ahead (#369, rules/P5) (ported from v1's `_join_bound_first_name`)
- given names written short — `GIVEN_NAME_ABBREVIATIONS` (#789, a `given` reading carrying `same_as`, the full name: "chas", "wm", Filipino "ma"), the given names written short that rules/H2's shape yields to; nothing else reads it
- names a given name's second half reads — `PARTICLE_GIVEN_NAMES` (#704, a `given` reading written `after_particle`: "carmen", "ángeles", the Spanish words a compound given name writes behind a particle, which rules/P10 reads as a middle name in `María del Carmen García`, by default), `SAINT_PARTICLES` (the `saint` kind, `san`, `santa`, `st`, `ste`, whose run P10 refuses, `Santa María` being a surname) and `SHARED_NAMES` (#704, a name reading written `shared`: Sikh "kaur", "singh", which rules/O6 and O7 neither take nor complete and P10 does not complete, `Kaur del Carmen García`), private; each is read through classify's tags or the lexicon in `_post_rules`
- names a given name's second half reads — `PARTICLE_GIVEN_NAMES` (#704, a `given` reading written `after_particle`: "carmen", "ángeles", the Spanish words a compound given name writes behind a particle, which rules/P10 reads as a middle name in `María del Carmen García`, by default), `SAINT_PARTICLES` (the `saint` kind, `san`, `santa`, `st`, `ste`, `sta`, `sto`, whose run P10 refuses, `Santa María` being a surname) and `SHARED_NAMES` (#704, a name reading written `shared`: Sikh "kaur", "singh", which rules/O6 and O7 neither take nor complete and P10 does not complete, `Kaur del Carmen García`), private; each is read through classify's tags or the lexicon in `_post_rules`
- conjunctions — `CONJUNCTIONS` (e.g. "and", "of") used to chain multi-word titles and to join name parts (rules/P3), plus `CONJUNCTIONS_AMBIGUOUS` (#383/#479, rules/P8, `may_be_initial`, refused by the build on anything but a cased single letter), the subset of single cased letters that read as an INITIAL in a name written wholly in one case — a marker set
- maiden markers — `MAIDEN_MARKERS` (`birth_marker` readings, e.g. "née", "geb.") routing the following name to `maiden`; with an `addresses = "given"` title, the one kind a phrase may be (`z domu`)
- `config/surnames.py`, a public module — `KOREAN_SURNAMES`, the census list the 2.0 API splits unspaced hangul on (#271), still written in Python with an import-time `assert_normalized`: a segmentation list, outside the per-word model, like the locale packs' lists.
Expand Down
11 changes: 8 additions & 3 deletions docs/design/decisions/H6-title-before-a-family-comma.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
- The run leaves the family its last piece (`Dr., John` → family 'Dr.'), stops at a word that is a particle too (`St Pierre, Ed` and `Freiherr von Berg, Ed` keep their families, while `Dr. Freiherr von Berg, Ed` peels 'Dr.'), and stops where nothing but post-nominals stands behind a title (`RA PhD Jr., John` → family 'RA'), so a title/suffix dual with a name word behind it peels (`Rev. Sr. Smith, Mary` → title 'Rev. Sr.') — 2026-10-17 #701.
- A surname-like title opening a two-word surname is the title (`Pope Smith, John` → title 'Pope'), an accepted cost — 2026-10-17 #701.
- `gov` is a title (`Lt. Gov. Smith, John` → title 'Lt. Gov.'), the first piece the run leaves is the family whatever it is (`Dr. PhD Smith, John` → family 'PhD Smith'), and the particle stop is all that keeps a rank in the family, so `Graf Lambsdorff, Otto` reads title 'Graf' — 2026-10-17 #701.
- The comma-less writing of an abbreviated surname is not this rule's and still splits it (`Juan Sta. Maria` → middle 'Sta.', family 'Maria'); listing `sta` and `sto` as particles is weighed and not decided — 2026-10-18 #710.
- The comma-less writing of an abbreviated surname is not this rule's; `sta` and `sto` are saint particles, so `Juan Sta. Maria` reads family 'Sta. Maria' and `Juan Sta. Maria Reyes` family 'Sta. Maria Reyes' — 2026-10-18 #710; 2026-11-04 #725.

Open: [#709](https://github.com/derek73/python-nameparser/issues/709) (whether `graf`, `fürst`, `prinz` and the other ranks read as `freiherr` does); [#725](https://github.com/derek73/python-nameparser/issues/725) (whether `sta` and `sto` join the particles, so `Juan Sta. Maria` reads family 'Sta. Maria').
Open: [#709](https://github.com/derek73/python-nameparser/issues/709) (whether `graf`, `fürst`, `prinz` and the other ranks read as `freiherr` does).

## Log

Expand All @@ -21,5 +21,10 @@ Open: [#709](https://github.com/derek73/python-nameparser/issues/709) (whether `
THE PR REVIEW (2026-10-17), three decisions. (a) `gov` JOINS TITLES (Derek). `Lt. Gov. Smith, John` read title 'Lt.', family 'Gov. Smith' — a compound split across two fields, where master kept it whole in the family and the comma-less `Lt. Gov. John Smith` reads title 'Lt. Gov.' by H2's shape. The run is vocabulary-only, so the fix is the vocabulary: `gov` is the abbreviation of the listed `governor`. Counts (`uv run python tools/name_usage.py Gov`, 2026-10-17): 236 US Census 2010 surname bearers, rank 80,419, 91.53% Asian/Pacific Islander; SSA given names under 5 a year; Wikidata 3 family-name bearers, all Israeli. The claim acts only in front, so `John Gov` keeps family 'Gov', and a surname written first loses it (`Gov Sokha` → title 'Gov'), the graf-class trade (#salutation-titles) at a smaller incidence. No corpus name moved. The other unlisted second words the review found (`insp`, `sen`, `supt`, `cmdr`, `atty`, `msgr`) still split; each is a vocabulary question on its own. (b) `Dr. PhD Smith, John` KEEPS the branch reading, family 'PhD Smith' (Derek): the first piece the run leaves is family whatever it is, as piece 0 is with no title (`PhD Smith, John`, read that way by 1.4.0 too); master and 1.4.0 gave `Dr. PhD Smith, John` family 'Dr. Smith', suffix 'PhD'. Garbage input, so no row. (c) `Graf Lambsdorff, Otto` reads title 'Graf' while `Freiherr von Berg, Ed` keeps the rank, and the clause first written for it gave the 1919 legal-surname argument as the reason — which holds for every German rank alike. The reason is the particle stop: `freiherr` is the one rank in `particles`. rules/H6's Accepted clause now says so, and the vocabulary question (should `graf`, `fürst`, `prinz` and the rest read as `freiherr` does, with and without a comma) is [#709](https://github.com/derek73/python-nameparser/issues/709).

- 2026-10-18 (Derek), #710 — POINT (1) KEEPS ITS VOCABULARY-ONLY READING, AND NOW HAS A NAMED COST BEHIND IT. Closing #710 raised it again: `Xyz. Smith, John` keeps family 'Xyz. Smith', and since no surname ends in a period, title 'Xyz.' looked like a fair reading. The 2026-10-17 entry gave only the doctrine (the slot is a surname's slot, as at #H5) and the absence of need (every CLDR case is a listed title). It named no surname the shape would break. There is one: a surname can OPEN with an abbreviated word. Filipino records write Santa and Santo as `Sta.` and `Sto.`, and H2's shape (two or more letters, one trailing period) claims both. So `Sta. Maria, Juan`, which reads family 'Sta. Maria' today, would read title 'Sta.', family 'Maria'. US Census 2010 surname bearers, measured 2026-10-18 with `uv run python tools/name_usage.py "Sta. Maria"` (and likewise for each): Sta. Ana 408, Sta. Maria 376, Sto. Domingo 163, Sta. Cruz 110, each over 90% Asian/Pacific Islander. The Census strips punctuation (it looks these up as `STAMARIA`), but the unabbreviated spelling is a separate entry (`Santa Maria`, 12,375, 77% Hispanic), so these counts are the bearers recorded with the abbreviation; what the fold hides is whether they wrote its period, which is what H2's shape needs. `ste` is a particle and no title, so `Ste. Marie, Jean` never enters H6's run today; under a shape peel, point (3)'s particle stop is what would keep it whole. `sta` and `sto` are in no vocabulary set, so nothing would stop the shape on them. `Mt.` and `Ft.` have the same shape and are rare as surnames (`Mt. Pleasant` and `Ft. Smith` each under 100 Census bearers, same date). rules/H6's rationale now names the class, and `Sta. Maria, Juan` is a boundary example there.
NOT FIXED BY THIS RULE: the comma-less writing splits the same surname. `Juan Sta. Maria` reads given 'Juan', middle 'Sta.', family 'Maria' (and `Juan Sto. Domingo` likewise), so it disagrees with `Sta. Maria, Juan` on the family; `Jean Ste. Marie` reads family 'Ste. Marie' because `ste` is a particle. Listing `sta`/`sto` as particles (measured with `Lexicon.default().add(particles={"sta", "sto"})`) fixes both comma-less readings and leaves `Sta. Maria, Juan` as it is. Added that way they are never-given, so P1's fold takes a bare leading one: `Sta Juan` moves from given 'Sta', family 'Juan' to family 'Sta Juan' (bare `Sta` has 120 US Census surname bearers). Listed as ambiguous instead (adding them to `particles_ambiguous` as well), the comma-less readings are fixed the same way and `Sta Juan` keeps given 'Sta', with a `PARTICLE_OR_GIVEN` report. Neither listing has been decided.
NOT FIXED BY THIS RULE: the comma-less writing splits the same surname. `Juan Sta. Maria` reads given 'Juan', middle 'Sta.', family 'Maria' (and `Juan Sto. Domingo` likewise), so it disagrees with `Sta. Maria, Juan` on the family; `Jean Ste. Marie` reads family 'Ste. Marie' because `ste` is a particle. Listing `sta`/`sto` as particles (measured with `Lexicon.default().add(particles={"sta", "sto"})`) fixes both comma-less readings and leaves `Sta. Maria, Juan` as it is. Added that way they are never-given, so P1's fold takes a bare leading one: `Sta Juan` moves from given 'Sta', family 'Juan' to family 'Sta Juan' (bare `Sta` has 120 US Census surname bearers). Listed as ambiguous instead (adding them to `particles_ambiguous` as well), the comma-less readings are fixed the same way and `Sta Juan` keeps given 'Sta', with a `PARTICLE_OR_GIVEN` report. Neither listing has been decided. (Decided by the 2026-11-04 #725 entry: never-given saint particles.)
Declined: peeling by H2's shape before a family comma, again. Reopening it would need a stop for `sta`/`sto`. The particle listing above would supply one only if the peel kept point (3)'s particle stop, and that has to be done on purpose: H2's shape at the head of a name with no comma has no such stop, so `Ste. Marie Jean` reads title 'Ste.' today.
- 2026-11-04 (Derek), #725 — `STA` AND `STO` ARE SAINT PARTICLES, WITH NO GIVEN READING. `Juan Sta. Maria` read middle 'Sta.', family 'Maria' in 1.4.0 through 2.8.1 (the wheels measured 2026-11-04, `PYTHONSAFEPATH=1`, path asserted), and `Juan Sta. Maria Reyes` middle 'Sta. Maria', family 'Reyes'. fil.toml lists both with the `saint` kind, `may_be_given = false` and a title-case mask: `Juan Sta. Maria` → family 'Sta. Maria', `Juan Sto. Tomas Jr.` → family 'Sto. Tomas', `JUAN STA. MARIA` repairs to `Juan Sta. Maria`.
- **Why `saint`, not a plain particle.** As a plain particle, rules/P10 reads `Sta. Maria` behind a one-word given name as a second given name (`maria` is written `after_particle`), and `Juan Sta. Maria Reyes` keeps today's middle 'Sta. Maria'; P10 refuses a saint run, so the whole phrase stays the family.
- **Why no given reading.** With one, a bare `Sta` opening a name reads given 'Sta' and reports `particle-or-given`; without one, P1's fold reads `Sta Juan` family 'Sta Juan', the reading `ste` has had since #360. The counts lean to the family: ORCID's 2025 extract holds `sta` in 11 given boxes, none Philippine, five of them a surname filed with the given names (`Lou Irish Sta Ana`) and six junk (`Sta`, family `Rd`), against 122 family boxes, 38 Philippine; US Census 2010 counts Sta 120 times as a surname, US SSA under 5 births; `sto` is in 2 given boxes against 20 family boxes (`tools/name_usage.py sta --boxes`, 2026-11-04). Declined: keeping a `given` reading, as the issue first proposed.
- **Measured** 2026-11-04 against master `c6207549` (`config_diff.py diff --local all --committed --match '(?i)\bst[ao]\b' 'expr:Parser(lexicon=Lexicon.default().remove("sta", "sto", kind="saint"))' default`, the comparator being the default without the two readings): of 63 names holding either word as a word, 33 move, every one from middle to family (`Mrs Analiza Sta. Maria`, `Kristian Jay Sto. Tomas`), counted before this change's own case row joined the committed corpora, which makes them 64 and 34. 30 read right by eye. Two are garbage input, `Go/ sta Walin` and the Brazilian donor `JARDEL SEBBA FAZ STA ANNA`, whose registry name is `JARDEL SEBBA` (a farm, Fazenda Santa Anna, after it). One is wrong: `ERNANI ESPIRITO STO ASSIS`, registry `ERNANI ESPIRITO SANTO DE ASSIS`, reads family 'STO ASSIS', where `Sto` ends the Portuguese given name Espírito Santo rather than opening a surname; it is the one name written `Espirito Sto` in every local corpus (2026-11-04), and accepted. The committed snapshot moves one view: `Sta. Maria, Juan` loses the S from its initials, a particle writing none.
- **Accepted.** `Juan dela Cruz Sta. Ana` reads middle 'dela Cruz', family 'Sta. Ana', family 'dela Cruz Sta. Ana' before, the Filipino middle name being the mother's surname; under `FAMILY_FIRST` it reads given 'dela Cruz', middle 'Sta. Ana', family 'Juan', and under `FAMILY_FIRST_GIVEN_LAST` given 'Sta. Ana', middle 'dela Cruz', where both read given 'dela Cruz Sta. Ana' before, P1 and P4's reading under those orders, left to their own work. A bare `Sta` takes P1's fold wherever it opens a part: `Sta Maria Juan` reads family 'Sta Maria Juan', `Reyes, Sta Maria` family 'Reyes Sta Maria' with no given name, and `, Sta Maria` family 'Sta Maria', each a given 'Sta' before, while the period-marked `Reyes, Sta. Maria` still reads title 'Sta.' by H2's shape, as a leading `Sta.` does (`Sta. Maria Juan`); none of ORCID's given boxes plainly holds it as a given name (the counts above). A trailing one after a family comma is P6's (`Reyes, Juan Sta.` → family 'Sta. Reyes', middle 'Sta.' before). Standing in the middle or the family ahead of a name word, either word writes no initial, as a particle does there (`Reyes, Juan Sta. Maria` → 'J. M. R.', 'J. S. M. R.' before); read as the given name or as the whole family it writes one (`Juan Sta` → 'J. S.'). A glued `Sta.Ana` is unchanged: rules/S3 reads it as an unlisted dotted suffix where there is a word to spare (`Juan Reyes Sta.Ana` → suffix 'Sta.Ana'), and a family otherwise (`Juan Sta.Ana`).
2 changes: 1 addition & 1 deletion docs/design/decisions/P1-lone-particle-fold.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,4 @@ Declined:
[de Mesnil]) but breaks "de la Vega": measured, a single group is
positioned by the declared order — "Cher" reads given under GIVEN_FIRST — so "de la Vega" would read given="de la Vega" unless a further rule forced a particle-headed group into the family. Add that rule and [de Mesnil][Jean] yields the #364 reading anyway, so the deletion buys nothing and costs P4.

Decided 2026-08-18 (was Open: [#360](https://github.com/derek73/python-nameparser/issues/360), which particles count as never-given): #360 applied the criterion at decisions/vocabulary-collisions and moved `mc` and `ste` into the set (from the may-be-given half), adding the Spanish/Portuguese articles; later vocabulary work extends the set under the same criterion (#604's Irish particles).
Decided 2026-08-18 (was Open: [#360](https://github.com/derek73/python-nameparser/issues/360), which particles count as never-given): #360 applied the criterion at decisions/vocabulary-collisions and moved `mc` and `ste` into the set (from the may-be-given half), adding the Spanish/Portuguese articles; later vocabulary work extends the set under the same criterion (#604's Irish particles; #725's `sta` and `sto`, decisions/H6).
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Standing

- By default, behind a lone given name of one word read given-first, a particle chain opening with a particle run and a word whose `given` reading is written `after_particle` reads that run and word as a middle name, the chain's other words the family: `María del Carmen García` gives middle 'del Carmen', family 'García' — 2026-11-04 #704.
- A run holding a particle of the `saint` kind (`san`, `santa`, `st`, `ste`) is never such a phrase, `Santa María` being a surname — 2026-11-04 #704.
- A run holding a particle of the `saint` kind (`san`, `santa`, `st`, `ste`, and `sta` and `sto`, which decisions/H6 added for #725) is never such a phrase, `Santa María` being a surname — 2026-11-04 #704.
- The rule reads before rules/O6 and O7, so two-given-name handling takes the phrase as the middle unit beside the given name and two-family-name handling leaves it; O7 no longer reads the phrase itself — 2026-11-04 #704.
- The rule reports nothing — 2026-11-04 #704.

Expand Down
Loading
Loading