Metadata-Version: 2.4
Name: moore-g2p
Version: 0.2.0
Summary: Transcription graphème-phonème (G2P) pour le mooré, en ARPABET et IPA
Author-email: Michael Roger Zongo <michaelrogerzongo@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/root-gift/moore-g2p
Project-URL: Repository, https://github.com/root-gift/moore-g2p
Project-URL: Bug Tracker, https://github.com/root-gift/moore-g2p/issues
Keywords: moore,mooré,mossi,burkina-faso,g2p,grapheme-to-phoneme,phonetics,ipa,arpabet,tts,speech-synthesis,low-resource-language,african-languages
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: French
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=3.0; extra == "dev"
Requires-Dist: black>=22.0; extra == "dev"
Requires-Dist: flake8>=4.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Dynamic: license-file

# moore-g2p

Transcription graphème-phonème pour le **mooré**, langue gur parlée par
environ huit millions de personnes au Burkina Faso.

Convertit du texte mooré en phonèmes, en deux notations produites depuis un
inventaire unique :

- **ARPABET** — pour les systèmes de synthèse vocale (Tacotron2, VITS, F5-TTS)
- **IPA** — pour la lecture humaine, la documentation et la description
  linguistique

```python
from moore_g2p import transcribe, transcribe_ipa

transcribe("bãngã")       # 'B AA_N N G AA'
transcribe_ipa("bãngã")   # 'bãnga'
```

---

## Installation

### Usage local, pendant le développement

Depuis le dossier du projet, en mode éditable : les modifications du code
sont prises en compte immédiatement, sans réinstallation.

```bash
git clone https://github.com/root-gift/moore-g2p.git
cd moore-g2p

python -m venv .venv                  # environnement isolé
source .venv/bin/activate             # Windows : .venv\Scripts\activate

pip install -e ".[dev]"               # paquet + outils de développement
pytest
```

Il n'y a pas de `requirements.txt` : les dépendances sont déclarées dans
`pyproject.toml`, qui est la source unique depuis la PEP 621. Celles de
développement — pytest, black, flake8, build, twine — vivent dans
`[project.optional-dependencies]` et s'installent avec le suffixe `[dev]`.

### Depuis PyPI, une fois publié

```bash
pip install moore-g2p
```

**Aucune dépendance à l'exécution.** Le paquet n'importe que `re`, `sys`,
`typing`, `unicodedata` et `argparse`, tous dans la bibliothèque standard.
Rien à installer, aucun conflit de versions possible, et l'intégration dans
un pipeline d'entraînement n'ajoute rien à son environnement.

---

## Usage

### Un mot

```python
from moore_g2p import MooreG2P

g = MooreG2P()
g.transcribe("rĩmã")                    # 'R I_N M AA_N'
g.transcribe("rĩmã", notation="ipa")    # 'rĩmã'
```

### Une phrase, ponctuation comprise

La ponctuation est **conservée** : c'est le seul support de la prosodie —
pauses, groupes de souffle, intonation montante.

```python
g.transcribe_text("Rĩmã, fo yaa bãngã?")
# 'R I_N M AA_N , F O | Y AA1 | B AA_N N G AA ?'

g.transcribe_text("Rĩmã, fo yaa bãngã?", notation="ipa")
# 'rĩmã, fo jaː bãnga?'
```

En ARPABET, `|` marque la frontière de mot. En IPA, les mots sont séparés
par une espace, selon l'usage phonétique.

### Traiter un corpus

Utilisez `strict=True` : tout graphème non reconnu lève alors une exception
au lieu de disparaître silencieusement du texte d'entraînement.

```python
g = MooreG2P(strict=True)
try:
    g.transcribe(ligne)
except ValueError as e:
    print(f"ligne {n} : {e}")
```

### Nombres

Les chiffres sont développés en toutes lettres avant la conversion, comme le
fait `text/numbers.py` dans Tacotron2 — sauf que celui-ci dépend d'`inflect`,
qui ne connaît que l'anglais.

```python
from moore_g2p.numbers import expand_number, expand_numbers

expand_number(0)                     # 'zaalm'
expand_number(13)                    # 'piig la a tã'
expand_number(2001)                  # 'tusra yiib la a ye'
expand_numbers("Yaa 25 la 3.")       # 'Yaa pisi la a nu la tãabo.'
```

