Skip to main content

Ethiopian/Eritrean name intelligence: parsing, transliteration, spelling variants, and patronymic-aware fuzzy matching.

Project description

habesha-names

Ethiopian/Eritrean name intelligence for Python: fidel script handling, transliteration, spelling-variant generation, name parsing, and patronymic-aware fuzzy matching.

Status

Current release: 0.1.0. The linguistic defaults (practical transliteration rules) and the bundled name lexicon are native-speaker verified (see Data verification). The golden test corpus is mechanically generated and partially curated. The public API is stable within the 0.1.x series.

  • Zero runtime dependencies — stdlib only
  • Deterministic and explainable — no ML at runtime, no network calls; every match score ships an explanation object. Built for KYC/AML, remittance, HR, and entity-resolution pipelines.
  • Fully typed (py.typed, mypy strict)

Why

Habesha names break global identity systems:

  1. No family names. A full name is given name + father's given name (+ grandfather's). "First/Last name" fields are semantically wrong.
  2. No standard romanization. ጸሐይ → Tsehay / Tsehai / Sehay / Tzehay — same person, four database records.
  3. Compound given names. "Haile Mariam" can be ONE given name (Hailemariam) or given + patronym.
  4. Abbreviation conventions. Gebremedhin → G/Medhin, G.Medhin, Gebre Medhin — all common in official documents.
  5. Fidel homophones. ሀ/ሐ/ኀ, ሰ/ሠ, ጸ/ፀ, አ/ዐ are pronounced identically; spelling varies by writer.

Install

pip install habesha-names

(From a checkout: pip install -e .)

Quick tour

Every snippet below is a doctest and runs in CI.

Parse — name structure, not first/last fields

>>> from habesha_names import parse
>>> p = parse("ወይዘሮ ጸሐይ ገብረመድህን")
>>> (p.title, p.given, p.patronym)
('Weizero', 'ጸሀይ', 'ገብረመድህን')
>>> p.script
'ethiopic'
>>> parse("Hailemariam Desalegn").given_is_compound
True
>>> parse("G/Medhin Tesfaye").given      # slash abbreviation expanded
'Gebremedhin'
>>> parse("Bikila, Abebe").given         # comma inversion handled
'Abebe'

Variants — the spellings your database actually contains

>>> from habesha_names import variants
>>> variants("ጸሐይ", n=6)
['Tsehay', 'Sehay', 'Tsehai', 'Tzehay', 'Tsehaye', 'Sehai']
>>> variants("Gebremedhin", n=5)
['Gebremedhin', 'Gebre Medhin', 'Gebre-Medhin', 'G/Medhin', 'G.Medhin']

Match — patronymic-aware fuzzy matching

>>> from habesha_names import match
>>> match("Ato Abebe Bikila", "abebe bikila") >= 0.85
True
>>> round(float(match("Tesfay Mohamed", "Tesfaye Muhammed")), 2)
0.94
>>> match("Abebe Bikila", "Bikila Abebe").swapped   # field swap tolerated
True
>>> match("Abebe Bikila", "Almaz Tesfahun") <= 0.6
True

Score interpretation

Match scores are calibrated to three bands:

score reading
≥ 0.85 likely the same person
0.60 – 0.85 review zone — route to an analyst
≤ 0.60 likely different people

The middle band is intentional, not indecision: records like "Tesfaye Girma" vs "Tesfahun Girma" (siblings — different given name, shared patronym) score there by design, because in KYC/AML pipelines a shared patronym is exactly the kind of near-match a human should see.

Explainability — every score can be justified

>>> result = match("ወይዘሮ ጸሐይ ገብረመድህን", "Tsehay G/Medhin")
>>> result.score
1.0
>>> [(pair.token_a, pair.token_b, pair.method) for pair in result.pairs]
[('ጸሀይ', 'Tsehay', 'exact'), ('ገብረመድህን', 'Gebremedhin', 'exact')]
>>> for note in result.notes:
...     print(note)
a: patronym 'ገብረመድህን' is a joined compound (Gebre + Medhin)
b: abbreviation 'G/Medhin' expanded with top candidate 'Gebre' (candidates: Gebre (0.8), Girma (0.2))

