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.
Alpha status
Current release: 0.1.0a1 (alpha). The practical transliteration
defaults and the bundled name lexicon are agent-seeded and pending
native-speaker verification — every linguistic default ships flagged
"verified": false (see Data verification).
Concretely, that means match scores and variant outputs may change in
0.1.0 final as defaults are reviewed, and the API is not yet
frozen. Suitable for evaluation and integration prototyping; pin the
exact version if you depend on today's scores.
- 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:
- No family names. A full name is given name + father's given name (+ grandfather's). "First/Last name" fields are semantically wrong.
- No standard romanization. ጸሐይ → Tsehay / Tsehai / Sehay / Tzehay — same person, four database records.
- Compound given names. "Haile Mariam" can be ONE given name (Hailemariam) or given + patronym.
- Abbreviation conventions. Gebremedhin → G/Medhin, G.Medhin, Gebre Medhin — all common in official documents.
- Fidel homophones. ሀ/ሐ/ኀ, ሰ/ሠ, ጸ/ፀ, አ/ዐ are pronounced identically; spelling varies by writer.
Install
pip install --pre habesha-names
(Only alpha releases exist so far, so pip needs --pre; plain
pip install habesha-names will work once 0.1.0 final is out.
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)
('Woizero', 'ጸሀይ', 'ገብረመድህን')
>>> 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
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.
Data verification
All bundled linguistic data (lexicons, transliteration tables, variant
rules, golden test pairs) was seeded programmatically or by a non-native
speaker and ships flagged "verified": false until it passes
native-speaker review. Match scores are deterministic and explainable,
but treat linguistic defaults as provisional until 0.1.0 final.
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file habesha_names-0.1.0a1.tar.gz.
File metadata
- Download URL: habesha_names-0.1.0a1.tar.gz
- Upload date:
- Size: 77.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aedda0f0a35805270be80449c3a2b58a1e8ba6c587818314fbed14becf9298f3
|
|
| MD5 |
de8d6b0b46bb1247b7d1397f1cba0f27
|
|
| BLAKE2b-256 |
9da392257f5a27f00c2e85a52b11c989b9c63dcf0e5ccea00fe99d77fe31dd86
|
Provenance
The following attestation bundles were made for habesha_names-0.1.0a1.tar.gz:
Publisher:
release.yml on Robel231/habesha-names
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
habesha_names-0.1.0a1.tar.gz -
Subject digest:
aedda0f0a35805270be80449c3a2b58a1e8ba6c587818314fbed14becf9298f3 - Sigstore transparency entry: 2164425805
- Sigstore integration time:
-
Permalink:
Robel231/habesha-names@cf6464454cdae56680ef89e82fa07db1d796062c -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/Robel231
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@cf6464454cdae56680ef89e82fa07db1d796062c -
Trigger Event:
push
-
Statement type:
File details
Details for the file habesha_names-0.1.0a1-py3-none-any.whl.
File metadata
- Download URL: habesha_names-0.1.0a1-py3-none-any.whl
- Upload date:
- Size: 50.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
063ab7ba879845056398ef712a953db172ce45730aa34133155d616af5a98369
|
|
| MD5 |
09b7f4304f76c5b41e393c757951f05a
|
|
| BLAKE2b-256 |
5e15829d23a5c49a63d3410455abcfdb0261d3b7ae1b4e176048dfe8ec705f6f
|
Provenance
The following attestation bundles were made for habesha_names-0.1.0a1-py3-none-any.whl:
Publisher:
release.yml on Robel231/habesha-names
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
habesha_names-0.1.0a1-py3-none-any.whl -
Subject digest:
063ab7ba879845056398ef712a953db172ce45730aa34133155d616af5a98369 - Sigstore transparency entry: 2164425822
- Sigstore integration time:
-
Permalink:
Robel231/habesha-names@cf6464454cdae56680ef89e82fa07db1d796062c -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/Robel231
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@cf6464454cdae56680ef89e82fa07db1d796062c -
Trigger Event:
push
-
Statement type: