Skip to content

Add locales.MN, a Mongolian pack reading the father's name as the family (#1237) - #1365

Open
derek73 wants to merge 6 commits into
masterfrom
feat/issue-1237-mongolian-pack
Open

derek73 wants to merge 6 commits into
masterfrom
feat/issue-1237-mongolian-pack

Conversation

@derek73

@derek73 derek73 commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

Closes #1237. Refs #1253.

Adds locales.MN, a Mongolian pack, the first leaf of #1253's decision (its first statement: family is the name a convention files a person under).

from nameparser import parser_for, locales
sukhee = parser_for(locales.MN).parse("Батын Сүхээ")
sukhee.family, sukhee.given   # ('Батын', 'Сүхээ')

1.4.0 through 3.0.1, and the default still, read given Батын, family Сүхээ.

What the pack is

  • A plain choice of mn and mn.toml's new [rules] table, name_order = "family-first-given-last", so the father's name, which a Mongolian name writes first and passports file as the surname, is the family.
  • Given name last, not second, so a word written before the father's name (a clan name, Боржигин Батын Сүхээ) leaves the given name in place. The clan name itself is not read as one.
  • The choice of mn changes no reading (mn.toml's lama and an exclusion are on by default); it is recorded so a strict parser reads mn.toml. Two choice tests assumed a non-empty surface, so _INERT_CHOICES exempts MN and a test holds its surface empty.
  • The genitive is not read into the patronymic view (see the decision below). The glued form Ц.Сандаг-Очир (Should a Mongolian choice read a glued father's initial as the family? (Ц.Сандаг-Очир gives given 'Ц.', family 'Сандаг-Очир') #1306) reads family Ц., given Сандаг-Очир, by tokenize's full stop; not changed.
  • Traditional Mongolian script reads by the declared order like any script rules/W4 does not settle, so under the pack family-first (ᠪᠠᠲᠤ ᠶᠢᠨ ᠰᠦᠬᠡ → family the joined genitive, rules/T6). The default's reading of it is decisions/T6's open question, unchanged.
  • The default does not move.

Gate: measured before the code (2026-11-12, master 2b39f23)

Default against FAMILY_FIRST, right where family is the father's name:

Source Script Written default FAMILY_FIRST
State Great Khural (mn_parliament, OpenSanctions 2026-11-06, read directly; opensanctions-parliaments does not fetch it) Cyrillic 126 father first, nominative 0 126
JRC-Names, six Mongolian people, the two-word spellings the recipe classifies Cyrillic 9 of 17 classified, all father first (the rest father first by eye, or noise) 0 9
same Latin 45 of 55 classified: 42 father first, 3 given first 3 42
ORCID country MN, 150 Latin free-text credit names with a family box, against that box Latin two full words: 22 father first, 65 given first 73 25

Recipes, the comparators and the collisions are in decisions/vocabulary-model's 2026-11-12 #1237 entry.

Decisions for the maintainer

  1. Not done: a rule reading the genitive into the patronymic view. Where does a father's name go when the name has no family name? (Abiy Ahmed Ali, Ухнаагийн Хүрэлсүх, Бекмырза Чолпонбай уулу) #1253 allows one. I recommend against it for now, for three reasons:
    • Under this pack's order the view would only repeat family. The one reading it adds alone, a romanized given-first name with the genitive last, appears in no cached source.
    • The romanized endings collide with given names written first: JRC's two-word spellings opening with -yn include Mervyn King, Carolyn Porco and Kathryn Gustafson, and its -ийн ones include the Bulgarian spellings of Madeleine, Gene and Dean.
    • A rule kind is a new PatronymicRule member and a Policy change, in a file another worker is editing.
  2. Not taken: an order for Cyrillic only. The evidence argues for reading Cyrillic father-first and leaving romanized names to the declared order. Policy.script_orders could say that if Script had a Cyrillic member, but it has none (_policy.py again), and rules/W4's licence is a script that settles the order, which Cyrillic, shared with Russian, does not. The pack ships the order Should a Mongolian choice read the father's name first? (Ухнаагийн Хүрэлсүх gives given 'Ухнаагийн', family 'Хүрэлсүх') #1237's option 1 and Where does a father's name go when the name has no family name? (Abiy Ahmed Ali, Ухнаагийн Хүрэлсүх, Бекмырза Чолпонбай уулу) #1253's decision name.
  3. Accepted costs (each pinned by a test):
    • A romanized name written given-first reads backwards (Suhee Bataagiin → family Suhee).
    • So does the given name followed by the father's initial (Uyanga B. → family Uyanga), which the default reads right and reports as an order ambiguity: 8 spaced among ORCID's 150, against 2 with the initial first.
    • Russian names in a Mongolian list (raised in review): the given-last order reads Сидоров Иван Петрович given Петрович, where FAMILY_FIRST reads given Иван; and under parser_for(locales.MN, locales.RU) the patronymic lands in given, where R9's East Slavic rule (which reads a middle word) does not see it, so no patronymic is read. Neither the clan-name shape the order was chosen for nor this one is measured; your call whether FAMILY_FIRST is the better default.
    • Traditional script written with a plain space before the genitive suffix (ᠪᠠᠲᠤ ᠶᠢᠨ ᠰᠦᠬᠡ) reads the suffix as a middle name, as the default does; with U+202F it joins (rules/T6).
    • The CLDR sample's own title Ноён (Mr.) is no listed title: a vocabulary candidate, not filed.

Sweeps under the pack

  • Comma shapes: a family comma keeps the default's reading (Батын, Сүхээ, Сүхээ, Батын, , Сүхээ).
  • Titles and suffixes: Др. Батын Сүхээ, Dr. Bataagiin Suhee, Батын Сүхээ Jr. and Bataagiin Suhee, PhD read the father's name as the family.
  • One word: a lone word reads given; Policy.lone_name_word=Role.FAMILY on the base reads it family.
  • Word counts and spellings: three and four words, a hyphenated given name, one case, NFD (Дорждэрэмийн, Khürelsükh).
  • Parser.revise keeps the family. No view fills.
  • Pack unions:
    • parser_for(locales.MN, locales.RU) declares the order, so Сидоров Иван Петрович reads given Петрович (O1's scope: a declared family-first order stands).
    • MN+TR_AZ and MN+VI read by the order too.
    • strict=True reads the same names alike; Lama stays a title.
  • Pickling: the pack and a parser round-trip. Under the 3.0.1 wheel both are refused, but for 3.1's own new fields (PolicyPatch.initials_as_family, a military reading's same_as), as every 3.1 pickle is. A hand-built Locale of the same data parses alike there.
  • Matching:
    • Батын Сүхээ / Сүхээ Батын conflicts under the pack as it does by default; across-roles does not reach a two-word swap.
    • Б. Сүхээ is consistent with the full name only with family-initial accepted.
    • Батын / Бат (genitive against nominative) conflict: a candidate, not filed.
  • Default:
    • tools/differential/snapshot.py → no reading moves; the snapshot gains one row, O4's new example read by default.
    • snapshot.py --local <each> --base <master 2b39f23d checkout> → matches the base for all 16 local corpora (ctgov-officials, cz-candidates, dof-signatures, fec-individuals, govuk-people, jrc-names, nepal-candidates, opensanctions-parliaments (re-run on the refreshed cache), orcid-researchers, patentsview-inventors, psc-individuals, tse-donors, tw-donors, uit-vinames, ukec-individuals, vn-assembly).

Also in this PR

  • tools/differential/cldr_agreement_locales.json gains MN's record, and nothing else moved in the re-record.
  • docs/contributing.rst and decisions/deviates-registry record the inert choice. docs/policy.rst still names only vi as setting FAMILY_FIRST_GIVEN_LAST; that file belongs to another in-flight PR, so it is left alone.
  • Three feat(#1237) case rows. Each was checked against the 1.4.0 and 3.0.1 wheels, and each fails on master (no locales.MN).
  • rules/O4 gains an [mn] example.
  • With the maintainer's approval, the shared ~/.cache/nameparser-tools/local/opensanctions-parliaments was refreshed: it held 17 datasets, which the tools now refuse, and now holds 23 (24,066 names).

Checks

  • uv run pytest with the ja extra: 46,040 passed. Without it: 46,008 passed. Python 3.15 in its own environment: all passed apart from the corpus test the rules-corpus rebuild then fixed.
  • uv run pytest -n0 -m serial: 35 passed.
  • uv run mypy, uv run ruff check, tools/vocab/build.py --check: clean.
  • sphinx-build -b doctest docs, sphinx-build -W -b html docs, python -m doctest README.rst: clean.

Reviews

Each review ran as a subagent in its own detached worktree on the pushed head (b4aee8e), with a read-only master checkout beside it.

Review Found Done
design-docs-reviewer Enkhtaivan Erdene cited as written family-first, though its boxes say given-first. #1237's option 1 misquoted (it named family-first). The JRC "9 of 9" and "Latin and Greek" overstated what the recipe classifies. "Three in four" taken from the wrong population. The spaced-initial and population counts did not reproduce (8, not 6). The traditional-script example had lost its U+202F. All corrected in f0bfd08, with each recipe's filter stated.
code-reviewer Missed roster entry in docs/contributing.rst. The inert choice is not recorded in decisions/deviates-registry. "snapshot unchanged" is literally false (one row added). The given-last order's cost to Russian names is unweighed. Plain-space traditional script is untested. docs/policy.rst names only vi. All fixed except policy.rst, which is another PR's file. The Russian-name cost is now Decision 3 above.
comment-analyzer The glued initial is divided by script_segment (rules/T8), not tokenize. The U+202F example. contributing.rst. A wrong NFD test comment. mn.py claimed a "gate re-take" the entry did not record. "yet" in locales.rst. A misordered clause in languages.rst, with a romanized example that was not the Cyrillic one's transliteration. The strict-mode comment and the DEVIATES "so" were imprecise. All fixed.
pr-test-analyzer No critical gaps; mutations confirm each test bites. The strict choice was untested. Readings the entry states were unpinned. The non-interference gate is vacuous for an all-declaring pack, as it already was for VI. Added a strict test, a table of the entry's stated readings, and seven more default-equal comma and title rows. The vacuous gate is VI's shape and left as is.
code-reviewer, round 2 Run on the fix commit f0bfd08. One fresh error: the new Russian-name bullet blamed O1 for the empty patronymic view, but O1 stands down under either declared order; the cause is that R9's East Slavic rule reads only a middle word, and the given-last order puts the patronymic in given. Everything else re-verified (ORCID and JRC counts over the stated filters, the U+202F, T8's division, the snapshot row, the doctest and prose, the new tests' mutations).

🤖 Generated with Claude Code

derek73 and others added 2 commits November 12, 2026 12:25
…ily (#1237)

A plain choice of mn with mn.toml's [rules] order, family first with the
given name last, so the father's name a Mongolian name writes first is
the family, as #1253's decision states. The genitive is not read into
the patronymic view, and the choice of mn changes no reading. The
default does not move.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hoice

From review: the glued initial is divided by script_segment (rules/T8),
not tokenize; the traditional-script example holds its U+202F; the JRC
and ORCID figures name the population each recipe classifies; the
Russian-name cost of the given-last order is recorded; contributing.rst
and decisions/deviates-registry name the inert choice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@derek73 derek73 added this to the 3.1 milestone Nov 12, 2026
@derek73 derek73 added enhancement non-latin-script Names written in a script other than Latin (rules/W) locale-packs Locale packs and language choices (nameparser.locales) name-order Name order, family-first and two family names (rules/O) labels Nov 12, 2026
@derek73 derek73 self-assigned this Nov 12, 2026
From the round-2 review of f0bfd08: O1 stands down under either
declared order; the view is empty because R9's East Slavic rule reads a
middle word, and FAMILY_FIRST_GIVEN_LAST puts the patronymic in given.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
derek73 and others added 3 commits November 12, 2026 13:13
…he in suggest_measure

local_parliaments.DATASETS gains mn_parliament (126 members, each in
Cyrillic, father's name first), so #1237's pack is measured from the
cache. suggest_measure.py read each local corpus's raw.jsonl directly,
which skipped local_corpora.load's checks and kept running on a
parliaments cache fetched from fewer datasets; it now reads through
load, and a stale or edited corpus stops the run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…script that settles none (#1237)

Decided in coordination: the pack reads a name written wholly in
Cyrillic family first with the given name last, and leaves a romanized
name to the caller's order, since Mongolians mostly write their names
given-first in Latin letters (65 of 87 two-word ORCID credits).

Script.CYRILLIC joins the enum and the script table; the readers that
mean the East Asian scripts (rules/T3's interpunct flank, the
two-family-names run count, the segmenter's neighbour test) read
_EAST_ASIAN_SCRIPTS, so a parser without the pack reads every name as
before (the snapshot and the 16 local corpora match master). A Cyrillic
word with a hyphen or apostrophe inside it classifies too.
PolicyPatch.script_orders composes by script, so the pack adds its entry
without erasing a caller's own; parser_for warns where two packs set one
script differently. The entry orders a name and settles no lone word
(rules/O5). rules/W4 gains the pack's Except, decisions/W4 the entry,
and root AGENTS.md says Cyrillic does not mean family-first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement locale-packs Locale packs and language choices (nameparser.locales) name-order Name order, family-first and two family names (rules/O) non-latin-script Names written in a script other than Latin (rules/W)

Projects

None yet

1 participant