**Structure.** Le connecteur dépend du rang de ce qui suit : `la a` devant une
unité, `la` seul devant un rang supérieur.

```
112 = koabg la piig la a yiibu
            └ la devant « dix »
                      └ la a devant l'unité
```

Le `a` est une particule autonome, non un élément du numéral : 1 se dit
`yembo`, `yembre` ou `ye`. La particule tombe devant la forme `yi`, et devant elle seule.

L'unité prend sa forme **brève** (`yi`, `tã`) sous une dizaine seule, et sa
forme **pleine** (`yiibu`, `tãabo`) dès qu'une centaine ou un rang supérieur
précède :

```
  12 = piig la yi
 612 = kobs-yoob la piig la a yiibu
3003 = tusra tãab la a tãabo
```

Trois formes coexistent ainsi pour 3 : `tãabo` pleine, `tã` brève, `tãab`
devant `la`. Devant un connecteur, la voyelle finale tombe : `piiga` → `piig`,
`koabga` → `koabg`, `tusri` → `tusr`, `yiibu` → `yiib` ; `pisi`, `kobisi` et
`yopoe` sont invariables.

Le millier a quatre formes selon le contexte : `tusri` isolé, `tusr` devant
`la`, `tusra` avec un multiplicateur de 2 à 9, `tus` avec un multiplicateur
dizaine. Le trait d'union des dizaines ne s'écrit que devant une
consonne : `pis-tã`, mais `pisi`. Les centaines reprennent exactement cette
structure avec le radical `kobs-` : `kobisi` (200), `kobs-tã` (300),
`kobs-naase` (400).

**Méthode.** `ATTESTES` contient les formes fournies par un locuteur et est
consultée en premier, comme un lexique d'exceptions ; le reste est généré par
règles. La plage traitée va de 0 à 999 999.

```python
from moore_g2p.numbers import conflits

for n, attestee, generee in conflits():
    print(n, attestee, "|", generee)
```

**Liaison.** À la lecture, `la a` est couramment prononcé en liaison. Le
module produit la forme orthographique et laisse la réalisation au modèle
acoustique, qui l'apprend de l'audio. Si l'on préfère encoder la liaison au
niveau des phonèmes, c'est une règle à ajouter au G2P et non au module de
nombres, car elle dépasse le cas des numéraux.

**Variantes libres.** `yembo`, `yembre` et `ye` sont interchangeables pour 1,
comme `wae` et `wɛ` pour 9. `canoniser()` les ramène à une forme unique, ce
qui permet de comparer deux écritures d'un même nombre sans qu'un choix de
variante compte comme une différence.

```python
from moore_g2p.numbers import canoniser
canoniser("pis-wɛ la yembre")    # 'pis-wae la ye'
```

`conflits()` liste les cas où la règle diverge d'une forme attestée, variantes
mises à part. Ce sont
les points du système qui restent à arbitrer par un locuteur ; tant qu'ils
figurent dans `ATTESTES`, la forme attestée l'emporte.

Le développement est actif par défaut et se désactive par
`MooreG2P(nombres=False)`.

### Lexique d'exceptions

Prioritaire sur toutes les règles, et modifiable à chaud. C'est le bon
endroit pour les noms propres, les emprunts et les toponymes. Tant qu'une
entrée n'utilise que des symboles existants, aucun réentraînement n'est
nécessaire en aval.

```python
g = MooreG2P(lexique={"Wẽnnaam": "W EH_N N1 AA1 M"})
g.transcribe("Wẽnnaam")   # 'W EH_N N1 AA1 M'
```

### Ligne de commande

```bash
moore-g2p "Rĩmã, fo yaa bãngã?"
moore-g2p -n ipa "Rĩmã, fo yaa bãngã?"
moore-g2p --tacotron2 "bãngã"
moore-g2p --inventaire
cat corpus.txt | moore-g2p --strict > corpus.phonemes
```

### Intégration Tacotron2

```python
g.to_tacotron2("Rĩmã, fo yaa bãngã?")
# '{R I_N M AA_N}, {F O} {Y AA1} {B AA_N N G AA}?'
```

Les accolades délimitent les zones ARPABET, pas les mots ; la ponctuation
reste à l'extérieur, où Tacotron2 la déclare déjà. Le préfixe `@` est ajouté
en interne par `_arpabet_to_sequence` — ne l'écrivez pas.

Les symboles doivent d'abord être déclarés dans `text/symbols.py`, faute de
quoi Tacotron2 les supprime **sans avertissement** :

```python
from text.symbols import symbols
g.verifier_couverture(symbols)   # lève une erreur en listant les manquants
```

---

## Points de conception

### Normalisation Unicode

`ã ẽ ĩ õ ũ` possèdent une forme précomposée. **`ɛ̃` et `ɔ̃` n'en ont pas** :
ce sont toujours deux points de code, lettre plus tilde combinant. Aucune
normalisation Unicode ne peut les atomiser — c'est pourquoi le conseil
habituel « normalisez en NFC » échoue ici.

Le paquet ramène donc `ɛ̃` vers `ẽ` et `ɔ̃` vers `õ`, qui sont des caractères
uniques, dès la première étape. Après quoi plus aucun diacritique combinant
ne subsiste et le reste du traitement devient insensible à la saisie.

```python
import unicodedata as ud
g.transcribe(ud.normalize("NFC", "Wẽnnaam")) == \
    g.transcribe(ud.normalize("NFD", "Wẽnnaam"))   # True
```

### Créneaux réservés

La matrice d'embeddings d'un modèle TTS a exactement `n_symboles` lignes.
Ajouter un symbole après l'entraînement change cette taille et rend tout
checkpoint inchargeable.

L'inventaire réserve donc des lignes vides, placées **à la fin** pour que les
indices des symboles réels ne bougent jamais. Pour en attribuer une plus
tard, renseignez `inventory.CRENEAUX_ACTIVES` ; un fine-tune léger reste
nécessaire pour que la ligne apprenne quelque chose.

### Le ton n'est pas traité

Le mooré est une langue à tons, mais l'orthographe standard ne les note pas.
Un modèle entraîné sur cette base les apprend implicitement, par mémorisation
lexicale, et se trompe sur les mots inconnus et les homographes tonaux.
Traiter les tons demanderait un lexique tonal et un désambiguïsateur
morphosyntaxique.

---

## À valider par un locuteur natif

Ces choix reposent sur la cohérence interne, sans attestation documentaire.
Ils sont modifiables en tête de `inventory.py`.

1. **Timbre des voyelles dénasalisées** — on suppose que `ẽ` [ɛ̃] donne [ɛ] et
   `õ` [ɔ̃] donne [ɔ]. Voir `DENASALISATION`.
2. **Asymétrie des nasales doublées** — `ẽẽ` et `õõ` sont admises, `ãã`, `ĩĩ`,
   `ũũ` non. Voir `NASALES_DOUBLABLES`.
3. **Portée de la dénasalisation** — seules `g`, `d`, `b` déclenchent la règle.
   Voir `CONS_DENASALISANTES`.
4. **Suites vocaliques** — elles ne figurent dans aucune source publiée. Voir
   `SUITES`.

---

## Développement

Un `Makefile` regroupe les commandes courantes.

```bash
make install   # environnement de développement
make test      # tests avec couverture
make lint      # flake8 + black --check
make format    # applique black
make build     # sdist + wheel, puis twine check
```

Les vérifications tournent aussi avant chaque commit si vous le souhaitez :

```bash
pre-commit install
```

L'intégration continue exécute la suite sur Linux, macOS et Windows, de
Python 3.8 à 3.12, plus le lint et la construction du paquet.

## Publier une nouvelle version

1. Mettre à jour la version dans `pyproject.toml` **et**
   `moore_g2p/__init__.py`. Le workflow refuse de publier si les deux
   divergent du tag.
2. Compléter `CHANGELOG.md`.
3. `make test && make lint && make build`
4. Créer une release GitHub avec le tag `vX.Y.Z`.

La publication sur PyPI se fait alors automatiquement, par publication de
confiance — aucun jeton API à stocker. Il faut l'avoir configurée une fois
sur PyPI, dans les paramètres du projet, en déclarant le dépôt GitHub et le
workflow `publish.yml`.

Pour publier à la main :

```bash
make build
twine upload --repository testpypi dist/*   # essai
twine upload dist/*                          # publication
```

## Licence

MIT. Voir [LICENSE](LICENSE).

## Citation

```bibtex
@software{moore_g2p,
  title  = {moore-g2p: Grapheme-to-Phoneme Conversion for Mooré},
  author = {Zongo, Michael Roger},
  year   = {2026},
  url    = {https://github.com/root-gift/moore-g2p}
}
```
