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
4 changes: 3 additions & 1 deletion docs/design/decisions/generational-words.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Closes #661. 31 spelled-out generational words join SUFFIX_WORDS, and one predic
- A refused word carries `excluded = { generation = … }` with its reason in the file of the language whose reading is refused (`uv run python tools/vocab/query.py --excluded generation`) — 2026-10-14 #661; 2026-10-14 #679.
- The words ship spaced only, `二世` excepted, so a hyphen-glued Jr. is no suffix: `Теодор Рузвелт-младши` reads family 'Рузвелт-младши' — 2026-10-14 #661.
- `二世` is a generation word that peels spaced and glued, the one exception to spaced-only shipping: `陳怡君二世` reads family '陳怡君', suffix '二世', and its `ja.toml` and `zh.toml` entries carry `issue = 671` and `attached` with `issue = 707` — 2026-10-21 #762.
- The generational words whose collisions are with OTHER languages written in the same script (fr `père`; the Latin abbreviations `ml.` and `st.`) ship in no default, that being the case a locale pack exists for: `Jiří Dienstbier ml.` reads family 'ml.', and no cs pack exists; cs `st.` is recorded as a reading off by default, for that pack — 2026-10-14 #679; 2026-10-25 #752.
- The generational words whose collisions are with OTHER languages written in the same script (fr `père`; the Latin abbreviations `ml.` and `st.` in cs, hr, sk and sl) ship in no default, that being the case a language choice exists for: each is a reading written `default = false` in its language's file, so `Jiří Dienstbier ml.` reads family 'ml.' by default and suffix 'ml.' with `cs` chosen — 2026-10-14 #679; 2026-10-25 #752; 2026-10-29 #753, phase 3c.
- fr `fils` is a generation reading gated by its family reading, its case and the count (rules/S11): `Alexandre Dumas fils` reads suffix 'fils' and `Marie Fils` family 'Fils', both reported — 2026-10-25 #752.

Elsewhere: the run start that lets any unambiguous suffix word of two or more letters begin the credential run, whatever its script (`Antonio Pavlović mlađi Jones` reads suffix 'mlađi Jones', as does `Pieter Bruegel stariji Jones` with 'stariji Jones'), is decisions/S8's 2026-10-14 (Derek), #679 entry, which retracted this file's #602 run-start bullet; the glued peel of 二世 is decisions/W2's 2026-10-17 #707 entry.
Expand Down Expand Up @@ -38,3 +38,5 @@ Open: #684 (locale packs for `Jiří Dienstbier ml.` and `st.`, the generational
- **2026-10-23 #843 (Derek) — JÚNIOR SHIPS; THE AGNOMENS WAIT.** "Suffix, Júnior only for now." `júnior` joins `pt.toml` as a generation reading with `same_as = "jr"`: the standard Portuguese spelling, which `José Silva Júnior` read as family 'Júnior', middle 'Silva' in 1.4.0 and 2.5.0 (checked against both wheels) while `José Silva Junior` read suffix 'Junior'. Its collisions are `junior`'s, already accepted: a given name (Wikidata 37+ as Júnior, 43,182+ US SSA births as Junior), where the suffix claim does not act (`Júnior Silva` keeps given 'Júnior'), and family bearers (Wikidata 49+, US Census 2,146 as JUNIOR), which a suffix word costs its surname wherever it ends a name (`Pedro Júnior` reads given 'Pedro', suffix 'Júnior', as `Pedro Junior` does), counts from `tools/name_usage.py` on 2026-10-23. In the local corpus `tse-donors` (fetched 2026-10-22), 1,071 of its 854,531 records write it anywhere in the name (`tools/name_usage.py`), 991 distinct filed names end in it, and 983 records end in it, in either case, where the tax registry's name for the same donor ends `JUNIOR` (last words compared after `str.upper()`). The accented spelling reads as the unaccented one in all 120 pairs of a grid of 20 shapes (comma shapes, an empty head, a title, a particle, a trailing title, a nickname, a maiden clause, a credential and a name word behind it, both one-case spellings) under all three orders, composed and decomposed, comparing fields, ambiguity kinds and `initials()`; with `júnior` removed from the lexicon (`Lexicon.default().remove(suffix_words={'júnior'})`), 108 of the 120 pairs differ, and the 12 that do not hold Júnior where no suffix reading reaches it (`Júnior Silva`, and `Júnior, José`, before a family comma). Measured 2026-10-23 on `tse-donors` against the tree before this change (`snapshot.py --local tse-donors`, baseline recorded with `--tree`): 993 of 848,351 filed names move, 990 ending in Júnior and gaining it as the suffix out of the family name (3 of them two-word names, `RONALDO JÚNIOR`, left with no family as `RONALDO JUNIOR` is), and 3 with Júnior inside the name, where it starts a credential run (decisions/S8) that takes the surnames behind it, `JORGE ANTÔNIO JÚNIOR BARBOSA SILVA` reading family 'ANTÔNIO', suffix 'JÚNIOR BARBOSA SILVA' as the JUNIOR spelling already did; and `local_corpora.py pairs tse-donors --agreement` stops listing JÚNIOR / JUNIOR (866 accents-only pairs read with two surnames before, none after). The agnomens (distinct filed names ending in them: `Filho` 10,979, `Neto` 8,439, `Sobrinho` 955, `Netto` 432) stay family names for now. They are legally part of the surname, and `Neto` is a borne surname and given name alone (Wikidata 276+ family, 12+ given), which a suffix reading would leave with no family, as `Pedro Junior` has none. No exclusion records them, because nothing was refused: the question is open on #843.

