This release is a pre-release and may not be stable for production use.
espyak
A pure-Python reimplementation of espeak-ng's grapheme-to-phoneme (G2P) front-end. Text → phonemes only: no synthesis, no audio, no C extension, no runtime dependencies.
Reproduces the
espeak-ngbinary (pinned 1.52.0) byte-for-byte. The full-headword parity audit (every*_listword,force_compatmode) is 99.71% across 105 languages — 59 at 100% — and 99.69% across 22 sub-dialect variants (pt-br, en-us, es-419, …). 117 languages bundled. The remaining fails are catalogued indocs/remaining-gaps.md.
from espyak import G2P
g2p = G2P("en")
g2p.phonemize("hello world") # 'həlˈəʊ wˈɜːld' (IPA)
g2p.phonemize("hello world", ipa=False) # "h@l'oU w'3:ld" (Kirshenbaum / -x)
G2P("es").phonemize("buenos días") # 'bwˈenos dˈias'
G2P("de").phonemize("straße") # 'ʃtɾˈɑːsə'
G2P("ru").phonemize("привет") # 'prʲivʲˈet'
Why
espyak gives projects espeak-ng's phonemes without the native dependency: nothing to
shell out to, no C-extension to build, and the rules are readable and patchable in Python.
It drops in as a backend for phoonnx.
Install
pip install -e . # from a clone (the espeak-ng source data is bundled, ~44 MB)
# or: uv pip install -e .
Python ≥ 3.9. The espeak-ng dictsource/, phsource/, and lang/ data are bundled under
espyak/data/ at the pinned 1.52.0 tag, so nothing is needed system-wide.
Usage
Python
from espyak import G2P
g2p = G2P("en") # one translator per language — construct once, reuse
g2p.phonemize("read") # 'ɹˈiːd'
g2p.phonemize("2024 dogs") # numbers expand to words, then phonemes
g2p.phonemize("cat", ipa=True) # 'kˈat' — Unicode IPA (default)
g2p.phonemize("cat", ipa=False) # "k'at" — Kirshenbaum ASCII (espeak -x)
g2p.phonemize("cat", separator="_") # 'k_ˈa_t' — separate phonemes
g2p.phonemize("cat", tie="͡") # tie multi-char phoneme names
Dialects / sub-dialect variants
A variant code (pt-br, en-us, es-419, fr-be, …) is loaded the same way — pass it to
G2P. espeak-ng models a sub-dialect as a small voice file that layers over the shared
base language (its dictionary, rules and translator config), overriding only the phoneme
table, the dictionary conditionals, and a few post-translation phoneme substitutions:
G2P("pt-br").phonemize("dia") # 'dʒˈiæ' — Brazilian di palatalization
G2P("pt").phonemize("dia") # 'dˈiɐ' — base (European) Portuguese
G2P("en-us").phonemize("better") # 'bˈɛɾɚ' — rhotic, the en-us replace table
G2P("es-419").phonemize("cielo") # 'sjˈelo' — seseo (θ→s); base es → 'θjˈelo'
The 22 supported variants (codes are case-insensitive):
| family | variants |
|---|---|
| en | en-us, en-us-nyc, en-gb-scotland, en-gb-x-rp, en-gb-x-gbclan, en-gb-x-gbcwmd, en-029, en-shaw |
| ca | ca-va, ca-ba, ca-nw |
| fr | fr-be, fr-ch |
| pt / es | pt-br, es-419 |
| ru | ru-cl, ru-lv |
| vi | vi-vn-x-central, vi-vn-x-south |
| cmn / yue / fa | cmn-latn-pinyin, yue-latn-jyutping, fa-latn |
Command line
espyak -v en "hello world" # həlˈəʊ wˈɜːld
espyak -v es "díganme" # dˈiɣanme
espyak -v fr -x "bonjour" # bO~Z'ur (Kirshenbaum)
espyak -v de --sep _ "haus" # h_ˈaʊ_s
echo "привет" | espyak -v ru - # read from stdin
Output formats
| API argument | CLI flag | effect |
|---|---|---|
| (default) | --ipa |
Unicode IPA with ˈ/ˌ stress |
ipa=False |
-x |
Kirshenbaum ASCII |
separator="_" |
--sep=_ |
insert a separator between phonemes |
tie="͡" |
--tie |
tie character within multi-char names |
G2P(lang).phonemize(text, ipa=True, tie=None, separator=None) is the whole surface; see
docs/usage.md for details and render() (raw phoneme-string rendering).
How it works
espyak parses espeak-ng's own source data at load time and replays its pipeline in Python:
text → dictionary _list lookup → prefix/suffix retranslation → letter-to-sound rules
→ SetWordStress → phoneme programs (ChangePhoneme/InsertPhoneme) → render (IPA / -x)
Fidelity is inherited from the bundled data; the matcher, stress, number, and
phoneme-program logic are re-implemented to match the binary, espeak-ng's quirks included.
docs/architecture.md has the module map and pipeline.
Verification
pytest -q # unit + fixture tests
python test/sweep.py 25 # per-language _list-headword sweep vs the oracle
python test/corpus_sweep.py # real-sentence corpus vs the oracle
python test/parity_audit.py --cap 2000 # full-headword parity, every language
python test/parity_audit.py --variants --cap 2000 # the same for the 22 sub-dialect variants
The reference ("oracle") is a pinned espeak-ng 1.52.0 build, used only to generate
expected outputs — espyak never calls it at runtime. Every dictionary *_list headword
is a free test case. parity_audit.py tests every headword in force_compat mode (the
edge cases — single accented letters, abbreviations, codepoint names — are where parity
breaks) and writes a per-language table plus a JSONL of every mismatch; --variants audits
each dialect's base headwords through its voice layer. test/report.md holds the
per-language sweep pass rate.
Coverage
Full-headword parity is 99.71% (105 languages, --cap 2000) and 99.69% across the
22 variants, with 59 base languages at 100%. The ~136 remaining base fails are concentrated
in phoneme-level language switches, letter-name/abbreviation spelling (LookupDictList),
unported numbers.c branches, and a small irreducible floor (formant-synthesis allophones,
the ru a/ɑ reduction, oracle self-inconsistencies). They are catalogued, classified,
and prioritized in docs/remaining-gaps.md. The default engine
(force_compat=False) is the linguistically-correct G2P and may deviate from espeak-ng only
at the entries in docs/divergences.md.
Project layout
espyak/ the engine (one module per espeak-ng translation unit)
api.py public G2P entry point
dictionary.py MatchRule / TranslateRules / SetWordStress / LookupDict2
rule_compiler.py compiledict.c — rule byte encoding + groups
phoneme_tab.py phsource loader; phoneme_program.py — ChangePhoneme/InsertPhoneme
language_data.py per-language translator config (tr_languages.c + voice files)
voice.py sub-dialect VARIANT loader (voices.c LoadVoice) — pt-br/en-us/es-419/…
numbers.py TranslateNumber + ordinals/fractions
render.py phoneme list → IPA / Kirshenbaum / stress / tie / separator
data/ bundled espeak-ng dictsource/ phsource/ lang/ @ 1.52.0
docs/ architecture, usage, divergences, remaining-gaps, code-review/
examples/ runnable usage examples
test/ unit tests, oracle fixtures, sweep + corpus + parity_audit harnesses
Provenance
espyak is an AI-assisted port. The Python was written by an AI coding assistant that
read and instrumented espeak-ng's C source; human review has been minimal. It is not an
independent clean-room implementation.
License
espyak is GPL-3.0-or-later, the same as espeak-ng — from which it is derived and whose
data it bundles under espyak/data/. See LICENSE.
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 espyak-0.0.3a1.tar.gz.
File metadata
- Download URL: espyak-0.0.3a1.tar.gz
- Upload date:
- Size: 11.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6527f9871aeeaf4376386a6c7fd64494baa8a0ac63b1809ba03d1f4bcb5010a4
|
|
| MD5 |
94774464a44d5f341cd8fd02d09ee7cd
|
|
| BLAKE2b-256 |
89b0bc05d83d4c3cdfe41b19416ac148e4a85f27387dc8b9fba2b74b3205ea8e
|
File details
Details for the file espyak-0.0.3a1-py3-none-any.whl.
File metadata
- Download URL: espyak-0.0.3a1-py3-none-any.whl
- Upload date:
- Size: 12.2 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1774b8c85dec3c58ee37971d8c00323b78dbd4e29388787c70ec55c2738354d6
|
|
| MD5 |
4c6e3f49db43560ad964a40b0cebc7cc
|
|
| BLAKE2b-256 |
9445659536f4d03dd185658b1f40e562c4eee25bfedf8babdc282b164f44fd46
|