Normalize — fidel homophones collapse before comparison

>>> from habesha_names import normalize
>>> normalize("ፀሐይ")                    # ፀ→ጸ, ሐ→ሀ
'ጸሀይ'
>>> normalize("ፀሐይ") == normalize("ጸሀይ")
True

Transliterate — practical romanization, no diacritics

>>> from habesha_names import transliterate
>>> transliterate("ተስፋዬ")
'Tesfaye'
>>> transliterate("ገብረመድህን")
'Gebremedhin'
>>> transliterate("ፀሐይ") == transliterate("ጸሀይ") == "Tsehay"
True

Building blocks

>>> from habesha_names import is_ethiopic, phonetic_key
>>> is_ethiopic("ተስፋዬ")
True
>>> phonetic_key("Tsehay") == phonetic_key("Sehai")   # HabeshaKey
True

Public API

from habesha_names import (
    parse, match, variants, transliterate,
    normalize, phonetic_key, is_ethiopic,
)

Everything else is internal. Reverse transliteration (to_fidel) and gender inference (guess_gender) are planned for v0.2.

Known limitations

  • Bekele ↔ Bikila score 0.90 — the phonetic key's single first-vowel class slot folds these two distinct names together. A richer vowel representation is planned for v0.2; until then this pair is a recorded known_fail in the golden corpus.
  • Spelling rewrites inside spaced compound forms can misalign against the joined form (e.g. "Gebrie Medhin" vs "Gebremedhin"), also recorded as known_fail corpus entries.

Data verification

The bundled lexicons (given names, titles, compound elements) and the practical transliteration rules passed native-speaker review in July 2026 and are flagged "verified": true; any newly added entry starts false again until reviewed. The golden test corpus remains mechanically generated (needs_human markers) pending human curation, and tuning constants are accepted for 0.1.0 as-is — to be revisited against a human-curated corpus.

Development

python -m venv .venv
.venv\Scripts\activate.bat
pip install -e .[dev]
check.bat        # pytest -q && ruff check . && mypy src --strict

See ARCHITECTURE.md for the design and CHANGELOG.md for release history.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

habesha_names-0.1.0.tar.gz (82.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

habesha_names-0.1.0-py3-none-any.whl (52.7 kB view details)

Uploaded Python 3

File details

Details for the file habesha_names-0.1.0.tar.gz.

File metadata

  • Download URL: habesha_names-0.1.0.tar.gz
  • Upload date:
  • Size: 82.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for habesha_names-0.1.0.tar.gz
Algorithm Hash digest
SHA256 123e63aceca1547658ae65e903a08b89439ebb69c65601cf2f7149cd57501d77
MD5 ac5eb23c9da1f19dfca30afdd883ba7f
BLAKE2b-256 907db04b7aa95c0273098e895118d14e0bb00427e4e4b328b7a27da9ca0cc808

See more details on using hashes here.

Provenance

The following attestation bundles were made for habesha_names-0.1.0.tar.gz:

Publisher: release.yml on Robel231/habesha-names

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file habesha_names-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: habesha_names-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 52.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for habesha_names-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 610ab2ef06e4b9dea9c02e099e09215a7f9c4cd78237475a68b3834d24758071
MD5 3e0cff0259f6fd2537f9595f03ea71e5
BLAKE2b-256 2a1c7928ff41e08cba2c08209130ae12018f3d8a64aec51b8f7187d5e76b098d

See more details on using hashes here.

Provenance

The following attestation bundles were made for habesha_names-0.1.0-py3-none-any.whl:

Publisher: release.yml on Robel231/habesha-names

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page