arbtok 0.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arbtok-0.0.0/PKG-INFO +342 -0
- arbtok-0.0.0/README.md +305 -0
- arbtok-0.0.0/arbtok/__init__.py +24 -0
- arbtok-0.0.0/arbtok/_ensemble.py +164 -0
- arbtok-0.0.0/arbtok/arabizi.py +285 -0
- arbtok-0.0.0/arbtok/constants.py +87 -0
- arbtok-0.0.0/arbtok/data/donor_lexicons/en-GB.tsv +74318 -0
- arbtok-0.0.0/arbtok/data/lexicons/ar-DZ.tsv +12 -0
- arbtok-0.0.0/arbtok/data/lexicons/ar-EG.tsv +36 -0
- arbtok-0.0.0/arbtok/data/lexicons/ar-IQ.tsv +30 -0
- arbtok-0.0.0/arbtok/data/lexicons/ar-MA.tsv +12 -0
- arbtok-0.0.0/arbtok/data/lexicons/ar-SA-x-najd.tsv +30 -0
- arbtok-0.0.0/arbtok/data/lexicons/ar-SY.tsv +34 -0
- arbtok-0.0.0/arbtok/data/lexicons/ar-TN.tsv +17 -0
- arbtok-0.0.0/arbtok/data/lexicons/ar-x-maghrebi.tsv +40 -0
- arbtok-0.0.0/arbtok/diacritize.py +372 -0
- arbtok-0.0.0/arbtok/dialect_lexicon.py +159 -0
- arbtok-0.0.0/arbtok/dialects.py +358 -0
- arbtok-0.0.0/arbtok/donor_lexicon.py +74 -0
- arbtok-0.0.0/arbtok/espeak_wrapper.py +237 -0
- arbtok-0.0.0/arbtok/fusion.py +452 -0
- arbtok-0.0.0/arbtok/lattice.py +551 -0
- arbtok-0.0.0/arbtok/lexicon.py +132 -0
- arbtok-0.0.0/arbtok/models/rawi_ensemble.logits.int8.onnx +0 -0
- arbtok-0.0.0/arbtok/models/rawi_v2.vocab.json +1 -0
- arbtok-0.0.0/arbtok/nisba.py +113 -0
- arbtok-0.0.0/arbtok/o2i_plugins.py +171 -0
- arbtok-0.0.0/arbtok/orthography.py +199 -0
- arbtok-0.0.0/arbtok/plugin.py +409 -0
- arbtok-0.0.0/arbtok/sandhi.py +345 -0
- arbtok-0.0.0/arbtok/stress.py +95 -0
- arbtok-0.0.0/arbtok/tashkeel.py +51 -0
- arbtok-0.0.0/arbtok/tokenizer.py +736 -0
- arbtok-0.0.0/arbtok/translit.py +633 -0
- arbtok-0.0.0/arbtok/unicode_symbol2label.py +4173 -0
- arbtok-0.0.0/arbtok/util.py +818 -0
- arbtok-0.0.0/arbtok/version.py +10 -0
- arbtok-0.0.0/arbtok/waqf.py +192 -0
- arbtok-0.0.0/arbtok.egg-info/PKG-INFO +342 -0
- arbtok-0.0.0/arbtok.egg-info/SOURCES.txt +81 -0
- arbtok-0.0.0/arbtok.egg-info/dependency_links.txt +1 -0
- arbtok-0.0.0/arbtok.egg-info/entry_points.txt +8 -0
- arbtok-0.0.0/arbtok.egg-info/requires.txt +15 -0
- arbtok-0.0.0/arbtok.egg-info/top_level.txt +1 -0
- arbtok-0.0.0/pyproject.toml +56 -0
- arbtok-0.0.0/requirements.txt +17 -0
- arbtok-0.0.0/setup.cfg +4 -0
- arbtok-0.0.0/tests/test_arabizi.py +175 -0
- arbtok-0.0.0/tests/test_arabizi_digit_gutturals.py +80 -0
- arbtok-0.0.0/tests/test_diacritize.py +131 -0
- arbtok-0.0.0/tests/test_diacritize_long_vowel_glottal.py +89 -0
- arbtok-0.0.0/tests/test_dialect_lexicon.py +165 -0
- arbtok-0.0.0/tests/test_digit_leak.py +96 -0
- arbtok-0.0.0/tests/test_donor_lexicon.py +70 -0
- arbtok-0.0.0/tests/test_donor_lexicon_rhotic.py +112 -0
- arbtok-0.0.0/tests/test_established_loans.py +77 -0
- arbtok-0.0.0/tests/test_fusion.py +216 -0
- arbtok-0.0.0/tests/test_gold_code_switched.py +71 -0
- arbtok-0.0.0/tests/test_grouped_thousands.py +31 -0
- arbtok-0.0.0/tests/test_ipa_fuzzy.py +274 -0
- arbtok-0.0.0/tests/test_iqraeval_benchmark.py +107 -0
- arbtok-0.0.0/tests/test_lattice.py +305 -0
- arbtok-0.0.0/tests/test_lect_benchmark.py +95 -0
- arbtok-0.0.0/tests/test_lect_state_isolation.py +66 -0
- arbtok-0.0.0/tests/test_lexicon.py +135 -0
- arbtok-0.0.0/tests/test_mixed_script_token.py +86 -0
- arbtok-0.0.0/tests/test_msa_pausal.py +559 -0
- arbtok-0.0.0/tests/test_nisba.py +55 -0
- arbtok-0.0.0/tests/test_normalization.py +53 -0
- arbtok-0.0.0/tests/test_numerals.py +220 -0
- arbtok-0.0.0/tests/test_o2i_plugins.py +72 -0
- arbtok-0.0.0/tests/test_pipeline_order.py +68 -0
- arbtok-0.0.0/tests/test_plugin.py +109 -0
- arbtok-0.0.0/tests/test_plugin_imports.py +31 -0
- arbtok-0.0.0/tests/test_spec_dialects.py +198 -0
- arbtok-0.0.0/tests/test_stress.py +132 -0
- arbtok-0.0.0/tests/test_supported_lects.py +42 -0
- arbtok-0.0.0/tests/test_tatweel.py +58 -0
- arbtok-0.0.0/tests/test_tokens.py +298 -0
- arbtok-0.0.0/tests/test_translit.py +225 -0
- arbtok-0.0.0/tests/test_translit_projection.py +153 -0
- arbtok-0.0.0/tests/test_vocalized_gap.py +204 -0
- arbtok-0.0.0/tests/test_word_boundary_and_shadda.py +80 -0
arbtok-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arbtok
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Rule-based Arabic (MSA) text-to-IPA with tashkeel diacritization — an orthography2ipa G2P plugin
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Project-URL: Homepage, https://github.com/TigreGotico/arbtok
|
|
7
|
+
Project-URL: Issues, https://github.com/TigreGotico/arbtok/issues
|
|
8
|
+
Keywords: arabic,ipa,g2p,phonemizer,tashkeel,diacritization,tts
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: numpy
|
|
24
|
+
Requires-Dist: onnxruntime
|
|
25
|
+
Requires-Dist: quebra-frases
|
|
26
|
+
Requires-Dist: langcodes
|
|
27
|
+
Requires-Dist: ovos-number-parser>=0.18.5a1
|
|
28
|
+
Requires-Dist: ovos-date-parser>=0.6.4a1
|
|
29
|
+
Requires-Dist: ovos-utils>=0.15.1a1
|
|
30
|
+
Requires-Dist: ovos-config>=3.5.1a1
|
|
31
|
+
Requires-Dist: orthography2ipa>=7.95.1a1
|
|
32
|
+
Requires-Dist: huggingface_hub
|
|
33
|
+
Provides-Extra: test
|
|
34
|
+
Requires-Dist: pytest; extra == "test"
|
|
35
|
+
Requires-Dist: pytest-timeout; extra == "test"
|
|
36
|
+
Requires-Dist: jiwer; extra == "test"
|
|
37
|
+
|
|
38
|
+
# arbtok
|
|
39
|
+
|
|
40
|
+
**Arabic text→IPA** with **dialect-aware tashkeel** diacritization, a
|
|
41
|
+
self-contained Arabic engine built on
|
|
42
|
+
[orthography2ipa](https://github.com/TigreGotico/orthography2ipa), covering MSA,
|
|
43
|
+
Classical, and 30+ regional varieties.
|
|
44
|
+
|
|
45
|
+
## What arbtok adds over orthography2ipa
|
|
46
|
+
|
|
47
|
+
[orthography2ipa](https://github.com/TigreGotico/orthography2ipa) (o2i) is a
|
|
48
|
+
language-agnostic grapheme→IPA lattice engine. For Arabic it assumes
|
|
49
|
+
**fully-vocalized** input: given the tashkeel, it transcribes accurately, but
|
|
50
|
+
real Arabic text is written **without** the short vowels, and o2i cannot invent
|
|
51
|
+
them, a bare skeleton transcribes incompletely wherever a vowel or gemination is
|
|
52
|
+
unwritten. That is the gap arbtok exists to close.
|
|
53
|
+
|
|
54
|
+
arbtok sits on o2i's lattice and adds the layer o2i deliberately leaves out:
|
|
55
|
+
|
|
56
|
+
- **A bundled neural diacritizer (rawi), extracted from
|
|
57
|
+
[text2tashkeel](https://github.com/TigreGotico/text2tashkeel).** text2tashkeel
|
|
58
|
+
is a diacritization library with a family of ONNX models. Arbtok takes its
|
|
59
|
+
**rawi** ensemble and stitches it into a single 4.9 MB logits ONNX baked into
|
|
60
|
+
the wheel (`arbtok/_ensemble.py`, built by
|
|
61
|
+
`tools/build_ensemble_logits_onnx.py`). There is **no runtime dependency on
|
|
62
|
+
text2tashkeel** and no network: the model rides inside the package. This is
|
|
63
|
+
what lets arbtok read the undiacritized text a person actually types.
|
|
64
|
+
- **Dialect-aware fusion, cross-word sandhi, loanword nativization, and a waqf
|
|
65
|
+
register switch**: the sentence-level, variety-specific phonology below.
|
|
66
|
+
|
|
67
|
+
The split shows up directly in the numbers. On the `arabic-dialects-gold20` set,
|
|
68
|
+
scored on the **undiacritized** `raw` skeleton (each lect at its own register:
|
|
69
|
+
full iʿrāb for MSA/Classical, pausal for the spoken varieties, using
|
|
70
|
+
`scripts/benchmark_gold20.py --undiac`), arbtok roughly **halves** o2i's error and
|
|
71
|
+
beats every dialect, while o2i-on-a-skeleton is barely better than espeak-ng:
|
|
72
|
+
|
|
73
|
+
| system | mean PER (stress-stripped) | MSA `ar` | Classical `arb` |
|
|
74
|
+
|---|---|---|---|
|
|
75
|
+
| **arbtok** (diacritizer on) | **0.147** | **0.076** | **0.055** |
|
|
76
|
+
| orthography2ipa (bare) | 0.302 | 0.363 | 0.444 |
|
|
77
|
+
| espeak-ng | 0.308 | 0.345 | 0.314 |
|
|
78
|
+
|
|
79
|
+
On the *vocalized* form of the same gold, arbtok's diacritizer is idle and it
|
|
80
|
+
simply matches o2i (that set does not exercise the layer). The undiacritized
|
|
81
|
+
per-dialect table is the benchmark that actually measures arbtok.
|
|
82
|
+
|
|
83
|
+
## Dialect-aware tashkeel
|
|
84
|
+
|
|
85
|
+
To our knowledge arbtok is the **only Arabic phonemizer whose diacritization is
|
|
86
|
+
dialect-aware**. Every other pipeline runs an MSA-trained diacritizer and then
|
|
87
|
+
phonemizes whatever it wrote. Arbtok turns that pipeline around. The bundled
|
|
88
|
+
rawi neural ensemble, extracted from
|
|
89
|
+
[text2tashkeel](https://github.com/TigreGotico/text2tashkeel) and stitched into a
|
|
90
|
+
4.9 MB ONNX inside the wheel (no network, no external model package), exposes
|
|
91
|
+
its per-character **distribution**, and arbtok
|
|
92
|
+
scores that distribution against **each variety's own phonological licensing**:
|
|
93
|
+
the orthography2ipa grapheme table and allophone rules of the target lect
|
|
94
|
+
(`docs/rawi-fusion.md`). The chosen tashkeel is the model's most probable
|
|
95
|
+
reading *that the dialect's orthography actually admits*, for all 33 supported
|
|
96
|
+
lects, from Najdi and Hejazi to Tunisian, Egyptian, and the qeltu Iraqi of
|
|
97
|
+
Mosul (`docs/dialects.md`).
|
|
98
|
+
|
|
99
|
+
So the same bare sentence receives variety-appropriate marks and IPA:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from arbtok.plugin import ArbtokG2PPlugin
|
|
103
|
+
|
|
104
|
+
bare = "ذهب الولد الى المدرسة" # undiacritized input
|
|
105
|
+
ArbtokG2PPlugin(lang="ar").transcribe(bare) # ˈðahab ˈalwalad ˈalaː ˈlmudrasa
|
|
106
|
+
ArbtokG2PPlugin(lang="ar-TN").transcribe(bare) # ˈðahab ˈalwalad ˈalɛː ˈlmudrasa
|
|
107
|
+
ArbtokG2PPlugin(lang="ar-SA-x-najd").transcribe("يشرب القهوة في البيت")
|
|
108
|
+
# ˈjaʃrab alˈɡahawa ˈfiː ˈlbajt — Najdi /g/ for qāf, epenthetic gahawa vowel
|
|
109
|
+
ArbtokG2PPlugin(lang="ar-TN").transcribe("يشرب القهوة في البيت")
|
|
110
|
+
# ˈjaʃrab alˈqahwa ˈfiː ˈlbiːt — Tunisian monophthong /iː/ in bayt
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Measured on the bare-input TTS gold (33 lects × 20 sentences, mean per-sentence
|
|
114
|
+
phoneme error rate), scoring the ensemble distribution under dialect licensing
|
|
115
|
+
outperforms running the same ensemble as a free generator, with the margin
|
|
116
|
+
concentrated on the lects that diverge most from MSA, the signature of the
|
|
117
|
+
licensing doing the work (`docs/rawi-fusion.md` carries the full table).
|
|
118
|
+
|
|
119
|
+
Three capabilities define the engine:
|
|
120
|
+
|
|
121
|
+
1. **Dialect-aware tashkeel**: the fusion scorer above. It is on by default
|
|
122
|
+
(`fusion=False` opts out), guarded so a human's marks are never overwritten
|
|
123
|
+
and a letter the writing spells is never rewritten.
|
|
124
|
+
2. **Per-lect cited loanword nativization**: code-switched Latin words are
|
|
125
|
+
read out of the *matrix lect's own* inventory, per published loanword
|
|
126
|
+
literature (Cairene `[manaɡar]` vs Najdi `[manadʒar]`, see below).
|
|
127
|
+
3. **Waqf / register policy**: one declared switch between the spoken pausal
|
|
128
|
+
register (the TTS default) and full-iʿrāb recitation (see below).
|
|
129
|
+
|
|
130
|
+
## The lattice underneath
|
|
131
|
+
|
|
132
|
+
Word phonology is built on the **orthography2ipa shared lattice**: the
|
|
133
|
+
language-agnostic grapheme tokenizer (`PhonetokTokenizer`) over the `ar`
|
|
134
|
+
spec grapheme table produces a per-position candidate lattice. The `ar`
|
|
135
|
+
engine handles the segment-local phonology natively, gemination (shadda ّ,
|
|
136
|
+
glides included), lam-alif / presentation ligatures (ﻻ → `laː`), onset
|
|
137
|
+
glides (يَ → `ja`), a hamza carrier's bare /ʔ/ before an explicit harakah, a
|
|
138
|
+
fatḥa + standalone alif maksūra as one long vowel (حَتَّى → `ħattaː`), a
|
|
139
|
+
sukūn-final **coda glide** (ظَبْي → `ðˤabj`, رَمْي → `ramj`, while فِي stays
|
|
140
|
+
`fiː`), and pausal tāʾ marbūṭa. The Arabic morpho-phonology that the shared
|
|
141
|
+
grapheme table cannot express is layered on as composable
|
|
142
|
+
`LatticeRescorer`s (`arbtok/lattice.py`) rather than a private tokenizer
|
|
143
|
+
fork:
|
|
144
|
+
|
|
145
|
+
- **sun-letter assimilation** (idghām ash-shamsiyya): the lām of the
|
|
146
|
+
definite article ⟨ال⟩ assimilates into a following coronal (sun) letter
|
|
147
|
+
(`al-šams` → `aš-šams`). Moon letters keep the lām (`al-qamar`).
|
|
148
|
+
- **hamzat al-waṣl** elision: a word-initial prosthetic alif is silent,
|
|
149
|
+
its harakah carrying the vowel (`istiqbāl`).
|
|
150
|
+
- **accusative-alif** silencing after tanwīn al-fatḥ (`marħaban`), and the
|
|
151
|
+
bare glottal stop of a hamza carrier before a sukūn or word edge
|
|
152
|
+
(`taʔθīr`).
|
|
153
|
+
|
|
154
|
+
Emphatic (pharyngealization) spreading rides on the `ar` spec's own B8
|
|
155
|
+
`allophone_rules`. Cross-word sandhi is orthogonal to the word lattice: clitic
|
|
156
|
+
joining, cross-word waṣl elision, tanwīn pausal forms, tāʾ marbūṭa, and
|
|
157
|
+
idgham/iqlab nasal assimilation all live in the sentence-level orchestration,
|
|
158
|
+
exposed to plain orthography2ipa through its sandhi plugin hook
|
|
159
|
+
(`arbtok/o2i_plugins.py`). Bare (undiacritized) text is diacritized first by
|
|
160
|
+
the bundled rawi ensemble (the dialect-aware fusion path above), entirely
|
|
161
|
+
inside the wheel.
|
|
162
|
+
|
|
163
|
+
> Honesty note: the gold IPA reference set was LLM-generated and has not been
|
|
164
|
+
> validated by a native MSA speaker. If you speak MSA, pull requests are very
|
|
165
|
+
> welcome.
|
|
166
|
+
|
|
167
|
+
## Installation
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
pip install arbtok
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Usage
|
|
174
|
+
|
|
175
|
+
arbtok is built **on** [orthography2ipa](https://github.com/TigreGotico/orthography2ipa)
|
|
176
|
+
(spec data and the shared `G2PPlugin`/`WordContext` base types) and owns the
|
|
177
|
+
Arabic pipeline. orthography2ipa stays the language-agnostic base library.
|
|
178
|
+
|
|
179
|
+
### Engine class
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from arbtok.tokenizer import Sentence
|
|
183
|
+
|
|
184
|
+
Sentence("اَلسَّلَامُ عَلَيْكُمْ").ipa
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
An isolated MSA word transcribes on the shared lattice directly:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from arbtok.lattice import word_ipa
|
|
191
|
+
|
|
192
|
+
word_ipa("الشَّمْس") # 'aʃˈʃams' — sun-letter assimilation as a rescorer
|
|
193
|
+
word_ipa("الْقَمَر") # 'ˈalqamar' — moon-letter control (lām kept)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Bare text is handled by diacritizing first:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from arbtok.plugin import ArbtokG2PPlugin
|
|
200
|
+
|
|
201
|
+
plugin = ArbtokG2PPlugin()
|
|
202
|
+
plugin.transcribe("كتاب جميل") # auto-tashkeel + IPA
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Varieties
|
|
206
|
+
|
|
207
|
+
Pass a spec code as `lang=` to phonemize a variety. `arbtok.supported_lects()`
|
|
208
|
+
lists every code it resolves to, with the orthography2ipa quality tier of each.
|
|
209
|
+
Bare (undiacritized) input is restored **before** dialect allophony applies. The
|
|
210
|
+
model and stem lexicon are MSA artifacts, but the fusion scorer constrains the
|
|
211
|
+
model's distribution to the readings the *target lect's* orthography licenses
|
|
212
|
+
(see the flagship section above and [`docs/rawi-fusion.md`](docs/rawi-fusion.md)).
|
|
213
|
+
See [`docs/dialects.md`](docs/dialects.md) for the resolution rules, the
|
|
214
|
+
supported list, and the pinned pipeline order.
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
import arbtok
|
|
218
|
+
from arbtok.plugin import ArbtokG2PPlugin
|
|
219
|
+
|
|
220
|
+
arbtok.supported_lects()[:2] # [Lect('ar', 'research'), …]
|
|
221
|
+
ArbtokG2PPlugin(lang="ar-SA-x-najd").transcribe_word("قَهْوَة") # 'ˈɡahawa'
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Waqf, the register switch (`register="pausal"`)
|
|
225
|
+
|
|
226
|
+
Read aloud, Arabic **pauses in waqf form** (Wright, *A Grammar of the Arabic
|
|
227
|
+
Language*, 3rd ed., I §372. Ryding, *A Reference Grammar of MSA*, CUP 2005,
|
|
228
|
+
§2.4): at a phrase boundary the word-final short vowel (the case/mood ending,
|
|
229
|
+
iʿrāb) is not pronounced, tanwīn *-un/-in* drop with their /n/, tanwīn *-an*
|
|
230
|
+
lengthens to /aː/ on its written seat alif, and a tāʾ marbūṭa voiced only by
|
|
231
|
+
its ending falls silent with it (مَدِينَةٌ. → *madiːna*). The construct-state
|
|
232
|
+
/at/ (an iḍāfa head pausing with its tāʾ) is **not modeled**.
|
|
233
|
+
|
|
234
|
+
The named switch is `register`: `"pausal"` (the default, the TTS register)
|
|
235
|
+
or `"full"` (continuous full-iʿrāb passthrough). Under `"full"` every waqf
|
|
236
|
+
reduction is disabled and every written ending is read out. This is the
|
|
237
|
+
recitation and pedagogical register, and the mode for fully-vocalized MSA
|
|
238
|
+
that should be read exactly as its author pointed it, including scoring
|
|
239
|
+
against iʿrāb-keeping gold:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
ArbtokG2PPlugin(register="pausal").transcribe("رَأَيْتُ كِتَابًا.") # …kitaːbaː
|
|
243
|
+
ArbtokG2PPlugin(register="full").transcribe("رَأَيْتُ كِتَابًا.") # …kitaːban
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Under `register="pausal"`, a pause has to be **written** (a punctuation
|
|
247
|
+
token): no pause is invented at the edge of the input. When the diacritizer
|
|
248
|
+
runs on bare text it restores the pausal register throughout, since the
|
|
249
|
+
modern spoken register keeps no iʿrāb at all. The boolean `pausal=True/False`
|
|
250
|
+
is the same switch's original spelling and wins when passed explicitly.
|
|
251
|
+
|
|
252
|
+
The iʿrāb-driven reductions are facts about the MSA/Classical registers only:
|
|
253
|
+
a dialect lect has no case endings to drop, so its final short vowels and its
|
|
254
|
+
lexicalized *-an* adverbs (أَهْلًا وَسَهْلًا → *ahlan wasahlan*) are read as
|
|
255
|
+
written under either register.
|
|
256
|
+
|
|
257
|
+
Both modes run the same lattice and rescorers. The flag is consulted in one
|
|
258
|
+
place (`arbtok.sandhi`), so the transform applies exactly once.
|
|
259
|
+
|
|
260
|
+
### Foreign words (loanword nativization)
|
|
261
|
+
|
|
262
|
+
Real Arabic text is full of Latin-script words, such as *عندي meeting الساعة ٣*. A Latin
|
|
263
|
+
run is read as a **loanword**: phonemized with its donor spec (English by default)
|
|
264
|
+
and *nativized* into the matrix lect's phonology, out of that lect's own declared
|
|
265
|
+
inventory. The nativization table is chosen by walking the orthography2ipa parent
|
|
266
|
+
chain, so each lect adapts as its loanword literature says it does. Cairene reads
|
|
267
|
+
*manager* with the native stop ǧīm `[manaɡar]` and merges the interdental of *think*
|
|
268
|
+
to `[tink]`, while Najdi keeps the affricate `[manadʒar]` and the interdental
|
|
269
|
+
`[θink]`. A symbol the matrix lect cannot realize is refused (`None`) rather than
|
|
270
|
+
emitted unpronounceable.
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
ArbtokG2PPlugin(lang="ar-EG").transcribe_word("manager") # 'manaɡar'
|
|
274
|
+
ArbtokG2PPlugin(lang="ar-SA-x-najd").transcribe_word("manager") # 'manadʒar'
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`nativize=True` is the default (a TTS voice needs a pronounceable reading). Pass
|
|
278
|
+
`nativize=False` for linguistic output that must not invent a pronunciation:
|
|
279
|
+
the Latin run is then left in place, untranscribed:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
ArbtokG2PPlugin(lang="ar-SA-x-najd", nativize=False).transcribe("عندي meeting")
|
|
283
|
+
# 'ˈʕindiː meeting'
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Cited tables ship for Najdi (`ar-SA-x-najd`, Alhoody 2019), Egyptian (`ar-EG`,
|
|
287
|
+
Hafez 1996 / Watson 2002) and Levantine (`ar-x-levantine`, Al-Saidat 2011 / Cowell
|
|
288
|
+
1964). A lect with no table of its own (e.g. `ar-KW`) falls back to a conservative
|
|
289
|
+
pan-Arabic default.
|
|
290
|
+
|
|
291
|
+
### Diacritization only
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
from arbtok.tashkeel import TashkeelDiacritizer # the bundled rawi ensemble
|
|
295
|
+
|
|
296
|
+
TashkeelDiacritizer().diacritize("كتاب جميل")
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### As orthography2ipa plugins
|
|
300
|
+
|
|
301
|
+
Installing arbtok registers three **named** orthography2ipa step plugins
|
|
302
|
+
(`normalize` / `rescore` / `sandhi`, see `arbtok/o2i_plugins.py`) so plain
|
|
303
|
+
orthography2ipa can transcribe **undiacritized** Arabic, which it cannot do
|
|
304
|
+
alone since its input contract is diacritized text. The plugin is opted into at
|
|
305
|
+
the call site, never applied implicitly:
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
from orthography2ipa import G2P
|
|
309
|
+
|
|
310
|
+
G2P("ar").transcribe("كتب") # 'ˈktb' — no vowels to read
|
|
311
|
+
G2P("ar", plugins={"normalize": "arbtok"}).transcribe("كتب") # 'ˈkatab' — arbtok restores them
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Quality benchmarks
|
|
315
|
+
|
|
316
|
+
The test suite pins a gold sentence set (CER target ≤ 5% against the
|
|
317
|
+
reference transcriptions) and benchmarks against espeak-ng. See
|
|
318
|
+
`tests/test_ipa_fuzzy.py` and `docs/` for details.
|
|
319
|
+
|
|
320
|
+
For **per-lect** scoring, every resolvable variety against the orthography2ipa
|
|
321
|
+
Arabic TTS gold, diacritized and bare, next to espeak-ng, run
|
|
322
|
+
`python scripts/benchmark_stack.py --lect`. See
|
|
323
|
+
[`docs/benchmarks.md`](docs/benchmarks.md), which carries the full table and the
|
|
324
|
+
honesty note on why those figures are engine-similarity to cited-rule o2i output
|
|
325
|
+
rather than native-validated truth.
|
|
326
|
+
|
|
327
|
+
## Related projects
|
|
328
|
+
|
|
329
|
+
- [orthography2ipa](https://github.com/TigreGotico/orthography2ipa), the
|
|
330
|
+
language-agnostic grapheme-to-IPA lattice engine arbtok builds on.
|
|
331
|
+
- [text2tashkeel](https://github.com/TigreGotico/text2tashkeel), the
|
|
332
|
+
diacritization library that trains the rawi model family arbtok bundles.
|
|
333
|
+
|
|
334
|
+
## Documentation
|
|
335
|
+
|
|
336
|
+
[`docs/`](docs/quickstart.md) covers the diacritizer, the fusion scorer,
|
|
337
|
+
dialect resolution, Arabizi input, the code-switched gold set, the full API,
|
|
338
|
+
and advanced usage.
|
|
339
|
+
|
|
340
|
+
## License
|
|
341
|
+
|
|
342
|
+
Apache-2.0.
|
arbtok-0.0.0/README.md
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# arbtok
|
|
2
|
+
|
|
3
|
+
**Arabic text→IPA** with **dialect-aware tashkeel** diacritization, a
|
|
4
|
+
self-contained Arabic engine built on
|
|
5
|
+
[orthography2ipa](https://github.com/TigreGotico/orthography2ipa), covering MSA,
|
|
6
|
+
Classical, and 30+ regional varieties.
|
|
7
|
+
|
|
8
|
+
## What arbtok adds over orthography2ipa
|
|
9
|
+
|
|
10
|
+
[orthography2ipa](https://github.com/TigreGotico/orthography2ipa) (o2i) is a
|
|
11
|
+
language-agnostic grapheme→IPA lattice engine. For Arabic it assumes
|
|
12
|
+
**fully-vocalized** input: given the tashkeel, it transcribes accurately, but
|
|
13
|
+
real Arabic text is written **without** the short vowels, and o2i cannot invent
|
|
14
|
+
them, a bare skeleton transcribes incompletely wherever a vowel or gemination is
|
|
15
|
+
unwritten. That is the gap arbtok exists to close.
|
|
16
|
+
|
|
17
|
+
arbtok sits on o2i's lattice and adds the layer o2i deliberately leaves out:
|
|
18
|
+
|
|
19
|
+
- **A bundled neural diacritizer (rawi), extracted from
|
|
20
|
+
[text2tashkeel](https://github.com/TigreGotico/text2tashkeel).** text2tashkeel
|
|
21
|
+
is a diacritization library with a family of ONNX models. Arbtok takes its
|
|
22
|
+
**rawi** ensemble and stitches it into a single 4.9 MB logits ONNX baked into
|
|
23
|
+
the wheel (`arbtok/_ensemble.py`, built by
|
|
24
|
+
`tools/build_ensemble_logits_onnx.py`). There is **no runtime dependency on
|
|
25
|
+
text2tashkeel** and no network: the model rides inside the package. This is
|
|
26
|
+
what lets arbtok read the undiacritized text a person actually types.
|
|
27
|
+
- **Dialect-aware fusion, cross-word sandhi, loanword nativization, and a waqf
|
|
28
|
+
register switch**: the sentence-level, variety-specific phonology below.
|
|
29
|
+
|
|
30
|
+
The split shows up directly in the numbers. On the `arabic-dialects-gold20` set,
|
|
31
|
+
scored on the **undiacritized** `raw` skeleton (each lect at its own register:
|
|
32
|
+
full iʿrāb for MSA/Classical, pausal for the spoken varieties, using
|
|
33
|
+
`scripts/benchmark_gold20.py --undiac`), arbtok roughly **halves** o2i's error and
|
|
34
|
+
beats every dialect, while o2i-on-a-skeleton is barely better than espeak-ng:
|
|
35
|
+
|
|
36
|
+
| system | mean PER (stress-stripped) | MSA `ar` | Classical `arb` |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| **arbtok** (diacritizer on) | **0.147** | **0.076** | **0.055** |
|
|
39
|
+
| orthography2ipa (bare) | 0.302 | 0.363 | 0.444 |
|
|
40
|
+
| espeak-ng | 0.308 | 0.345 | 0.314 |
|
|
41
|
+
|
|
42
|
+
On the *vocalized* form of the same gold, arbtok's diacritizer is idle and it
|
|
43
|
+
simply matches o2i (that set does not exercise the layer). The undiacritized
|
|
44
|
+
per-dialect table is the benchmark that actually measures arbtok.
|
|
45
|
+
|
|
46
|
+
## Dialect-aware tashkeel
|
|
47
|
+
|
|
48
|
+
To our knowledge arbtok is the **only Arabic phonemizer whose diacritization is
|
|
49
|
+
dialect-aware**. Every other pipeline runs an MSA-trained diacritizer and then
|
|
50
|
+
phonemizes whatever it wrote. Arbtok turns that pipeline around. The bundled
|
|
51
|
+
rawi neural ensemble, extracted from
|
|
52
|
+
[text2tashkeel](https://github.com/TigreGotico/text2tashkeel) and stitched into a
|
|
53
|
+
4.9 MB ONNX inside the wheel (no network, no external model package), exposes
|
|
54
|
+
its per-character **distribution**, and arbtok
|
|
55
|
+
scores that distribution against **each variety's own phonological licensing**:
|
|
56
|
+
the orthography2ipa grapheme table and allophone rules of the target lect
|
|
57
|
+
(`docs/rawi-fusion.md`). The chosen tashkeel is the model's most probable
|
|
58
|
+
reading *that the dialect's orthography actually admits*, for all 33 supported
|
|
59
|
+
lects, from Najdi and Hejazi to Tunisian, Egyptian, and the qeltu Iraqi of
|
|
60
|
+
Mosul (`docs/dialects.md`).
|
|
61
|
+
|
|
62
|
+
So the same bare sentence receives variety-appropriate marks and IPA:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from arbtok.plugin import ArbtokG2PPlugin
|
|
66
|
+
|
|
67
|
+
bare = "ذهب الولد الى المدرسة" # undiacritized input
|
|
68
|
+
ArbtokG2PPlugin(lang="ar").transcribe(bare) # ˈðahab ˈalwalad ˈalaː ˈlmudrasa
|
|
69
|
+
ArbtokG2PPlugin(lang="ar-TN").transcribe(bare) # ˈðahab ˈalwalad ˈalɛː ˈlmudrasa
|
|
70
|
+
ArbtokG2PPlugin(lang="ar-SA-x-najd").transcribe("يشرب القهوة في البيت")
|
|
71
|
+
# ˈjaʃrab alˈɡahawa ˈfiː ˈlbajt — Najdi /g/ for qāf, epenthetic gahawa vowel
|
|
72
|
+
ArbtokG2PPlugin(lang="ar-TN").transcribe("يشرب القهوة في البيت")
|
|
73
|
+
# ˈjaʃrab alˈqahwa ˈfiː ˈlbiːt — Tunisian monophthong /iː/ in bayt
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Measured on the bare-input TTS gold (33 lects × 20 sentences, mean per-sentence
|
|
77
|
+
phoneme error rate), scoring the ensemble distribution under dialect licensing
|
|
78
|
+
outperforms running the same ensemble as a free generator, with the margin
|
|
79
|
+
concentrated on the lects that diverge most from MSA, the signature of the
|
|
80
|
+
licensing doing the work (`docs/rawi-fusion.md` carries the full table).
|
|
81
|
+
|
|
82
|
+
Three capabilities define the engine:
|
|
83
|
+
|
|
84
|
+
1. **Dialect-aware tashkeel**: the fusion scorer above. It is on by default
|
|
85
|
+
(`fusion=False` opts out), guarded so a human's marks are never overwritten
|
|
86
|
+
and a letter the writing spells is never rewritten.
|
|
87
|
+
2. **Per-lect cited loanword nativization**: code-switched Latin words are
|
|
88
|
+
read out of the *matrix lect's own* inventory, per published loanword
|
|
89
|
+
literature (Cairene `[manaɡar]` vs Najdi `[manadʒar]`, see below).
|
|
90
|
+
3. **Waqf / register policy**: one declared switch between the spoken pausal
|
|
91
|
+
register (the TTS default) and full-iʿrāb recitation (see below).
|
|
92
|
+
|
|
93
|
+
## The lattice underneath
|
|
94
|
+
|
|
95
|
+
Word phonology is built on the **orthography2ipa shared lattice**: the
|
|
96
|
+
language-agnostic grapheme tokenizer (`PhonetokTokenizer`) over the `ar`
|
|
97
|
+
spec grapheme table produces a per-position candidate lattice. The `ar`
|
|
98
|
+
engine handles the segment-local phonology natively, gemination (shadda ّ,
|
|
99
|
+
glides included), lam-alif / presentation ligatures (ﻻ → `laː`), onset
|
|
100
|
+
glides (يَ → `ja`), a hamza carrier's bare /ʔ/ before an explicit harakah, a
|
|
101
|
+
fatḥa + standalone alif maksūra as one long vowel (حَتَّى → `ħattaː`), a
|
|
102
|
+
sukūn-final **coda glide** (ظَبْي → `ðˤabj`, رَمْي → `ramj`, while فِي stays
|
|
103
|
+
`fiː`), and pausal tāʾ marbūṭa. The Arabic morpho-phonology that the shared
|
|
104
|
+
grapheme table cannot express is layered on as composable
|
|
105
|
+
`LatticeRescorer`s (`arbtok/lattice.py`) rather than a private tokenizer
|
|
106
|
+
fork:
|
|
107
|
+
|
|
108
|
+
- **sun-letter assimilation** (idghām ash-shamsiyya): the lām of the
|
|
109
|
+
definite article ⟨ال⟩ assimilates into a following coronal (sun) letter
|
|
110
|
+
(`al-šams` → `aš-šams`). Moon letters keep the lām (`al-qamar`).
|
|
111
|
+
- **hamzat al-waṣl** elision: a word-initial prosthetic alif is silent,
|
|
112
|
+
its harakah carrying the vowel (`istiqbāl`).
|
|
113
|
+
- **accusative-alif** silencing after tanwīn al-fatḥ (`marħaban`), and the
|
|
114
|
+
bare glottal stop of a hamza carrier before a sukūn or word edge
|
|
115
|
+
(`taʔθīr`).
|
|
116
|
+
|
|
117
|
+
Emphatic (pharyngealization) spreading rides on the `ar` spec's own B8
|
|
118
|
+
`allophone_rules`. Cross-word sandhi is orthogonal to the word lattice: clitic
|
|
119
|
+
joining, cross-word waṣl elision, tanwīn pausal forms, tāʾ marbūṭa, and
|
|
120
|
+
idgham/iqlab nasal assimilation all live in the sentence-level orchestration,
|
|
121
|
+
exposed to plain orthography2ipa through its sandhi plugin hook
|
|
122
|
+
(`arbtok/o2i_plugins.py`). Bare (undiacritized) text is diacritized first by
|
|
123
|
+
the bundled rawi ensemble (the dialect-aware fusion path above), entirely
|
|
124
|
+
inside the wheel.
|
|
125
|
+
|
|
126
|
+
> Honesty note: the gold IPA reference set was LLM-generated and has not been
|
|
127
|
+
> validated by a native MSA speaker. If you speak MSA, pull requests are very
|
|
128
|
+
> welcome.
|
|
129
|
+
|
|
130
|
+
## Installation
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pip install arbtok
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Usage
|
|
137
|
+
|
|
138
|
+
arbtok is built **on** [orthography2ipa](https://github.com/TigreGotico/orthography2ipa)
|
|
139
|
+
(spec data and the shared `G2PPlugin`/`WordContext` base types) and owns the
|
|
140
|
+
Arabic pipeline. orthography2ipa stays the language-agnostic base library.
|
|
141
|
+
|
|
142
|
+
### Engine class
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from arbtok.tokenizer import Sentence
|
|
146
|
+
|
|
147
|
+
Sentence("اَلسَّلَامُ عَلَيْكُمْ").ipa
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
An isolated MSA word transcribes on the shared lattice directly:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from arbtok.lattice import word_ipa
|
|
154
|
+
|
|
155
|
+
word_ipa("الشَّمْس") # 'aʃˈʃams' — sun-letter assimilation as a rescorer
|
|
156
|
+
word_ipa("الْقَمَر") # 'ˈalqamar' — moon-letter control (lām kept)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Bare text is handled by diacritizing first:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from arbtok.plugin import ArbtokG2PPlugin
|
|
163
|
+
|
|
164
|
+
plugin = ArbtokG2PPlugin()
|
|
165
|
+
plugin.transcribe("كتاب جميل") # auto-tashkeel + IPA
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Varieties
|
|
169
|
+
|
|
170
|
+
Pass a spec code as `lang=` to phonemize a variety. `arbtok.supported_lects()`
|
|
171
|
+
lists every code it resolves to, with the orthography2ipa quality tier of each.
|
|
172
|
+
Bare (undiacritized) input is restored **before** dialect allophony applies. The
|
|
173
|
+
model and stem lexicon are MSA artifacts, but the fusion scorer constrains the
|
|
174
|
+
model's distribution to the readings the *target lect's* orthography licenses
|
|
175
|
+
(see the flagship section above and [`docs/rawi-fusion.md`](docs/rawi-fusion.md)).
|
|
176
|
+
See [`docs/dialects.md`](docs/dialects.md) for the resolution rules, the
|
|
177
|
+
supported list, and the pinned pipeline order.
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
import arbtok
|
|
181
|
+
from arbtok.plugin import ArbtokG2PPlugin
|
|
182
|
+
|
|
183
|
+
arbtok.supported_lects()[:2] # [Lect('ar', 'research'), …]
|
|
184
|
+
ArbtokG2PPlugin(lang="ar-SA-x-najd").transcribe_word("قَهْوَة") # 'ˈɡahawa'
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Waqf, the register switch (`register="pausal"`)
|
|
188
|
+
|
|
189
|
+
Read aloud, Arabic **pauses in waqf form** (Wright, *A Grammar of the Arabic
|
|
190
|
+
Language*, 3rd ed., I §372. Ryding, *A Reference Grammar of MSA*, CUP 2005,
|
|
191
|
+
§2.4): at a phrase boundary the word-final short vowel (the case/mood ending,
|
|
192
|
+
iʿrāb) is not pronounced, tanwīn *-un/-in* drop with their /n/, tanwīn *-an*
|
|
193
|
+
lengthens to /aː/ on its written seat alif, and a tāʾ marbūṭa voiced only by
|
|
194
|
+
its ending falls silent with it (مَدِينَةٌ. → *madiːna*). The construct-state
|
|
195
|
+
/at/ (an iḍāfa head pausing with its tāʾ) is **not modeled**.
|
|
196
|
+
|
|
197
|
+
The named switch is `register`: `"pausal"` (the default, the TTS register)
|
|
198
|
+
or `"full"` (continuous full-iʿrāb passthrough). Under `"full"` every waqf
|
|
199
|
+
reduction is disabled and every written ending is read out. This is the
|
|
200
|
+
recitation and pedagogical register, and the mode for fully-vocalized MSA
|
|
201
|
+
that should be read exactly as its author pointed it, including scoring
|
|
202
|
+
against iʿrāb-keeping gold:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
ArbtokG2PPlugin(register="pausal").transcribe("رَأَيْتُ كِتَابًا.") # …kitaːbaː
|
|
206
|
+
ArbtokG2PPlugin(register="full").transcribe("رَأَيْتُ كِتَابًا.") # …kitaːban
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Under `register="pausal"`, a pause has to be **written** (a punctuation
|
|
210
|
+
token): no pause is invented at the edge of the input. When the diacritizer
|
|
211
|
+
runs on bare text it restores the pausal register throughout, since the
|
|
212
|
+
modern spoken register keeps no iʿrāb at all. The boolean `pausal=True/False`
|
|
213
|
+
is the same switch's original spelling and wins when passed explicitly.
|
|
214
|
+
|
|
215
|
+
The iʿrāb-driven reductions are facts about the MSA/Classical registers only:
|
|
216
|
+
a dialect lect has no case endings to drop, so its final short vowels and its
|
|
217
|
+
lexicalized *-an* adverbs (أَهْلًا وَسَهْلًا → *ahlan wasahlan*) are read as
|
|
218
|
+
written under either register.
|
|
219
|
+
|
|
220
|
+
Both modes run the same lattice and rescorers. The flag is consulted in one
|
|
221
|
+
place (`arbtok.sandhi`), so the transform applies exactly once.
|
|
222
|
+
|
|
223
|
+
### Foreign words (loanword nativization)
|
|
224
|
+
|
|
225
|
+
Real Arabic text is full of Latin-script words, such as *عندي meeting الساعة ٣*. A Latin
|
|
226
|
+
run is read as a **loanword**: phonemized with its donor spec (English by default)
|
|
227
|
+
and *nativized* into the matrix lect's phonology, out of that lect's own declared
|
|
228
|
+
inventory. The nativization table is chosen by walking the orthography2ipa parent
|
|
229
|
+
chain, so each lect adapts as its loanword literature says it does. Cairene reads
|
|
230
|
+
*manager* with the native stop ǧīm `[manaɡar]` and merges the interdental of *think*
|
|
231
|
+
to `[tink]`, while Najdi keeps the affricate `[manadʒar]` and the interdental
|
|
232
|
+
`[θink]`. A symbol the matrix lect cannot realize is refused (`None`) rather than
|
|
233
|
+
emitted unpronounceable.
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
ArbtokG2PPlugin(lang="ar-EG").transcribe_word("manager") # 'manaɡar'
|
|
237
|
+
ArbtokG2PPlugin(lang="ar-SA-x-najd").transcribe_word("manager") # 'manadʒar'
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`nativize=True` is the default (a TTS voice needs a pronounceable reading). Pass
|
|
241
|
+
`nativize=False` for linguistic output that must not invent a pronunciation:
|
|
242
|
+
the Latin run is then left in place, untranscribed:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
ArbtokG2PPlugin(lang="ar-SA-x-najd", nativize=False).transcribe("عندي meeting")
|
|
246
|
+
# 'ˈʕindiː meeting'
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Cited tables ship for Najdi (`ar-SA-x-najd`, Alhoody 2019), Egyptian (`ar-EG`,
|
|
250
|
+
Hafez 1996 / Watson 2002) and Levantine (`ar-x-levantine`, Al-Saidat 2011 / Cowell
|
|
251
|
+
1964). A lect with no table of its own (e.g. `ar-KW`) falls back to a conservative
|
|
252
|
+
pan-Arabic default.
|
|
253
|
+
|
|
254
|
+
### Diacritization only
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
from arbtok.tashkeel import TashkeelDiacritizer # the bundled rawi ensemble
|
|
258
|
+
|
|
259
|
+
TashkeelDiacritizer().diacritize("كتاب جميل")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### As orthography2ipa plugins
|
|
263
|
+
|
|
264
|
+
Installing arbtok registers three **named** orthography2ipa step plugins
|
|
265
|
+
(`normalize` / `rescore` / `sandhi`, see `arbtok/o2i_plugins.py`) so plain
|
|
266
|
+
orthography2ipa can transcribe **undiacritized** Arabic, which it cannot do
|
|
267
|
+
alone since its input contract is diacritized text. The plugin is opted into at
|
|
268
|
+
the call site, never applied implicitly:
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
from orthography2ipa import G2P
|
|
272
|
+
|
|
273
|
+
G2P("ar").transcribe("كتب") # 'ˈktb' — no vowels to read
|
|
274
|
+
G2P("ar", plugins={"normalize": "arbtok"}).transcribe("كتب") # 'ˈkatab' — arbtok restores them
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Quality benchmarks
|
|
278
|
+
|
|
279
|
+
The test suite pins a gold sentence set (CER target ≤ 5% against the
|
|
280
|
+
reference transcriptions) and benchmarks against espeak-ng. See
|
|
281
|
+
`tests/test_ipa_fuzzy.py` and `docs/` for details.
|
|
282
|
+
|
|
283
|
+
For **per-lect** scoring, every resolvable variety against the orthography2ipa
|
|
284
|
+
Arabic TTS gold, diacritized and bare, next to espeak-ng, run
|
|
285
|
+
`python scripts/benchmark_stack.py --lect`. See
|
|
286
|
+
[`docs/benchmarks.md`](docs/benchmarks.md), which carries the full table and the
|
|
287
|
+
honesty note on why those figures are engine-similarity to cited-rule o2i output
|
|
288
|
+
rather than native-validated truth.
|
|
289
|
+
|
|
290
|
+
## Related projects
|
|
291
|
+
|
|
292
|
+
- [orthography2ipa](https://github.com/TigreGotico/orthography2ipa), the
|
|
293
|
+
language-agnostic grapheme-to-IPA lattice engine arbtok builds on.
|
|
294
|
+
- [text2tashkeel](https://github.com/TigreGotico/text2tashkeel), the
|
|
295
|
+
diacritization library that trains the rawi model family arbtok bundles.
|
|
296
|
+
|
|
297
|
+
## Documentation
|
|
298
|
+
|
|
299
|
+
[`docs/`](docs/quickstart.md) covers the diacritizer, the fusion scorer,
|
|
300
|
+
dialect resolution, Arabizi input, the code-switched gold set, the full API,
|
|
301
|
+
and advanced usage.
|
|
302
|
+
|
|
303
|
+
## License
|
|
304
|
+
|
|
305
|
+
Apache-2.0.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
I'm just a dude trying to create TTS voices to help the visually impaired arabic community.
|
|
3
|
+
|
|
4
|
+
I am not an arabic speaker, I never had any previous interaction with the arabic script, this might be a pointless experiment powered by LLM hallucinations.
|
|
5
|
+
|
|
6
|
+
You have been warned.
|
|
7
|
+
|
|
8
|
+
if you speak MSA pull requests welcome!
|
|
9
|
+
|
|
10
|
+
- test.py contains a reference dataset to benchmark the code against, IPA transcriptions have been LLM generated
|
|
11
|
+
- main logic is in tokenizer.py
|
|
12
|
+
- see tests folder for some benchmarks and comparison against pre existing (low quality) datasets
|
|
13
|
+
- varieties are orthography2ipa specs, named by code (ar, ar-SA-x-najd,
|
|
14
|
+
ar-SA-x-hejaz, ar-EG, ar-x-gulf, …): the spec supplies the grapheme table and
|
|
15
|
+
the allophone rules, so a variety's phonology comes from cited spec data.
|
|
16
|
+
Pass `lang=` — see docs/dialects.md
|
|
17
|
+
|
|
18
|
+
``supported_lects()`` enumerates every variety `lang=` resolves to, each with
|
|
19
|
+
its orthography2ipa quality tier.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from arbtok.dialects import Lect, spec_for_lang, supported_lects
|
|
23
|
+
|
|
24
|
+
__all__ = ["Lect", "spec_for_lang", "supported_lects"]
|