Spaces:
Sleeping
Sleeping
jefffffff9
Add Adlam/Pular Fula integration: transliterator + 3 new datasets + normalisation pipeline
ced078c | """ | |
| Adlam โ Latin transliteration for Pular (Guinea Fula). | |
| Adlam (๐ค๐คฃ๐คค๐คข๐คฅ) is the indigenous alphabet created by Ibrahima and Abdoulaye Barry | |
| for the Fula language family. Unicode block U+1E900โU+1E95F. | |
| This module provides: | |
| - adlam_to_latin(text) โ convert Adlam script โ Latin romanization | |
| - latin_to_adlam(text) โ convert Latin romanization โ Adlam script | |
| - normalize_pular(text) โ canonical pre-processing for ASR training: | |
| strips diacritics variants, lowercases, unifies spacing | |
| - contains_adlam(text) โ detect whether a string has Adlam characters | |
| Transliteration table follows the standard Pular (Guinea) orthography used in: | |
| - SIL/Fulfulde literacy materials | |
| - Pullo-Africa-Protagonist dataset | |
| - guizme/adlam_fulfulde dataset | |
| Note: Whisper's BPE tokenizer covers the entire Unicode BMP but has never seen | |
| Adlam in pre-training text, so Adlam tokens produce garbage output. Training | |
| and ASR therefore always use Latin romanization; Adlam is converted to Latin | |
| before feeding to the model, and Latin is kept as-is for display. | |
| """ | |
| from __future__ import annotations | |
| import re | |
| import unicodedata | |
| # โโ Adlam โ Latin mapping (uppercase + lowercase pairs) โโโโโโโโโโโโโโโโโโโโโโ | |
| # Source: Unicode Adlam chart + SIL Pulaar keyboard standard | |
| _ADLAM_TO_LATIN: list[tuple[str, str]] = [ | |
| # Uppercase (U+1E900โU+1E921), then lowercase (U+1E922โU+1E943) | |
| ("\U0001e900", "A"), # ๐ค โ A | |
| ("\U0001e901", "B"), # ๐ค โ B | |
| ("\U0001e902", "B"), # ๐ค โ B (Bhe) | |
| ("\U0001e903", "D"), # ๐ค โ D | |
| ("\U0001e904", "D"), # ๐ค โ D (Dhe) | |
| ("\U0001e905", "E"), # ๐ค โ E | |
| ("\U0001e906", "F"), # ๐ค โ F | |
| ("\U0001e907", "G"), # ๐ค โ G | |
| ("\U0001e908", "H"), # ๐ค โ H | |
| ("\U0001e909", "I"), # ๐ค โ I | |
| ("\U0001e90a", "J"), # ๐ค โ J | |
| ("\U0001e90b", "K"), # ๐ค โ K | |
| ("\U0001e90c", "L"), # ๐ค โ L | |
| ("\U0001e90d", "M"), # ๐ค โ M | |
| ("\U0001e90e", "N"), # ๐ค โ N | |
| ("\U0001e90f", "NG"), # ๐ค โ NG | |
| ("\U0001e910", "O"), # ๐ค โ O | |
| ("\U0001e911", "P"), # ๐ค โ P | |
| ("\U0001e912", "R"), # ๐ค โ R | |
| ("\U0001e913", "S"), # ๐ค โ S | |
| ("\U0001e914", "T"), # ๐ค โ T | |
| ("\U0001e915", "U"), # ๐ค โ U | |
| ("\U0001e916", "V"), # ๐ค โ V | |
| ("\U0001e917", "W"), # ๐ค โ W | |
| ("\U0001e918", "Y"), # ๐ค โ Y | |
| ("\U0001e919", "Z"), # ๐ค โ Z | |
| ("\U0001e91a", "KH"), # ๐ค โ KH | |
| ("\U0001e91b", "QU"), # ๐ค โ QU | |
| ("\U0001e91c", "SH"), # ๐ค โ SH | |
| ("\U0001e91d", "GH"), # ๐ค โ GH | |
| ("\U0001e91e", "NY"), # ๐ค โ NY (ษฒ) | |
| ("\U0001e91f", "TH"), # ๐ค โ TH | |
| ("\U0001e920", "WH"), # ๐ค โ WH | |
| ("\U0001e921", "NY"), # ๐คก โ NY (ษณ) | |
| # Lowercase | |
| ("\U0001e922", "a"), # ๐คข โ a | |
| ("\U0001e923", "b"), # ๐คฃ โ b | |
| ("\U0001e924", "b"), # ๐คค โ b | |
| ("\U0001e925", "d"), # ๐คฅ โ d | |
| ("\U0001e926", "d"), # ๐คฆ โ d | |
| ("\U0001e927", "e"), # ๐คง โ e | |
| ("\U0001e928", "f"), # ๐คจ โ f | |
| ("\U0001e929", "g"), # ๐คฉ โ g | |
| ("\U0001e92a", "h"), # ๐คช โ h | |
| ("\U0001e92b", "i"), # ๐คซ โ i | |
| ("\U0001e92c", "j"), # ๐คฌ โ j | |
| ("\U0001e92d", "k"), # ๐คญ โ k | |
| ("\U0001e92e", "l"), # ๐คฎ โ l | |
| ("\U0001e92f", "m"), # ๐คฏ โ m | |
| ("\U0001e930", "n"), # ๐คฐ โ n | |
| ("\U0001e931", "ng"), # ๐คฑ โ ng | |
| ("\U0001e932", "o"), # ๐คฒ โ o | |
| ("\U0001e933", "p"), # ๐คณ โ p | |
| ("\U0001e934", "r"), # ๐คด โ r | |
| ("\U0001e935", "s"), # ๐คต โ s | |
| ("\U0001e936", "t"), # ๐คถ โ t | |
| ("\U0001e937", "u"), # ๐คท โ u | |
| ("\U0001e938", "v"), # ๐คธ โ v | |
| ("\U0001e939", "w"), # ๐คน โ w | |
| ("\U0001e93a", "y"), # ๐คบ โ y | |
| ("\U0001e93b", "z"), # ๐คป โ z | |
| ("\U0001e93c", "kh"), # ๐คผ โ kh | |
| ("\U0001e93d", "qu"), # ๐คฝ โ qu | |
| ("\U0001e93e", "sh"), # ๐คพ โ sh | |
| ("\U0001e93f", "gh"), # ๐คฟ โ gh | |
| ("\U0001e940", "ny"), # ๐ฅ โ ny (ษฒ) | |
| ("\U0001e941", "th"), # ๐ฅ โ th | |
| ("\U0001e942", "wh"), # ๐ฅ โ wh | |
| ("\U0001e943", "ny"), # ๐ฅ โ ny (ษณ) | |
| # Digits | |
| ("\U0001e950", "0"), # ๐ฅ | |
| ("\U0001e951", "1"), # ๐ฅ | |
| ("\U0001e952", "2"), # ๐ฅ | |
| ("\U0001e953", "3"), # ๐ฅ | |
| ("\U0001e954", "4"), # ๐ฅ | |
| ("\U0001e955", "5"), # ๐ฅ | |
| ("\U0001e956", "6"), # ๐ฅ | |
| ("\U0001e957", "7"), # ๐ฅ | |
| ("\U0001e958", "8"), # ๐ฅ | |
| ("\U0001e959", "9"), # ๐ฅ | |
| ] | |
| # Build fast lookup dicts | |
| _A2L: dict[str, str] = {a: l for a, l in _ADLAM_TO_LATIN} | |
| _L2A: dict[str, str] = {} | |
| for _a, _l in reversed(_ADLAM_TO_LATIN): # reversed so single-char wins over digraph | |
| _L2A[_l.lower()] = _a | |
| # Adlam Unicode range for fast detection | |
| _ADLAM_START = 0x1E900 | |
| _ADLAM_END = 0x1E95F | |
| def contains_adlam(text: str) -> bool: | |
| """Return True if text contains any Adlam character.""" | |
| return any(_ADLAM_START <= ord(c) <= _ADLAM_END for c in text) | |
| def adlam_to_latin(text: str) -> str: | |
| """Convert Adlam script characters to Latin romanization. Non-Adlam chars pass through.""" | |
| result = [] | |
| for ch in text: | |
| result.append(_A2L.get(ch, ch)) | |
| return "".join(result) | |
| def latin_to_adlam(text: str) -> str: | |
| """ | |
| Convert Latin romanization to Adlam script. | |
| Handles digraphs (ng, kh, sh, gh, ny, th, wh, qu) before single chars. | |
| """ | |
| text = text.lower() | |
| out = [] | |
| i = 0 | |
| # Digraphs sorted longest-first | |
| digraphs = sorted( | |
| [(k, v) for k, v in _L2A.items() if len(k) == 2], | |
| key=lambda x: -len(x[0]), | |
| ) | |
| while i < len(text): | |
| matched = False | |
| for lat, adl in digraphs: | |
| if text[i:i + len(lat)] == lat: | |
| out.append(adl) | |
| i += len(lat) | |
| matched = True | |
| break | |
| if not matched: | |
| ch = text[i] | |
| out.append(_L2A.get(ch, ch)) | |
| i += 1 | |
| return "".join(out) | |
| def normalize_pular(text: str) -> str: | |
| """ | |
| Canonical pre-processing for Pular (Guinea Fula) ASR training: | |
| 1. Convert Adlam โ Latin if present | |
| 2. Unicode NFC | |
| 3. Lowercase | |
| 4. Collapse whitespace | |
| """ | |
| if contains_adlam(text): | |
| text = adlam_to_latin(text) | |
| text = unicodedata.normalize("NFC", text) | |
| text = text.lower() | |
| text = re.sub(r"\s+", " ", text).strip() | |
| return text | |