- **2026-10-25 #752 — THE AGNOMENS AND `fils` ARE GATED GENERATION READINGS (phase 2h).** SUPERSEDES: 2026-10-23 #843's agnomen half, which kept the agnomens family names, and 2026-10-14 #679's refusal of `fils` as pack-shaped. The 2026-10-23 #752 entry in decisions/vocabulary-model gave each generation reading a gate by its kind, and the record, with its measurements, is decisions/S11. `fils` collides inside French, a generation word and a French surname, so no French pack could settle it, which is the premise of #684 the design reversed: its `generation` reading now stands beside its `family` reading, and the gate reads the name around it. The agnomens are not part of the surname: an agnomen marks its bearer against a relative of the same name, as Júnior does, and its name readings (dated counts in `pt.toml` and `pt-BR.toml`, `netto` there being the spelling of Brazil's older orthography) keep a two-word name's family. cs `st.` keeps no exclusion: it is a `generation` reading written `default = false`, recorded for a Czech pack, since nothing in its writing tells it from Saint; hr, sk and sl keep theirs, as do `ml.` and `père`.

- **2026-10-29 #753, phase 3c — `ml.`, `st.` AND `père` ARE READINGS OFF BY DEFAULT.** The exclusions #679 recorded as pack-shaped are `generation` readings written `default = false` (cs `ml`, hr, sk and sl `ml` and `st`, fr `père`; cs `st` since phase 2h), read when their language is chosen, and the default does not move. The record, with what each choice moves, is decisions/vocabulary-model's 2026-10-29 #753, phase 3c entry.
8 changes: 8 additions & 0 deletions docs/design/decisions/vocabulary-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The vocabulary was a set of base sets plus marker subsets (`particles` and `part
- A patronymic field is to come in 3.0, as a view that names its words without emptying `family`, and the particle kinds already draw the `clan`/`patronymic` line for it: a patronymic is followed by a parent's name, a clan prefix by a family name the lineage shares — 2026-10-15/16; 2026-10-19 #749; 2026-10-28 #754 (Derek).
- A language pack is a choice of languages on `Locale.languages` selecting which readings compile, a tag choosing its own file and its ancestors', and a reading index, loaded on its first lookup, carries the languages to run time; strict waits for 3i, and third-party language files are deferred — 2026-10-27 #753 (Derek); 2026-10-29 #753, phase 3a (Derek); 2026-10-29 #753, phase 3b.
- A choice is matched without regard to case, a tag whose file does not exist selects its ancestors' (`pt-PT` selects `pt`), `mul` and `und` are no language to choose, and a reading turned on also prefers, so `de`'s generation `i` drops `ca`'s and `pl`'s conjunction under a `de` choice; a choice is one call's: one chained through `base=` applies on top of the base's rather than unioning with it, and a choice drops the caller's own base entries it removes — 2026-10-29 #753, phase 3b; 2026-10-29 #753, phase 3b, chained choices (Derek).
- A reading right in its own language and wrong only as a default, because another language written in the same script collides with it, is written `default = false` in its file, never excluded, so that after the packs an exclusion means wrong everywhere: cs `st` since phase 2h, and cs `ml`, hr, sk and sl `ml` and `st` and fr `père` since 3c, the rest in their packs' PRs — 2026-10-27 #753 (Derek); 2026-10-29 #753, phase 3c.
- A name reading written `default = false` derives no marker in the default and counts for no check guarding the default's compile; a particle whose word's only `given` readings are off is refused until a pack needs one — 2026-10-29 #753, phase 3a.
- A caller configures vocabulary by reading, `Lexicon` deriving the sets and gates the build derives, and the compiled sets become internal in 3.0 — 2026-10-28 #754, the `Lexicon` surface (Derek).
- A `Locale` may carry CLDR-shaped render patterns, display and sorting, a later addition whose shape is agreed; how a call reaches one is open — 2026-10-28 #754, render patterns.
Expand Down Expand Up @@ -312,3 +313,10 @@ Open: #913 (the title words phase 2j left, read as titles though borne as names)
1. **Chained choices compose one after the other.** `parser_for(EN, base=parser_for(PT))` reads `Maria do Carmo` family `Carmo` and `John Smith DO` family `DO`: `en` drops `pt`'s particle and `pt` had dropped `en`'s credential. The way to choose both is one call, `parser_for(PT, EN)`, which keeps both readings (`do Carmo`, suffix `DO`).
2. **A choice drops what it removes from the base, whoever put it there.** `parser_for(EN, base=Parser(lexicon=Lexicon.empty().add(particles={'do', 'van'})))` keeps only `van`: the delta removes `do` from `particles`, the caller's listing included, and leaves every entry it does not touch.
Weighed against: (b) a `Parser.languages` field recording the choice, so a base's choice unions with the packs' (a new public name, safe for pickles, `Parser`'s guard honoring `ADDED_IN`), deferred rather than declined; (c) a `Lexicon` field recording it, which every older `Lexicon`, `Parser` and pack pickle would refuse, `Lexicon`'s guard not honoring `ADDED_IN`. Open: (b), with the configure-by-reading method names (the 2026-10-28 #754 `Lexicon` surface entry), and with it whether `Reading.languages` follows the choice (the phase 3b entry above).

- **2026-10-29 #753, phase 3c — THE EIGHT PACK-SHAPED EXCLUSIONS ARE READINGS OFF BY DEFAULT: cs `ml`, hr, sk and sl `ml` and `st`, fr `père`; THE DEFAULT DOES NOT MOVE.** Carries out the 3c half of item 9 of the 2026-10-27 #753 entry, and 3c of its item 11.
- **The split.** Every exclusion whose reason opened "pack-shaped" refused a `generation` reading that is right in its language and wrong as a default only because another language written in Latin script collides with it: `ml.` against the initials `M. L.`, `st.` against Saint, `père` outside French. Each is now a `generation` reading written `default = false` in its own file, with the issue list it had and #753, an `expands` naming the full word, and a `note` giving the collision, the shape cs `st` has had since 2026-10-25 (phase 2h). The other exclusions a pack turns on (`pan`, `roz`, `nic`, `shri`, `sig`, `jaun`, `bt`, `bay`, `sb`) stay exclusions until the PR of the pack that turns each on; every other exclusion is wrong everywhere and stays. Recompute: `grep -l 'pack-shaped' nameparser/vocab/*.toml` lists no file, which tests/v2/test_locales.py's `test_no_exclusion_is_pack_shaped_any_more` holds.
- **The default does not move.** The build reports `_compiled.py` current, only `_index.py` gaining the eight rows (11 readings written `default = false`, from 3), and tests/v2/test_snapshot.py passes with no re-record.
- **What choosing each language now does,** measured 2026-10-29 on `b27dadd5` plus this change with `snapshot.py --languages TAG`, entries moved (names held only by `corpus_cldr.jsonl` among them): `cs`, `sk`, `sl` and `hr` 14 each (4), where `cs` moved 11 (2) and the other three none before; `fr` 0, no committed name holding `père`. The three new entries under each read `ml.` as the suffix: the rules corpus's `Jiří Dienstbier ml.`, and two CLDR renderings with `ml.` before a comma of credentials (`g. Janez Peter Novak ml., univ. dipl. inž.`). The other eleven are `st`, and each language's turning on its reading also prefers it (the phase 3b entry's turning-on bullet), so en's and fr's title and particle readings of `st` leave: `St Clair` reads given `St`, family `Clair`, where the default reads title `St`. That cost reached `cs` with 3b; it reaches `sk`, `sl` and `hr` here, and settling it, whether a Saint reading should outlast a choice that holds `st` as a generation, is 3f's, which turns those packs on. tests/v2/test_locales.py's `test_a_chosen_language_reads_its_off_generation_and_the_default_does_not` parses every generation reading off by default of more than one letter (`Anna Novak ml.`, with and without the period) under its language and the default; a one-letter word (es `h`, de `i`) is left out, the initial veto taking it written with its period (`Anna Novak h.` reads family `h.` under `es`, while a bare `h` reads suffix `h`).
- **Open for 3f: a turned-on `ml` reads a lone comma part as the suffix, silently.** `ml` has no name reading, so under a choice it is an ungated suffix word: `Novak, ML` reads suffix `ML` with nothing reported, where the default reads given `ML`, and `Novak ml., Anna` family `Novak`, suffix `ml.`; the gated `st` reports `SUFFIX_OR_NAME`, and fr `Jean Père` reports `GIVEN_OR_FAMILY` (suffix `Père`). The initial pairs `Anna M. L. Novak` and `Anna ML Novak` do not move. Pinned in tests/v2/test_locales.py; whether `ml` takes a name reading or a gate is 3f's. So is the one-letter gap: es `h` and de `i` turned on stay inert written with a period (`Juan Pérez H.` keeps family `H.` under `es`, `Wilhelm Karl I.` family `I.` under `de`), a strict expected failure there.
- Not touched: no `Lexicon` or `Parser` field, no code. The vocabulary README's example of an exclusion is now `duke`, wrong everywhere, where it was `père`.
Loading
Loading