scriptconv 0.0.1a5__tar.gz → 0.0.2__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.
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/PKG-INFO +20 -4
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/README.md +19 -3
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv/__init__.py +10 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv/notation.py +312 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv/version.py +2 -2
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv.egg-info/PKG-INFO +20 -4
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/tests/test_examples.py +12 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/tests/test_notation.py +122 -1
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/LICENSE +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/pyproject.toml +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/requirements.txt +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv/__main__.py +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv/py.typed +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv/scripts.py +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv/translit.py +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv.egg-info/SOURCES.txt +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv.egg-info/dependency_links.txt +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv.egg-info/requires.txt +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/scriptconv.egg-info/top_level.txt +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/setup.cfg +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/tests/test_cli.py +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/tests/test_scripts.py +0 -0
- {scriptconv-0.0.1a5 → scriptconv-0.0.2}/tests/test_translit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scriptconv
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: Shared script-conversion core — ISO-15924 detection, IPA↔ARPA/X-SAMPA/Lexique, Buckwalter, Hangul→jamo
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/TigreGotico/scriptconv
|
|
@@ -33,7 +33,7 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
**scriptconv** is a zero-dependency Python library for written-script operations:
|
|
35
35
|
ISO-15924 script identification and metadata, character-range detection, language-to-script
|
|
36
|
-
mapping, phoneme-notation transcoding (IPA ↔ ARPABET
|
|
36
|
+
mapping, phoneme-notation transcoding (IPA ↔ ARPABET / X-SAMPA / Lexique / Kirshenbaum / Cotovía / RFE,
|
|
37
37
|
Buckwalter ↔ Arabic script), and orthographic decomposition of Hangul syllable blocks into
|
|
38
38
|
jamo letters. Every conversion is a pure data table or arithmetic operation; no linguistic
|
|
39
39
|
rules, no external files, no runtime dependencies beyond the Python standard library.
|
|
@@ -75,7 +75,8 @@ from scriptconv import (
|
|
|
75
75
|
xsampa_to_ipa, ipa_to_xsampa,
|
|
76
76
|
buckwalter_to_arabic, arabic_to_buckwalter,
|
|
77
77
|
lexique_to_ipa, ipa_to_lexique,
|
|
78
|
-
kirshenbaum_to_ipa, ipa_to_kirshenbaum,
|
|
78
|
+
kirshenbaum_to_ipa, ipa_to_kirshenbaum, cotovia_to_ipa, ipa_to_cotovia,
|
|
79
|
+
rfe_to_ipa, ipa_to_rfe, looks_like_ipa,
|
|
79
80
|
decompose_hangul, hira_to_kana, kana_to_hira,
|
|
80
81
|
convert, can_convert, convert_batch, Notation, NOTATION_INFO,
|
|
81
82
|
)
|
|
@@ -107,6 +108,15 @@ ipa_to_lexique("vɛ̃") # "v5" (vin)
|
|
|
107
108
|
kirshenbaum_to_ipa("S") # "ʃ" (espeak-ng ASCII-IPA)
|
|
108
109
|
ipa_to_kirshenbaum("ŋ") # "N"
|
|
109
110
|
|
|
111
|
+
cotovia_to_ipa("karro") # "karo" (Galician Cotovía TTS notation)
|
|
112
|
+
ipa_to_cotovia("ʎ") # "L"
|
|
113
|
+
|
|
114
|
+
rfe_to_ipa("kaša") # "kaʃa" (Spanish/Romance RFE alphabet)
|
|
115
|
+
ipa_to_rfe("ɲ") # "ñ"
|
|
116
|
+
|
|
117
|
+
looks_like_ipa("pʰɑtʃ") # True (heuristic: has IPA-distinctive symbols)
|
|
118
|
+
looks_like_ipa("hello") # False
|
|
119
|
+
|
|
110
120
|
decompose_hangul("국민") # "ㄱㅜㄱㅁㅣㄴ" (orthographic jamo, no assimilation)
|
|
111
121
|
hira_to_kana("ひらがな") # "ヒラガナ"
|
|
112
122
|
kana_to_hira("カタカナ") # "かたかな"
|
|
@@ -136,7 +146,7 @@ python -m scriptconv lang ko
|
|
|
136
146
|
| Module | Contents |
|
|
137
147
|
|--------|----------|
|
|
138
148
|
| `scriptconv.scripts` | `Script` dataclass (with `script_type`), `SCRIPT_REGISTRY` (34 scripts), `detect_script`, `char_script`, `script_distribution`, `script_runs`, `base_direction`, `lang_to_script`, `script_to_langs`, `normalize_script_tag` |
|
|
139
|
-
| `scriptconv.notation` | `Notation` enum, `NotationInfo`/`NOTATION_INFO` fidelity registry, `convert` facade, `can_convert` predicate, `convert_batch` generator, pair-wise converters (ARPABET ↔ IPA, X-SAMPA ↔ IPA, Buckwalter ↔ Arabic, Lexique ↔ IPA, Kirshenbaum ↔ IPA) |
|
|
149
|
+
| `scriptconv.notation` | `Notation` enum, `NotationInfo`/`NOTATION_INFO` fidelity registry, `convert` facade, `can_convert` predicate, `convert_batch` generator, pair-wise converters (ARPABET ↔ IPA, X-SAMPA ↔ IPA, Buckwalter ↔ Arabic, Lexique ↔ IPA, Kirshenbaum ↔ IPA, Cotovía ↔ IPA, RFE ↔ IPA), `looks_like_ipa` detector |
|
|
140
150
|
| `scriptconv.translit` | `decompose_hangul` (Hangul blocks → jamo, compatibility or conjoining), `hira_to_kana`/`kana_to_hira` — all orthographic only |
|
|
141
151
|
|
|
142
152
|
## Documentation
|
|
@@ -165,6 +175,8 @@ Runnable scripts in [examples/](examples/):
|
|
|
165
175
|
| `12_kirshenbaum.py` | Kirshenbaum (ASCII-IPA) ↔ IPA, and ARPABET → Kirshenbaum routing |
|
|
166
176
|
| `13_script_runs.py` | Per-script segmentation of mixed-script text |
|
|
167
177
|
| `14_kana_transliteration.py` | Hiragana ↔ Katakana |
|
|
178
|
+
| `15_cotovia.py` | Cotovía (Galician TTS notation) ↔ IPA |
|
|
179
|
+
| `16_rfe.py` | RFE (Spanish/Romance philology alphabet) ↔ IPA |
|
|
168
180
|
|
|
169
181
|
## Fidelity guarantees
|
|
170
182
|
|
|
@@ -179,6 +191,8 @@ round-trip is exact and what happens to a symbol the table does not know.
|
|
|
179
191
|
| **Buckwalter ↔ Arabic** | Exact (the `^` shadda alias normalises to canonical `~`; precomposed lam-alef ligatures decompose to two chars, visually identical) | Exact | Passed through unchanged |
|
|
180
192
|
| **Lexique ↔ IPA** | Exact except the `°`/`3` schwa pair (both → `ə`; reverse always → `°`) | Exact | Passed through unchanged |
|
|
181
193
|
| **Kirshenbaum ↔ IPA** | Exact | **Lossy** — restricted ASCII inventory; IPA outside it passes through | Passed through unchanged |
|
|
194
|
+
| **Cotovía ↔ IPA** | Exact except the three `L`/`Z`/`jj` symbols for `ʎ` normalise to `L` | **Lossy** — Galician/Spanish inventory; IPA outside it passes through | Passed through unchanged |
|
|
195
|
+
| **RFE ↔ IPA** | Exact except `ñ`/`n̮` for `ɲ` normalise to `ñ` | **Lossy** — core Spanish/Romance inventory; IPA outside it passes through | Passed through unchanged |
|
|
182
196
|
|
|
183
197
|
This table is also available programmatically via `NOTATION_INFO`
|
|
184
198
|
(`NotationInfo` records with `lossless_to_ipa`, `lossless_from_ipa`,
|
|
@@ -204,4 +218,6 @@ Derived tables used internally:
|
|
|
204
218
|
| Buckwalter ↔ Arabic | Tim Buckwalter's Arabic transliteration scheme | — (factual 1:1 mapping) |
|
|
205
219
|
| Lexique phoneme codes | New, B. & Pallier, C. — *Manuel de Lexique 3* v3.11, Tableau 2; [chrplr/openlexicon](https://github.com/chrplr/openlexicon) | CC BY-SA 4.0 |
|
|
206
220
|
| Kirshenbaum ↔ IPA | Kirshenbaum 1993 ASCII-IPA standard (comp.speech), cross-checked against espeak-ng | — (factual symbol mapping) |
|
|
221
|
+
| Cotovía ↔ IPA | Universidade de Vigo GTM Cotovía TTS project (`fonemas.cpp`) | — (factual symbol mapping) |
|
|
222
|
+
| RFE ↔ IPA | RFE phonetic alphabet (Revista de Filología Española, 1915) | — (factual symbol mapping) |
|
|
207
223
|
| Hangul jamo tables | [stannam/hangul_to_ipa](https://github.com/stannam/hangul_to_ipa) | — |
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**scriptconv** is a zero-dependency Python library for written-script operations:
|
|
8
8
|
ISO-15924 script identification and metadata, character-range detection, language-to-script
|
|
9
|
-
mapping, phoneme-notation transcoding (IPA ↔ ARPABET
|
|
9
|
+
mapping, phoneme-notation transcoding (IPA ↔ ARPABET / X-SAMPA / Lexique / Kirshenbaum / Cotovía / RFE,
|
|
10
10
|
Buckwalter ↔ Arabic script), and orthographic decomposition of Hangul syllable blocks into
|
|
11
11
|
jamo letters. Every conversion is a pure data table or arithmetic operation; no linguistic
|
|
12
12
|
rules, no external files, no runtime dependencies beyond the Python standard library.
|
|
@@ -48,7 +48,8 @@ from scriptconv import (
|
|
|
48
48
|
xsampa_to_ipa, ipa_to_xsampa,
|
|
49
49
|
buckwalter_to_arabic, arabic_to_buckwalter,
|
|
50
50
|
lexique_to_ipa, ipa_to_lexique,
|
|
51
|
-
kirshenbaum_to_ipa, ipa_to_kirshenbaum,
|
|
51
|
+
kirshenbaum_to_ipa, ipa_to_kirshenbaum, cotovia_to_ipa, ipa_to_cotovia,
|
|
52
|
+
rfe_to_ipa, ipa_to_rfe, looks_like_ipa,
|
|
52
53
|
decompose_hangul, hira_to_kana, kana_to_hira,
|
|
53
54
|
convert, can_convert, convert_batch, Notation, NOTATION_INFO,
|
|
54
55
|
)
|
|
@@ -80,6 +81,15 @@ ipa_to_lexique("vɛ̃") # "v5" (vin)
|
|
|
80
81
|
kirshenbaum_to_ipa("S") # "ʃ" (espeak-ng ASCII-IPA)
|
|
81
82
|
ipa_to_kirshenbaum("ŋ") # "N"
|
|
82
83
|
|
|
84
|
+
cotovia_to_ipa("karro") # "karo" (Galician Cotovía TTS notation)
|
|
85
|
+
ipa_to_cotovia("ʎ") # "L"
|
|
86
|
+
|
|
87
|
+
rfe_to_ipa("kaša") # "kaʃa" (Spanish/Romance RFE alphabet)
|
|
88
|
+
ipa_to_rfe("ɲ") # "ñ"
|
|
89
|
+
|
|
90
|
+
looks_like_ipa("pʰɑtʃ") # True (heuristic: has IPA-distinctive symbols)
|
|
91
|
+
looks_like_ipa("hello") # False
|
|
92
|
+
|
|
83
93
|
decompose_hangul("국민") # "ㄱㅜㄱㅁㅣㄴ" (orthographic jamo, no assimilation)
|
|
84
94
|
hira_to_kana("ひらがな") # "ヒラガナ"
|
|
85
95
|
kana_to_hira("カタカナ") # "かたかな"
|
|
@@ -109,7 +119,7 @@ python -m scriptconv lang ko
|
|
|
109
119
|
| Module | Contents |
|
|
110
120
|
|--------|----------|
|
|
111
121
|
| `scriptconv.scripts` | `Script` dataclass (with `script_type`), `SCRIPT_REGISTRY` (34 scripts), `detect_script`, `char_script`, `script_distribution`, `script_runs`, `base_direction`, `lang_to_script`, `script_to_langs`, `normalize_script_tag` |
|
|
112
|
-
| `scriptconv.notation` | `Notation` enum, `NotationInfo`/`NOTATION_INFO` fidelity registry, `convert` facade, `can_convert` predicate, `convert_batch` generator, pair-wise converters (ARPABET ↔ IPA, X-SAMPA ↔ IPA, Buckwalter ↔ Arabic, Lexique ↔ IPA, Kirshenbaum ↔ IPA) |
|
|
122
|
+
| `scriptconv.notation` | `Notation` enum, `NotationInfo`/`NOTATION_INFO` fidelity registry, `convert` facade, `can_convert` predicate, `convert_batch` generator, pair-wise converters (ARPABET ↔ IPA, X-SAMPA ↔ IPA, Buckwalter ↔ Arabic, Lexique ↔ IPA, Kirshenbaum ↔ IPA, Cotovía ↔ IPA, RFE ↔ IPA), `looks_like_ipa` detector |
|
|
113
123
|
| `scriptconv.translit` | `decompose_hangul` (Hangul blocks → jamo, compatibility or conjoining), `hira_to_kana`/`kana_to_hira` — all orthographic only |
|
|
114
124
|
|
|
115
125
|
## Documentation
|
|
@@ -138,6 +148,8 @@ Runnable scripts in [examples/](examples/):
|
|
|
138
148
|
| `12_kirshenbaum.py` | Kirshenbaum (ASCII-IPA) ↔ IPA, and ARPABET → Kirshenbaum routing |
|
|
139
149
|
| `13_script_runs.py` | Per-script segmentation of mixed-script text |
|
|
140
150
|
| `14_kana_transliteration.py` | Hiragana ↔ Katakana |
|
|
151
|
+
| `15_cotovia.py` | Cotovía (Galician TTS notation) ↔ IPA |
|
|
152
|
+
| `16_rfe.py` | RFE (Spanish/Romance philology alphabet) ↔ IPA |
|
|
141
153
|
|
|
142
154
|
## Fidelity guarantees
|
|
143
155
|
|
|
@@ -152,6 +164,8 @@ round-trip is exact and what happens to a symbol the table does not know.
|
|
|
152
164
|
| **Buckwalter ↔ Arabic** | Exact (the `^` shadda alias normalises to canonical `~`; precomposed lam-alef ligatures decompose to two chars, visually identical) | Exact | Passed through unchanged |
|
|
153
165
|
| **Lexique ↔ IPA** | Exact except the `°`/`3` schwa pair (both → `ə`; reverse always → `°`) | Exact | Passed through unchanged |
|
|
154
166
|
| **Kirshenbaum ↔ IPA** | Exact | **Lossy** — restricted ASCII inventory; IPA outside it passes through | Passed through unchanged |
|
|
167
|
+
| **Cotovía ↔ IPA** | Exact except the three `L`/`Z`/`jj` symbols for `ʎ` normalise to `L` | **Lossy** — Galician/Spanish inventory; IPA outside it passes through | Passed through unchanged |
|
|
168
|
+
| **RFE ↔ IPA** | Exact except `ñ`/`n̮` for `ɲ` normalise to `ñ` | **Lossy** — core Spanish/Romance inventory; IPA outside it passes through | Passed through unchanged |
|
|
155
169
|
|
|
156
170
|
This table is also available programmatically via `NOTATION_INFO`
|
|
157
171
|
(`NotationInfo` records with `lossless_to_ipa`, `lossless_from_ipa`,
|
|
@@ -177,4 +191,6 @@ Derived tables used internally:
|
|
|
177
191
|
| Buckwalter ↔ Arabic | Tim Buckwalter's Arabic transliteration scheme | — (factual 1:1 mapping) |
|
|
178
192
|
| Lexique phoneme codes | New, B. & Pallier, C. — *Manuel de Lexique 3* v3.11, Tableau 2; [chrplr/openlexicon](https://github.com/chrplr/openlexicon) | CC BY-SA 4.0 |
|
|
179
193
|
| Kirshenbaum ↔ IPA | Kirshenbaum 1993 ASCII-IPA standard (comp.speech), cross-checked against espeak-ng | — (factual symbol mapping) |
|
|
194
|
+
| Cotovía ↔ IPA | Universidade de Vigo GTM Cotovía TTS project (`fonemas.cpp`) | — (factual symbol mapping) |
|
|
195
|
+
| RFE ↔ IPA | RFE phonetic alphabet (Revista de Filología Española, 1915) | — (factual symbol mapping) |
|
|
180
196
|
| Hangul jamo tables | [stannam/hangul_to_ipa](https://github.com/stannam/hangul_to_ipa) | — |
|
|
@@ -50,6 +50,11 @@ from scriptconv.notation import (
|
|
|
50
50
|
ipa_to_lexique,
|
|
51
51
|
kirshenbaum_to_ipa,
|
|
52
52
|
ipa_to_kirshenbaum,
|
|
53
|
+
cotovia_to_ipa,
|
|
54
|
+
ipa_to_cotovia,
|
|
55
|
+
rfe_to_ipa,
|
|
56
|
+
ipa_to_rfe,
|
|
57
|
+
looks_like_ipa,
|
|
53
58
|
)
|
|
54
59
|
from scriptconv.translit import (
|
|
55
60
|
decompose_hangul,
|
|
@@ -86,6 +91,11 @@ __all__ = [
|
|
|
86
91
|
"ipa_to_lexique",
|
|
87
92
|
"kirshenbaum_to_ipa",
|
|
88
93
|
"ipa_to_kirshenbaum",
|
|
94
|
+
"cotovia_to_ipa",
|
|
95
|
+
"ipa_to_cotovia",
|
|
96
|
+
"rfe_to_ipa",
|
|
97
|
+
"ipa_to_rfe",
|
|
98
|
+
"looks_like_ipa",
|
|
89
99
|
# translit
|
|
90
100
|
"decompose_hangul",
|
|
91
101
|
"hira_to_kana",
|
|
@@ -37,6 +37,11 @@ __all__ = [
|
|
|
37
37
|
"ipa_to_lexique",
|
|
38
38
|
"kirshenbaum_to_ipa",
|
|
39
39
|
"ipa_to_kirshenbaum",
|
|
40
|
+
"cotovia_to_ipa",
|
|
41
|
+
"ipa_to_cotovia",
|
|
42
|
+
"rfe_to_ipa",
|
|
43
|
+
"ipa_to_rfe",
|
|
44
|
+
"looks_like_ipa",
|
|
40
45
|
]
|
|
41
46
|
|
|
42
47
|
|
|
@@ -50,6 +55,8 @@ class Notation(str, Enum):
|
|
|
50
55
|
ARABIC = "arabic" # Arabic script (target/source for BW)
|
|
51
56
|
LEXIQUE = "lexique" # Lexique one-char-per-phoneme French notation
|
|
52
57
|
KIRSHENBAUM = "kirshenbaum" # ASCII-IPA (espeak-ng native notation)
|
|
58
|
+
COTOVIA = "cotovia" # Universidade de Vigo Cotovía TTS notation (gl)
|
|
59
|
+
RFE = "rfe" # Revista de Filología Española phonetic alphabet
|
|
53
60
|
|
|
54
61
|
def __repr__(self) -> str:
|
|
55
62
|
return f"Notation.{self.name}"
|
|
@@ -664,6 +671,244 @@ def ipa_to_kirshenbaum(ipa: str) -> str:
|
|
|
664
671
|
return "".join(_IPA_TO_KIRSHENBAUM.get(ch, ch) for ch in ipa)
|
|
665
672
|
|
|
666
673
|
|
|
674
|
+
# ---------------------------------------------------------------------------
|
|
675
|
+
# Cotovía ↔ IPA
|
|
676
|
+
#
|
|
677
|
+
# Cotovía is the internal phoneme notation of the Universidade de Vigo GTM
|
|
678
|
+
# Cotovía TTS system (Galician/Spanish). The table is the phoneme symbol map
|
|
679
|
+
# from its ``fonemas.cpp``. Symbols are matched longest-first (``tS``, ``rr``
|
|
680
|
+
# before single characters). The silence/pause marker (``#``) is a boundary
|
|
681
|
+
# token, not a phoneme, and is intentionally excluded.
|
|
682
|
+
# ---------------------------------------------------------------------------
|
|
683
|
+
|
|
684
|
+
_COTOVIA_TO_IPA: dict[str, str] = {
|
|
685
|
+
# Double-character symbols first (longest-match ordering)
|
|
686
|
+
"tS": "tʃ",
|
|
687
|
+
"rr": "r", # trill (single "r" is the tap ɾ)
|
|
688
|
+
"jj": "ʎ",
|
|
689
|
+
# Consonants
|
|
690
|
+
"B": "β",
|
|
691
|
+
"D": "ð",
|
|
692
|
+
"G": "ɣ",
|
|
693
|
+
"J": "ɲ",
|
|
694
|
+
"N": "ŋ",
|
|
695
|
+
"S": "ʃ",
|
|
696
|
+
"T": "θ",
|
|
697
|
+
"Z": "ʎ",
|
|
698
|
+
"L": "ʎ", # canonical Cotovía symbol for the palatal lateral
|
|
699
|
+
"b": "b",
|
|
700
|
+
"d": "d",
|
|
701
|
+
"f": "f",
|
|
702
|
+
"g": "ɡ",
|
|
703
|
+
"k": "k",
|
|
704
|
+
"l": "l",
|
|
705
|
+
"m": "m",
|
|
706
|
+
"n": "n",
|
|
707
|
+
"p": "p",
|
|
708
|
+
"r": "ɾ", # tap
|
|
709
|
+
"s": "s",
|
|
710
|
+
"t": "t",
|
|
711
|
+
"x": "x",
|
|
712
|
+
"X": "x",
|
|
713
|
+
# Vowels
|
|
714
|
+
"E": "ɛ",
|
|
715
|
+
"O": "ɔ",
|
|
716
|
+
"a": "a",
|
|
717
|
+
"e": "e",
|
|
718
|
+
"i": "i",
|
|
719
|
+
"o": "o",
|
|
720
|
+
"u": "u",
|
|
721
|
+
# Glides
|
|
722
|
+
"j": "j",
|
|
723
|
+
"w": "w",
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
# Reverse: IPA → Cotovía. First-wins deduplicates the symbols that share an IPA
|
|
727
|
+
# value; pin ʎ → "L" (the standard Cotovía palatal-lateral symbol) and x → "x".
|
|
728
|
+
_IPA_TO_COTOVIA: dict[str, str] = {}
|
|
729
|
+
for _cv, _ip in _COTOVIA_TO_IPA.items():
|
|
730
|
+
_IPA_TO_COTOVIA.setdefault(_ip, _cv)
|
|
731
|
+
_IPA_TO_COTOVIA["ʎ"] = "L"
|
|
732
|
+
_IPA_TO_COTOVIA["x"] = "x"
|
|
733
|
+
|
|
734
|
+
_CV_KEYS_SORTED = sorted(_COTOVIA_TO_IPA.keys(), key=len, reverse=True)
|
|
735
|
+
_CV_RE = re.compile("|".join(re.escape(s) for s in _CV_KEYS_SORTED))
|
|
736
|
+
_IPA_CV_KEYS_SORTED = sorted(_IPA_TO_COTOVIA.keys(), key=len, reverse=True)
|
|
737
|
+
_IPA_CV_RE = re.compile("|".join(re.escape(s) for s in _IPA_CV_KEYS_SORTED))
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
def cotovia_to_ipa(cotovia: str) -> str:
|
|
741
|
+
"""Convert a Cotovía phoneme string to IPA.
|
|
742
|
+
|
|
743
|
+
Multi-character symbols are matched longest-first. Characters outside the
|
|
744
|
+
table pass through unchanged.
|
|
745
|
+
|
|
746
|
+
Examples
|
|
747
|
+
--------
|
|
748
|
+
>>> cotovia_to_ipa("tS")
|
|
749
|
+
'tʃ'
|
|
750
|
+
>>> cotovia_to_ipa("karro")
|
|
751
|
+
'karo'
|
|
752
|
+
"""
|
|
753
|
+
result = []
|
|
754
|
+
pos = 0
|
|
755
|
+
while pos < len(cotovia):
|
|
756
|
+
m = _CV_RE.match(cotovia, pos)
|
|
757
|
+
if m:
|
|
758
|
+
result.append(_COTOVIA_TO_IPA[m.group(0)])
|
|
759
|
+
pos = m.end()
|
|
760
|
+
else:
|
|
761
|
+
result.append(cotovia[pos])
|
|
762
|
+
pos += 1
|
|
763
|
+
return "".join(result)
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def ipa_to_cotovia(ipa: str) -> str:
|
|
767
|
+
"""Convert an IPA string to Cotovía notation.
|
|
768
|
+
|
|
769
|
+
Multi-character IPA symbols are matched longest-first. Characters outside
|
|
770
|
+
the table pass through unchanged.
|
|
771
|
+
|
|
772
|
+
Examples
|
|
773
|
+
--------
|
|
774
|
+
>>> ipa_to_cotovia("tʃ")
|
|
775
|
+
'tS'
|
|
776
|
+
>>> ipa_to_cotovia("ʎ")
|
|
777
|
+
'L'
|
|
778
|
+
"""
|
|
779
|
+
result = []
|
|
780
|
+
pos = 0
|
|
781
|
+
while pos < len(ipa):
|
|
782
|
+
m = _IPA_CV_RE.match(ipa, pos)
|
|
783
|
+
if m:
|
|
784
|
+
result.append(_IPA_TO_COTOVIA[m.group(0)])
|
|
785
|
+
pos = m.end()
|
|
786
|
+
else:
|
|
787
|
+
result.append(ipa[pos])
|
|
788
|
+
pos += 1
|
|
789
|
+
return "".join(result)
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
# ---------------------------------------------------------------------------
|
|
793
|
+
# RFE (Revista de Filología Española) ↔ IPA
|
|
794
|
+
#
|
|
795
|
+
# The RFE phonetic alphabet (Navarro Tomás, Revista de Filología Española,
|
|
796
|
+
# 1915) is the traditional transcription system of Spanish and Romance
|
|
797
|
+
# philology. This is its core, well-attested inventory; rare interdental and
|
|
798
|
+
# variant diacritic symbols from the full standard are omitted. Symbols are
|
|
799
|
+
# matched longest-first (combining-mark symbols before their base letters).
|
|
800
|
+
# Reference: RFE Phonetic Alphabet
|
|
801
|
+
# (https://en.wikipedia.org/wiki/RFE_Phonetic_Alphabet).
|
|
802
|
+
# ---------------------------------------------------------------------------
|
|
803
|
+
|
|
804
|
+
_RFE_TO_IPA: dict[str, str] = {
|
|
805
|
+
# Combining-mark symbols (longest-match first)
|
|
806
|
+
"r̄": "r", # r + macron = alveolar trill (plain r = tap ɾ)
|
|
807
|
+
"n̮": "ɲ", # n + breve below = palatal nasal ɲ
|
|
808
|
+
"l̮": "ʎ", # l + breve below = palatal lateral ʎ
|
|
809
|
+
# Palatal / postalveolar
|
|
810
|
+
"ĉ": "tʃ", # ĉ = tʃ
|
|
811
|
+
"š": "ʃ", # š = ʃ
|
|
812
|
+
"ž": "ʒ", # ž = ʒ
|
|
813
|
+
"ŷ": "ɟʝ", # ŷ = ɟʝ (voiced palatal affricate)
|
|
814
|
+
"ñ": "ɲ", # ñ = ɲ
|
|
815
|
+
"y": "ʝ", # y = ʝ
|
|
816
|
+
"j": "j", # j = j
|
|
817
|
+
# Spanish approximant/fricative allophones
|
|
818
|
+
"ƀ": "β", # ƀ = β
|
|
819
|
+
"đ": "ð", # đ = ð
|
|
820
|
+
"ǥ": "ɣ", # ǥ = ɣ
|
|
821
|
+
# Other consonants
|
|
822
|
+
"θ": "θ", # θ = θ
|
|
823
|
+
"x": "x",
|
|
824
|
+
"ł": "ɫ", # ł = ɫ (velarized l)
|
|
825
|
+
"ŋ": "ŋ", # ŋ = ŋ
|
|
826
|
+
"w": "w",
|
|
827
|
+
"h": "h",
|
|
828
|
+
"b": "b",
|
|
829
|
+
"p": "p",
|
|
830
|
+
"t": "t",
|
|
831
|
+
"d": "d",
|
|
832
|
+
"k": "k",
|
|
833
|
+
"g": "ɡ", # g = ɡ (script g, matching the other tables)
|
|
834
|
+
"f": "f",
|
|
835
|
+
"v": "v",
|
|
836
|
+
"s": "s",
|
|
837
|
+
"z": "z",
|
|
838
|
+
"m": "m",
|
|
839
|
+
"n": "n",
|
|
840
|
+
"l": "l",
|
|
841
|
+
"r": "ɾ", # r = tap ɾ
|
|
842
|
+
# Vowels
|
|
843
|
+
"a": "a", "e": "e", "i": "i", "o": "o", "u": "u",
|
|
844
|
+
"ə": "ə", # ə = ə
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
# Reverse: IPA → RFE. First-wins deduplicates; pin the canonical RFE spellings
|
|
848
|
+
# for the palatal nasal (ñ) and the trill/tap pair.
|
|
849
|
+
_IPA_TO_RFE: dict[str, str] = {}
|
|
850
|
+
for _rfe, _ip in _RFE_TO_IPA.items():
|
|
851
|
+
_IPA_TO_RFE.setdefault(_ip, _rfe)
|
|
852
|
+
_IPA_TO_RFE["ɲ"] = "ñ" # ɲ → ñ (canonical, not the n̮ variant)
|
|
853
|
+
|
|
854
|
+
_RFE_KEYS_SORTED = sorted(_RFE_TO_IPA.keys(), key=len, reverse=True)
|
|
855
|
+
_RFE_RE = re.compile("|".join(re.escape(x) for x in _RFE_KEYS_SORTED))
|
|
856
|
+
_IPA_RFE_KEYS_SORTED = sorted(_IPA_TO_RFE.keys(), key=len, reverse=True)
|
|
857
|
+
_IPA_RFE_RE = re.compile("|".join(re.escape(x) for x in _IPA_RFE_KEYS_SORTED))
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
def rfe_to_ipa(rfe: str) -> str:
|
|
861
|
+
"""Convert an RFE (Revista de Filología Española) string to IPA.
|
|
862
|
+
|
|
863
|
+
Multi-character symbols are matched longest-first. Characters outside the
|
|
864
|
+
table pass through unchanged.
|
|
865
|
+
|
|
866
|
+
Examples
|
|
867
|
+
--------
|
|
868
|
+
>>> rfe_to_ipa("š")
|
|
869
|
+
'ʃ'
|
|
870
|
+
>>> rfe_to_ipa("kaša")
|
|
871
|
+
'kaʃa'
|
|
872
|
+
"""
|
|
873
|
+
result = []
|
|
874
|
+
pos = 0
|
|
875
|
+
while pos < len(rfe):
|
|
876
|
+
m = _RFE_RE.match(rfe, pos)
|
|
877
|
+
if m:
|
|
878
|
+
result.append(_RFE_TO_IPA[m.group(0)])
|
|
879
|
+
pos = m.end()
|
|
880
|
+
else:
|
|
881
|
+
result.append(rfe[pos])
|
|
882
|
+
pos += 1
|
|
883
|
+
return "".join(result)
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
def ipa_to_rfe(ipa: str) -> str:
|
|
887
|
+
"""Convert an IPA string to RFE (Revista de Filología Española) notation.
|
|
888
|
+
|
|
889
|
+
Multi-character IPA symbols are matched longest-first. Characters outside
|
|
890
|
+
the table pass through unchanged.
|
|
891
|
+
|
|
892
|
+
Examples
|
|
893
|
+
--------
|
|
894
|
+
>>> ipa_to_rfe("ʃ")
|
|
895
|
+
'š'
|
|
896
|
+
>>> ipa_to_rfe("ɲ")
|
|
897
|
+
'ñ'
|
|
898
|
+
"""
|
|
899
|
+
result = []
|
|
900
|
+
pos = 0
|
|
901
|
+
while pos < len(ipa):
|
|
902
|
+
m = _IPA_RFE_RE.match(ipa, pos)
|
|
903
|
+
if m:
|
|
904
|
+
result.append(_IPA_TO_RFE[m.group(0)])
|
|
905
|
+
pos = m.end()
|
|
906
|
+
else:
|
|
907
|
+
result.append(ipa[pos])
|
|
908
|
+
pos += 1
|
|
909
|
+
return "".join(result)
|
|
910
|
+
|
|
911
|
+
|
|
667
912
|
# ---------------------------------------------------------------------------
|
|
668
913
|
# convert — facade routing through IPA where no direct map exists
|
|
669
914
|
# ---------------------------------------------------------------------------
|
|
@@ -734,12 +979,16 @@ _TO_IPA: dict[Notation, "callable"] = {
|
|
|
734
979
|
Notation.XSAMPA: xsampa_to_ipa,
|
|
735
980
|
Notation.LEXIQUE: lexique_to_ipa,
|
|
736
981
|
Notation.KIRSHENBAUM: kirshenbaum_to_ipa,
|
|
982
|
+
Notation.COTOVIA: cotovia_to_ipa,
|
|
983
|
+
Notation.RFE: rfe_to_ipa,
|
|
737
984
|
}
|
|
738
985
|
_FROM_IPA: dict[Notation, "callable"] = {
|
|
739
986
|
Notation.ARPA: ipa_to_arpa,
|
|
740
987
|
Notation.XSAMPA: ipa_to_xsampa,
|
|
741
988
|
Notation.LEXIQUE: ipa_to_lexique,
|
|
742
989
|
Notation.KIRSHENBAUM: ipa_to_kirshenbaum,
|
|
990
|
+
Notation.COTOVIA: ipa_to_cotovia,
|
|
991
|
+
Notation.RFE: ipa_to_rfe,
|
|
743
992
|
}
|
|
744
993
|
_DIRECT_PAIRS: dict[tuple[Notation, Notation], "callable"] = {
|
|
745
994
|
(Notation.BUCKWALTER, Notation.ARABIC): buckwalter_to_arabic,
|
|
@@ -845,6 +1094,16 @@ NOTATION_INFO: dict[Notation, NotationInfo] = {
|
|
|
845
1094
|
token_separated=False,
|
|
846
1095
|
reference="Kirshenbaum 1993 ASCII-IPA standard (comp.speech)",
|
|
847
1096
|
),
|
|
1097
|
+
Notation.COTOVIA: NotationInfo(
|
|
1098
|
+
Notation.COTOVIA, lossless_to_ipa=False, lossless_from_ipa=False,
|
|
1099
|
+
token_separated=False,
|
|
1100
|
+
reference="Universidade de Vigo GTM Cotovía TTS (fonemas.cpp)",
|
|
1101
|
+
),
|
|
1102
|
+
Notation.RFE: NotationInfo(
|
|
1103
|
+
Notation.RFE, lossless_to_ipa=False, lossless_from_ipa=False,
|
|
1104
|
+
token_separated=False,
|
|
1105
|
+
reference="RFE phonetic alphabet (Revista de Filología Española, 1915)",
|
|
1106
|
+
),
|
|
848
1107
|
Notation.BUCKWALTER: NotationInfo(
|
|
849
1108
|
Notation.BUCKWALTER, lossless_to_ipa=True, lossless_from_ipa=False,
|
|
850
1109
|
token_separated=False,
|
|
@@ -894,3 +1153,56 @@ def convert_batch(
|
|
|
894
1153
|
yield stripped
|
|
895
1154
|
else:
|
|
896
1155
|
yield convert(stripped, _src, _dst)
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
# ---------------------------------------------------------------------------
|
|
1159
|
+
# looks_like_ipa — heuristic notation detection
|
|
1160
|
+
# ---------------------------------------------------------------------------
|
|
1161
|
+
|
|
1162
|
+
# Unicode blocks/characters that are distinctive to IPA transcription. Basic
|
|
1163
|
+
# Latin letters (p, a, t, …) and the Greek letters IPA borrows (β, θ, χ) are
|
|
1164
|
+
# deliberately excluded: they are indistinguishable from ordinary Latin/Greek
|
|
1165
|
+
# text, so they carry no signal.
|
|
1166
|
+
_IPA_DISTINCTIVE_RANGES = (
|
|
1167
|
+
(0x0250, 0x02AF), # IPA Extensions
|
|
1168
|
+
(0x02B0, 0x02FF), # Spacing Modifier Letters (ˈ ˌ ː ˑ ʰ ʲ …)
|
|
1169
|
+
(0x0363, 0x036F), # Combining diacritics used in IPA
|
|
1170
|
+
(0x1D00, 0x1D7F), # Phonetic Extensions
|
|
1171
|
+
(0x1D80, 0x1DBF), # Phonetic Extensions Supplement
|
|
1172
|
+
)
|
|
1173
|
+
# Individual combining marks common in IPA (nasalisation, syllabicity, etc.).
|
|
1174
|
+
_IPA_DISTINCTIVE_CHARS = frozenset("̩̪̥̃̊͜͡")
|
|
1175
|
+
|
|
1176
|
+
|
|
1177
|
+
def _is_ipa_distinctive(ch: str) -> bool:
|
|
1178
|
+
cp = ord(ch)
|
|
1179
|
+
if ch in _IPA_DISTINCTIVE_CHARS:
|
|
1180
|
+
return True
|
|
1181
|
+
return any(lo <= cp <= hi for lo, hi in _IPA_DISTINCTIVE_RANGES)
|
|
1182
|
+
|
|
1183
|
+
|
|
1184
|
+
def looks_like_ipa(text: str) -> bool:
|
|
1185
|
+
"""Heuristically decide whether *text* is an IPA transcription.
|
|
1186
|
+
|
|
1187
|
+
Returns ``True`` when *text* contains at least one character that is
|
|
1188
|
+
distinctive to IPA — from the IPA Extensions, Spacing Modifier Letters,
|
|
1189
|
+
or Phonetic Extensions blocks, or an IPA combining diacritic (e.g.
|
|
1190
|
+
``ɑ``, ``ʃ``, ``ː``, ``ˈ``, nasalisation).
|
|
1191
|
+
|
|
1192
|
+
This is a positive-signal heuristic, not a classifier: a transcription
|
|
1193
|
+
written only with characters IPA shares with the Latin alphabet
|
|
1194
|
+
(``"pat"``, ``"bad"``) has no distinctive marker and returns ``False``.
|
|
1195
|
+
It cannot be otherwise — those characters are the same codepoints as
|
|
1196
|
+
ordinary text. Use it to *guard* against feeding IPA where orthography
|
|
1197
|
+
is expected, not to prove a string is not IPA.
|
|
1198
|
+
|
|
1199
|
+
Examples
|
|
1200
|
+
--------
|
|
1201
|
+
>>> looks_like_ipa("pʰɑtʃ")
|
|
1202
|
+
True
|
|
1203
|
+
>>> looks_like_ipa("ˈhɛloʊ")
|
|
1204
|
+
True
|
|
1205
|
+
>>> looks_like_ipa("hello")
|
|
1206
|
+
False
|
|
1207
|
+
"""
|
|
1208
|
+
return any(_is_ipa_distinctive(ch) for ch in text)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scriptconv
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: Shared script-conversion core — ISO-15924 detection, IPA↔ARPA/X-SAMPA/Lexique, Buckwalter, Hangul→jamo
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/TigreGotico/scriptconv
|
|
@@ -33,7 +33,7 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
**scriptconv** is a zero-dependency Python library for written-script operations:
|
|
35
35
|
ISO-15924 script identification and metadata, character-range detection, language-to-script
|
|
36
|
-
mapping, phoneme-notation transcoding (IPA ↔ ARPABET
|
|
36
|
+
mapping, phoneme-notation transcoding (IPA ↔ ARPABET / X-SAMPA / Lexique / Kirshenbaum / Cotovía / RFE,
|
|
37
37
|
Buckwalter ↔ Arabic script), and orthographic decomposition of Hangul syllable blocks into
|
|
38
38
|
jamo letters. Every conversion is a pure data table or arithmetic operation; no linguistic
|
|
39
39
|
rules, no external files, no runtime dependencies beyond the Python standard library.
|
|
@@ -75,7 +75,8 @@ from scriptconv import (
|
|
|
75
75
|
xsampa_to_ipa, ipa_to_xsampa,
|
|
76
76
|
buckwalter_to_arabic, arabic_to_buckwalter,
|
|
77
77
|
lexique_to_ipa, ipa_to_lexique,
|
|
78
|
-
kirshenbaum_to_ipa, ipa_to_kirshenbaum,
|
|
78
|
+
kirshenbaum_to_ipa, ipa_to_kirshenbaum, cotovia_to_ipa, ipa_to_cotovia,
|
|
79
|
+
rfe_to_ipa, ipa_to_rfe, looks_like_ipa,
|
|
79
80
|
decompose_hangul, hira_to_kana, kana_to_hira,
|
|
80
81
|
convert, can_convert, convert_batch, Notation, NOTATION_INFO,
|
|
81
82
|
)
|
|
@@ -107,6 +108,15 @@ ipa_to_lexique("vɛ̃") # "v5" (vin)
|
|
|
107
108
|
kirshenbaum_to_ipa("S") # "ʃ" (espeak-ng ASCII-IPA)
|
|
108
109
|
ipa_to_kirshenbaum("ŋ") # "N"
|
|
109
110
|
|
|
111
|
+
cotovia_to_ipa("karro") # "karo" (Galician Cotovía TTS notation)
|
|
112
|
+
ipa_to_cotovia("ʎ") # "L"
|
|
113
|
+
|
|
114
|
+
rfe_to_ipa("kaša") # "kaʃa" (Spanish/Romance RFE alphabet)
|
|
115
|
+
ipa_to_rfe("ɲ") # "ñ"
|
|
116
|
+
|
|
117
|
+
looks_like_ipa("pʰɑtʃ") # True (heuristic: has IPA-distinctive symbols)
|
|
118
|
+
looks_like_ipa("hello") # False
|
|
119
|
+
|
|
110
120
|
decompose_hangul("국민") # "ㄱㅜㄱㅁㅣㄴ" (orthographic jamo, no assimilation)
|
|
111
121
|
hira_to_kana("ひらがな") # "ヒラガナ"
|
|
112
122
|
kana_to_hira("カタカナ") # "かたかな"
|
|
@@ -136,7 +146,7 @@ python -m scriptconv lang ko
|
|
|
136
146
|
| Module | Contents |
|
|
137
147
|
|--------|----------|
|
|
138
148
|
| `scriptconv.scripts` | `Script` dataclass (with `script_type`), `SCRIPT_REGISTRY` (34 scripts), `detect_script`, `char_script`, `script_distribution`, `script_runs`, `base_direction`, `lang_to_script`, `script_to_langs`, `normalize_script_tag` |
|
|
139
|
-
| `scriptconv.notation` | `Notation` enum, `NotationInfo`/`NOTATION_INFO` fidelity registry, `convert` facade, `can_convert` predicate, `convert_batch` generator, pair-wise converters (ARPABET ↔ IPA, X-SAMPA ↔ IPA, Buckwalter ↔ Arabic, Lexique ↔ IPA, Kirshenbaum ↔ IPA) |
|
|
149
|
+
| `scriptconv.notation` | `Notation` enum, `NotationInfo`/`NOTATION_INFO` fidelity registry, `convert` facade, `can_convert` predicate, `convert_batch` generator, pair-wise converters (ARPABET ↔ IPA, X-SAMPA ↔ IPA, Buckwalter ↔ Arabic, Lexique ↔ IPA, Kirshenbaum ↔ IPA, Cotovía ↔ IPA, RFE ↔ IPA), `looks_like_ipa` detector |
|
|
140
150
|
| `scriptconv.translit` | `decompose_hangul` (Hangul blocks → jamo, compatibility or conjoining), `hira_to_kana`/`kana_to_hira` — all orthographic only |
|
|
141
151
|
|
|
142
152
|
## Documentation
|
|
@@ -165,6 +175,8 @@ Runnable scripts in [examples/](examples/):
|
|
|
165
175
|
| `12_kirshenbaum.py` | Kirshenbaum (ASCII-IPA) ↔ IPA, and ARPABET → Kirshenbaum routing |
|
|
166
176
|
| `13_script_runs.py` | Per-script segmentation of mixed-script text |
|
|
167
177
|
| `14_kana_transliteration.py` | Hiragana ↔ Katakana |
|
|
178
|
+
| `15_cotovia.py` | Cotovía (Galician TTS notation) ↔ IPA |
|
|
179
|
+
| `16_rfe.py` | RFE (Spanish/Romance philology alphabet) ↔ IPA |
|
|
168
180
|
|
|
169
181
|
## Fidelity guarantees
|
|
170
182
|
|
|
@@ -179,6 +191,8 @@ round-trip is exact and what happens to a symbol the table does not know.
|
|
|
179
191
|
| **Buckwalter ↔ Arabic** | Exact (the `^` shadda alias normalises to canonical `~`; precomposed lam-alef ligatures decompose to two chars, visually identical) | Exact | Passed through unchanged |
|
|
180
192
|
| **Lexique ↔ IPA** | Exact except the `°`/`3` schwa pair (both → `ə`; reverse always → `°`) | Exact | Passed through unchanged |
|
|
181
193
|
| **Kirshenbaum ↔ IPA** | Exact | **Lossy** — restricted ASCII inventory; IPA outside it passes through | Passed through unchanged |
|
|
194
|
+
| **Cotovía ↔ IPA** | Exact except the three `L`/`Z`/`jj` symbols for `ʎ` normalise to `L` | **Lossy** — Galician/Spanish inventory; IPA outside it passes through | Passed through unchanged |
|
|
195
|
+
| **RFE ↔ IPA** | Exact except `ñ`/`n̮` for `ɲ` normalise to `ñ` | **Lossy** — core Spanish/Romance inventory; IPA outside it passes through | Passed through unchanged |
|
|
182
196
|
|
|
183
197
|
This table is also available programmatically via `NOTATION_INFO`
|
|
184
198
|
(`NotationInfo` records with `lossless_to_ipa`, `lossless_from_ipa`,
|
|
@@ -204,4 +218,6 @@ Derived tables used internally:
|
|
|
204
218
|
| Buckwalter ↔ Arabic | Tim Buckwalter's Arabic transliteration scheme | — (factual 1:1 mapping) |
|
|
205
219
|
| Lexique phoneme codes | New, B. & Pallier, C. — *Manuel de Lexique 3* v3.11, Tableau 2; [chrplr/openlexicon](https://github.com/chrplr/openlexicon) | CC BY-SA 4.0 |
|
|
206
220
|
| Kirshenbaum ↔ IPA | Kirshenbaum 1993 ASCII-IPA standard (comp.speech), cross-checked against espeak-ng | — (factual symbol mapping) |
|
|
221
|
+
| Cotovía ↔ IPA | Universidade de Vigo GTM Cotovía TTS project (`fonemas.cpp`) | — (factual symbol mapping) |
|
|
222
|
+
| RFE ↔ IPA | RFE phonetic alphabet (Revista de Filología Española, 1915) | — (factual symbol mapping) |
|
|
207
223
|
| Hangul jamo tables | [stannam/hangul_to_ipa](https://github.com/stannam/hangul_to_ipa) | — |
|
|
@@ -105,3 +105,15 @@ def test_14_kana_transliteration():
|
|
|
105
105
|
r = _run(EXAMPLES_DIR / "14_kana_transliteration.py")
|
|
106
106
|
assert r.returncode == 0, r.stderr
|
|
107
107
|
assert r.stdout.strip()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_15_cotovia():
|
|
111
|
+
r = _run(EXAMPLES_DIR / "15_cotovia.py")
|
|
112
|
+
assert r.returncode == 0, r.stderr
|
|
113
|
+
assert r.stdout.strip()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_16_rfe():
|
|
117
|
+
r = _run(EXAMPLES_DIR / "16_rfe.py")
|
|
118
|
+
assert r.returncode == 0, r.stderr
|
|
119
|
+
assert r.stdout.strip()
|
|
@@ -947,7 +947,7 @@ def test_notation_enum_values():
|
|
|
947
947
|
|
|
948
948
|
|
|
949
949
|
def test_notation_enum_count():
|
|
950
|
-
assert len(Notation) ==
|
|
950
|
+
assert len(Notation) == 9
|
|
951
951
|
|
|
952
952
|
|
|
953
953
|
def test_notation_str():
|
|
@@ -1027,3 +1027,124 @@ def test_lossless_to_ipa_flag_backed_by_round_trip():
|
|
|
1027
1027
|
if info.lossless_to_ipa and notation in samples:
|
|
1028
1028
|
for ch in samples[notation]:
|
|
1029
1029
|
assert from_ipa[notation](to_ipa[notation](ch)) == ch, notation
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
# ---------------------------------------------------------------------------
|
|
1033
|
+
# looks_like_ipa — heuristic notation detection
|
|
1034
|
+
# ---------------------------------------------------------------------------
|
|
1035
|
+
|
|
1036
|
+
from scriptconv.notation import looks_like_ipa # noqa: E402
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
@pytest.mark.parametrize("text", [
|
|
1040
|
+
"pʰɑtʃ", # IPA Extensions + modifier
|
|
1041
|
+
"ˈhɛloʊ", # stress mark + IPA Extensions
|
|
1042
|
+
"ɑ", # single IPA Extensions char
|
|
1043
|
+
"næ̃", # combining nasalisation
|
|
1044
|
+
"kat̪", # combining dental diacritic
|
|
1045
|
+
])
|
|
1046
|
+
def test_looks_like_ipa_true(text):
|
|
1047
|
+
assert looks_like_ipa(text) is True
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
@pytest.mark.parametrize("text", [
|
|
1051
|
+
"", # empty
|
|
1052
|
+
"hello", # plain Latin — ambiguous, no distinctive marker
|
|
1053
|
+
"att", # overlaps Latin
|
|
1054
|
+
"café", # accented Latin is not IPA-distinctive
|
|
1055
|
+
"θaβ", # bare Greek letters carry no IPA signal
|
|
1056
|
+
"مرحبا", # Arabic
|
|
1057
|
+
])
|
|
1058
|
+
def test_looks_like_ipa_false(text):
|
|
1059
|
+
assert looks_like_ipa(text) is False
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
# ---------------------------------------------------------------------------
|
|
1063
|
+
# Cotovía ↔ IPA
|
|
1064
|
+
# ---------------------------------------------------------------------------
|
|
1065
|
+
|
|
1066
|
+
from scriptconv.notation import cotovia_to_ipa, ipa_to_cotovia # noqa: E402
|
|
1067
|
+
|
|
1068
|
+
|
|
1069
|
+
@pytest.mark.parametrize("cv, ipa", [
|
|
1070
|
+
("tS", "tʃ"),
|
|
1071
|
+
("rr", "r"), # trill
|
|
1072
|
+
("r", "ɾ"), # tap
|
|
1073
|
+
("L", "ʎ"), ("Z", "ʎ"), ("jj", "ʎ"), # all three collapse to ʎ
|
|
1074
|
+
("B", "β"), ("D", "ð"), ("G", "ɣ"), ("J", "ɲ"), ("N", "ŋ"),
|
|
1075
|
+
("S", "ʃ"), ("T", "θ"), ("E", "ɛ"), ("O", "ɔ"), ("x", "x"), ("X", "x"),
|
|
1076
|
+
("karro", "karo"),
|
|
1077
|
+
])
|
|
1078
|
+
def test_cotovia_to_ipa(cv, ipa):
|
|
1079
|
+
assert cotovia_to_ipa(cv) == ipa
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
@pytest.mark.parametrize("ipa, cv", [
|
|
1083
|
+
("tʃ", "tS"),
|
|
1084
|
+
("r", "rr"), # trill
|
|
1085
|
+
("ɾ", "r"), # tap
|
|
1086
|
+
("ʎ", "L"), # canonical Cotovía palatal lateral
|
|
1087
|
+
("x", "x"),
|
|
1088
|
+
("β", "B"), ("ɲ", "J"), ("ɛ", "E"), ("ɔ", "O"),
|
|
1089
|
+
])
|
|
1090
|
+
def test_ipa_to_cotovia(ipa, cv):
|
|
1091
|
+
assert ipa_to_cotovia(ipa) == cv
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
def test_cotovia_tap_trill_round_trip():
|
|
1095
|
+
# The tap/trill distinction must survive a round-trip.
|
|
1096
|
+
assert ipa_to_cotovia(cotovia_to_ipa("karro")) == "karro" # trill
|
|
1097
|
+
assert ipa_to_cotovia(cotovia_to_ipa("kara")) == "kara" # tap
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
def test_convert_routes_through_cotovia():
|
|
1101
|
+
assert convert("tS", "cotovia", "x-sampa") == "tS"
|
|
1102
|
+
assert convert("CH IY1", "arpa", "cotovia") == "tSi"
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
def test_cotovia_pause_marker_not_a_phoneme():
|
|
1106
|
+
# "#" (silence marker) is excluded from the table and passes through
|
|
1107
|
+
# unchanged, rather than being emitted as a "pau" token.
|
|
1108
|
+
assert cotovia_to_ipa("#") == "#"
|
|
1109
|
+
# p/a/u are ordinary phonemes, converted independently.
|
|
1110
|
+
assert cotovia_to_ipa("pau") == "pau"
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
# ---------------------------------------------------------------------------
|
|
1114
|
+
# RFE (Revista de Filología Española) ↔ IPA
|
|
1115
|
+
# ---------------------------------------------------------------------------
|
|
1116
|
+
|
|
1117
|
+
from scriptconv.notation import rfe_to_ipa, ipa_to_rfe # noqa: E402
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
@pytest.mark.parametrize("rfe, ipa", [
|
|
1121
|
+
("š", "ʃ"), ("ž", "ʒ"), ("ĉ", "tʃ"), ("y", "ʝ"), ("ŷ", "ɟʝ"),
|
|
1122
|
+
("ñ", "ɲ"), ("n̮", "ɲ"), ("l̮", "ʎ"),
|
|
1123
|
+
("ƀ", "β"), ("đ", "ð"), ("ǥ", "ɣ"), ("θ", "θ"), ("ł", "ɫ"),
|
|
1124
|
+
("r", "ɾ"), ("r̄", "r"),
|
|
1125
|
+
("g", "ɡ"),
|
|
1126
|
+
("kaša", "kaʃa"),
|
|
1127
|
+
])
|
|
1128
|
+
def test_rfe_to_ipa(rfe, ipa):
|
|
1129
|
+
assert rfe_to_ipa(rfe) == ipa
|
|
1130
|
+
|
|
1131
|
+
|
|
1132
|
+
@pytest.mark.parametrize("ipa, rfe", [
|
|
1133
|
+
("ʃ", "š"), ("ʒ", "ž"), ("tʃ", "ĉ"), ("ʝ", "y"),
|
|
1134
|
+
("ɲ", "ñ"), # canonical, not the n̮ variant
|
|
1135
|
+
("ʎ", "l̮"),
|
|
1136
|
+
("β", "ƀ"), ("ð", "đ"), ("ɣ", "ǥ"),
|
|
1137
|
+
("ɾ", "r"), ("r", "r̄"),
|
|
1138
|
+
])
|
|
1139
|
+
def test_ipa_to_rfe(ipa, rfe):
|
|
1140
|
+
assert ipa_to_rfe(ipa) == rfe
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
def test_rfe_tap_trill_round_trip():
|
|
1144
|
+
assert ipa_to_rfe(rfe_to_ipa("far̄a")) == "far̄a" # trill
|
|
1145
|
+
assert ipa_to_rfe(rfe_to_ipa("kara")) == "kara" # tap
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
def test_convert_routes_through_rfe():
|
|
1149
|
+
assert convert("š", "rfe", "x-sampa") == "S"
|
|
1150
|
+
assert convert("ʃ", "ipa", "rfe") == "š"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|