scriptconv 0.0.3a12__tar.gz → 0.0.3a14__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.3a12 → scriptconv-0.0.3a14}/PKG-INFO +29 -1
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/pyproject.toml +8 -1
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/__main__.py +18 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/__init__.py +4 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/__init__.py +7 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/bw2ipa.py +207 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/hangul2ipa.py +784 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/ko_tables/aspiration.csv +20 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/ko_tables/assimilation.csv +31 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/ko_tables/double_coda.csv +17 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/ko_tables/hanja.tsv +8525 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/ko_tables/ipa.csv +22 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/ko_tables/neutralization.csv +11 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/ko_tables/tensification.csv +56 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/ko_tables/yale.csv +22 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/__init__.py +29 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/codeswitch.py +91 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/diacritize.py +89 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/dialectal.py +43 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/english_g2p.py +67 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/espeak.py +51 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/frontend.py +135 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/levantine_g2p.py +317 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/normalize.py +254 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/shami/phoneme_inventory.py +219 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/_thirdparty/zh_num.py +238 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/ar.py +96 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/en.py +187 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/ja.py +104 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/ko.py +65 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/registry.py +39 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/shami.py +81 -0
- scriptconv-0.0.3a14/scriptconv/phonemizers/zh.py +266 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/version.py +1 -1
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv.egg-info/PKG-INFO +29 -1
- scriptconv-0.0.3a14/scriptconv.egg-info/SOURCES.txt +78 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv.egg-info/requires.txt +34 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_phonemizers_base.py +29 -0
- scriptconv-0.0.3a14/tests/test_phonemizers_cjk_ar.py +69 -0
- scriptconv-0.0.3a12/scriptconv.egg-info/SOURCES.txt +0 -49
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/LICENSE +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/README.md +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/requirements.txt +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/__init__.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/cangjie.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/conventions.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/data/__init__.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/data/cangjie5_tc.tsv.gz +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/graph.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/notation.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/base.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/enums.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/eu.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/fa.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/gl.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/he.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/mul.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/mwl.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/o2ipa.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/pt.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/phonemizers/vi.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/py.typed +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/readings.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/scripts.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv/translit.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv.egg-info/dependency_links.txt +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/scriptconv.egg-info/top_level.txt +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/setup.cfg +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_arpa_stress.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_cangjie.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_cli.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_conventions.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_errors_policy.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_examples.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_graph.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_notation.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_readings.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_readings_zh.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_scripts.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/tests/test_scripts_stressonnx_compat.py +0 -0
- {scriptconv-0.0.3a12 → scriptconv-0.0.3a14}/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.3a14
|
|
4
4
|
Summary: Zero-dependency script & phoneme-notation core — ISO-15924 detection & metadata, IPA↔ARPABET/X-SAMPA/Lexique/Kirshenbaum/Cotovía/RFE, Buckwalter↔Arabic, Hangul→jamo, kana
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/TigreGotico/scriptconv
|
|
@@ -78,6 +78,33 @@ Requires-Dist: viphoneme; extra == "vi"
|
|
|
78
78
|
Provides-Extra: tashkeel
|
|
79
79
|
Requires-Dist: scriptconv[phonemizers]; extra == "tashkeel"
|
|
80
80
|
Requires-Dist: text2tashkeel; extra == "tashkeel"
|
|
81
|
+
Provides-Extra: ja-phonemizers
|
|
82
|
+
Requires-Dist: scriptconv[phonemizers]; extra == "ja-phonemizers"
|
|
83
|
+
Requires-Dist: pyopenjtalk; extra == "ja-phonemizers"
|
|
84
|
+
Requires-Dist: cutlet; extra == "ja-phonemizers"
|
|
85
|
+
Requires-Dist: pykakasi<3,>=2.3; extra == "ja-phonemizers"
|
|
86
|
+
Provides-Extra: ko
|
|
87
|
+
Requires-Dist: scriptconv[phonemizers]; extra == "ko"
|
|
88
|
+
Requires-Dist: g2pk; extra == "ko"
|
|
89
|
+
Requires-Dist: regex; extra == "ko"
|
|
90
|
+
Provides-Extra: zh-phonemizers
|
|
91
|
+
Requires-Dist: scriptconv[phonemizers]; extra == "zh-phonemizers"
|
|
92
|
+
Requires-Dist: jieba; extra == "zh-phonemizers"
|
|
93
|
+
Requires-Dist: pypinyin<1,>=0.50; extra == "zh-phonemizers"
|
|
94
|
+
Requires-Dist: xpinyin; extra == "zh-phonemizers"
|
|
95
|
+
Requires-Dist: g2pM; extra == "zh-phonemizers"
|
|
96
|
+
Requires-Dist: pinyin_to_ipa; extra == "zh-phonemizers"
|
|
97
|
+
Provides-Extra: en-phonemizers
|
|
98
|
+
Requires-Dist: scriptconv[phonemizers]; extra == "en-phonemizers"
|
|
99
|
+
Requires-Dist: g2p_en; extra == "en-phonemizers"
|
|
100
|
+
Requires-Dist: openphonemizer; extra == "en-phonemizers"
|
|
101
|
+
Requires-Dist: deep-phonemizer; extra == "en-phonemizers"
|
|
102
|
+
Provides-Extra: ar-phonemizers
|
|
103
|
+
Requires-Dist: scriptconv[phonemizers]; extra == "ar-phonemizers"
|
|
104
|
+
Requires-Dist: arbtok; extra == "ar-phonemizers"
|
|
105
|
+
Provides-Extra: shami
|
|
106
|
+
Requires-Dist: scriptconv[phonemizers]; extra == "shami"
|
|
107
|
+
Requires-Dist: regex; extra == "shami"
|
|
81
108
|
Provides-Extra: test
|
|
82
109
|
Requires-Dist: pytest; extra == "test"
|
|
83
110
|
Requires-Dist: pytest-timeout; extra == "test"
|
|
@@ -85,6 +112,7 @@ Requires-Dist: pykakasi<3,>=2.3; extra == "test"
|
|
|
85
112
|
Requires-Dist: pypinyin<1,>=0.50; extra == "test"
|
|
86
113
|
Requires-Dist: quebra-frases; extra == "test"
|
|
87
114
|
Requires-Dist: langcodes; extra == "test"
|
|
115
|
+
Requires-Dist: regex; extra == "test"
|
|
88
116
|
Dynamic: license-file
|
|
89
117
|
|
|
90
118
|
# scriptconv
|
|
@@ -53,7 +53,13 @@ he = ["scriptconv[phonemizers]", "phonikud-onnx"]
|
|
|
53
53
|
fa = ["scriptconv[phonemizers]", "persian_phonemizer"]
|
|
54
54
|
vi = ["scriptconv[phonemizers]", "viphoneme"]
|
|
55
55
|
tashkeel = ["scriptconv[phonemizers]", "text2tashkeel"]
|
|
56
|
-
|
|
56
|
+
ja-phonemizers = ["scriptconv[phonemizers]", "pyopenjtalk", "cutlet", "pykakasi>=2.3,<3"]
|
|
57
|
+
ko = ["scriptconv[phonemizers]", "g2pk", "regex"]
|
|
58
|
+
zh-phonemizers = ["scriptconv[phonemizers]", "jieba", "pypinyin>=0.50,<1", "xpinyin", "g2pM", "pinyin_to_ipa"]
|
|
59
|
+
en-phonemizers = ["scriptconv[phonemizers]", "g2p_en", "openphonemizer", "deep-phonemizer"]
|
|
60
|
+
ar-phonemizers = ["scriptconv[phonemizers]", "arbtok"]
|
|
61
|
+
shami = ["scriptconv[phonemizers]", "regex"]
|
|
62
|
+
test = ["pytest", "pytest-timeout", "pykakasi>=2.3,<3", "pypinyin>=0.50,<1", "quebra-frases", "langcodes", "regex"]
|
|
57
63
|
|
|
58
64
|
[project.urls]
|
|
59
65
|
Homepage = "https://github.com/TigreGotico/scriptconv"
|
|
@@ -65,6 +71,7 @@ include = ["scriptconv*"]
|
|
|
65
71
|
[tool.setuptools.package-data]
|
|
66
72
|
scriptconv = ["py.typed"]
|
|
67
73
|
"scriptconv.data" = ["*.tsv.gz"]
|
|
74
|
+
"scriptconv.phonemizers._thirdparty" = ["ko_tables/*.csv", "ko_tables/*.tsv"]
|
|
68
75
|
|
|
69
76
|
[tool.pytest.ini_options]
|
|
70
77
|
testpaths = ["tests"]
|
|
@@ -12,6 +12,7 @@ Usage
|
|
|
12
12
|
python -m scriptconv restyle <convention> <style> <text>
|
|
13
13
|
python -m scriptconv conventions [--script CODE]
|
|
14
14
|
python -m scriptconv route <src> <dst>
|
|
15
|
+
python -m scriptconv phonemize <lang> <text> [--phonemizer ID] [--alphabet A]
|
|
15
16
|
|
|
16
17
|
Examples
|
|
17
18
|
--------
|
|
@@ -69,6 +70,12 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
69
70
|
rt.add_argument("src", help="Source representation id")
|
|
70
71
|
rt.add_argument("dst", help="Target representation id")
|
|
71
72
|
|
|
73
|
+
ph = sub.add_parser("phonemize", help="Phonemize text (per-language default backend)")
|
|
74
|
+
ph.add_argument("lang", help="Language code")
|
|
75
|
+
ph.add_argument("text", help="Text to phonemize")
|
|
76
|
+
ph.add_argument("--phonemizer", default=None, help="Backend override (Phonemizer value)")
|
|
77
|
+
ph.add_argument("--alphabet", default="ipa", help="Output alphabet (default ipa)")
|
|
78
|
+
|
|
72
79
|
args = p.parse_args(argv)
|
|
73
80
|
|
|
74
81
|
if args.command is None:
|
|
@@ -86,6 +93,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
86
93
|
"restyle": lambda: _do_restyle(args),
|
|
87
94
|
"conventions": lambda: _do_conventions(args),
|
|
88
95
|
"route": lambda: _do_route(args),
|
|
96
|
+
"phonemize": lambda: _do_phonemize(args),
|
|
89
97
|
}
|
|
90
98
|
dispatch[args.command]()
|
|
91
99
|
return 0
|
|
@@ -108,6 +116,16 @@ def _do_convert(args: argparse.Namespace) -> None:
|
|
|
108
116
|
f"graph nodes: {', '.join(DEFAULT_GRAPH.nodes)})")
|
|
109
117
|
|
|
110
118
|
|
|
119
|
+
def _do_phonemize(args: argparse.Namespace) -> None:
|
|
120
|
+
from scriptconv.phonemizers import Alphabet, Phonemizer, phonemize
|
|
121
|
+
try:
|
|
122
|
+
override = Phonemizer(args.phonemizer) if args.phonemizer else None
|
|
123
|
+
print(phonemize(args.text, args.lang, Alphabet(args.alphabet),
|
|
124
|
+
override=override))
|
|
125
|
+
except (ValueError, ImportError) as e:
|
|
126
|
+
raise SystemExit(f"error: {e}")
|
|
127
|
+
|
|
128
|
+
|
|
111
129
|
def _do_route(args: argparse.Namespace) -> None:
|
|
112
130
|
from scriptconv.graph import DEFAULT_GRAPH
|
|
113
131
|
try:
|
|
@@ -21,6 +21,8 @@ from scriptconv.phonemizers.registry import (
|
|
|
21
21
|
get_phonemizer,
|
|
22
22
|
get_phonemizer_class,
|
|
23
23
|
phonemizer_for_lang,
|
|
24
|
+
phonemize,
|
|
25
|
+
register,
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
__all__ = [
|
|
@@ -37,4 +39,6 @@ __all__ = [
|
|
|
37
39
|
"get_phonemizer",
|
|
38
40
|
"get_phonemizer_class",
|
|
39
41
|
"phonemizer_for_lang",
|
|
42
|
+
"phonemize",
|
|
43
|
+
"register",
|
|
40
44
|
]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Vendored first-party-maintained G2P implementations.
|
|
2
|
+
|
|
3
|
+
Each module carries its upstream provenance and license in its header; only
|
|
4
|
+
permissively-licensed code lives here (MIT/Apache). GPL or NonCommercial
|
|
5
|
+
upstreams (KoG2P, mantoq's pyarabic/phonetise) are NOT vendored — their
|
|
6
|
+
wrapper classes raise with an explanation instead.
|
|
7
|
+
"""
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# translate.py
|
|
2
|
+
# Deterministic Mantoq → IPA converter
|
|
3
|
+
# Assumes the input is already tokenized using the Mantoq inventory.
|
|
4
|
+
|
|
5
|
+
# ---------------------------------------------------------------------------
|
|
6
|
+
# Token → IPA maps
|
|
7
|
+
# ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
CONSONANTS = {
|
|
10
|
+
"b": "b",
|
|
11
|
+
"t": "t",
|
|
12
|
+
"^": "θ",
|
|
13
|
+
"j": "d͡ʒ",
|
|
14
|
+
"H": "ħ",
|
|
15
|
+
"x": "x",
|
|
16
|
+
"d": "d",
|
|
17
|
+
"*": "ð",
|
|
18
|
+
"r": "r",
|
|
19
|
+
"z": "z",
|
|
20
|
+
"s": "s",
|
|
21
|
+
"$": "ʃ",
|
|
22
|
+
"S": "sˤ",
|
|
23
|
+
"D": "dˤ",
|
|
24
|
+
"T": "tˤ",
|
|
25
|
+
"Z": "ðˤ",
|
|
26
|
+
"E": "ʕ",
|
|
27
|
+
"g": "ɣ",
|
|
28
|
+
"f": "f",
|
|
29
|
+
"q": "q",
|
|
30
|
+
"k": "k",
|
|
31
|
+
"l": "l",
|
|
32
|
+
"m": "m",
|
|
33
|
+
"n": "n",
|
|
34
|
+
"h": "h",
|
|
35
|
+
"w": "w",
|
|
36
|
+
"y": "j",
|
|
37
|
+
"v": "v"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
VOWELS = {
|
|
41
|
+
"a": "a",
|
|
42
|
+
"aa": "aː",
|
|
43
|
+
"aaaa": "aːː",
|
|
44
|
+
"i": "i",
|
|
45
|
+
"ii": "iː",
|
|
46
|
+
"u": "u",
|
|
47
|
+
"uu": "uː",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Punctuation is passed through unchanged:
|
|
52
|
+
PUNCTUATION = set(list(".,;:!?()[]{}\"'"))
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Tokenizer
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
def tokenize_mantoq(text):
|
|
59
|
+
"""
|
|
60
|
+
Tokenize Mantoq string deterministically.
|
|
61
|
+
|
|
62
|
+
Priority:
|
|
63
|
+
1. _dbl_
|
|
64
|
+
2. aaaa
|
|
65
|
+
3. ii / uu / aa
|
|
66
|
+
4. single char tokens
|
|
67
|
+
5. punctuation and raw chars are passed through
|
|
68
|
+
"""
|
|
69
|
+
tokens = []
|
|
70
|
+
i = 0
|
|
71
|
+
L = len(text)
|
|
72
|
+
|
|
73
|
+
while i < L:
|
|
74
|
+
|
|
75
|
+
# doubling marker
|
|
76
|
+
if text.startswith("_dbl_", i):
|
|
77
|
+
tokens.append("_dbl_")
|
|
78
|
+
i += 5
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
# word separator
|
|
82
|
+
if text.startswith("_+_", i):
|
|
83
|
+
tokens.append("_+_")
|
|
84
|
+
i += 3
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
# longest vowel first
|
|
88
|
+
if text.startswith("aaaa", i):
|
|
89
|
+
tokens.append("aaaa")
|
|
90
|
+
i += 4
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
if text.startswith("aa", i):
|
|
94
|
+
tokens.append("aa")
|
|
95
|
+
i += 2
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
if text.startswith("ii", i):
|
|
99
|
+
tokens.append("ii")
|
|
100
|
+
i += 2
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
if text.startswith("uu", i):
|
|
104
|
+
tokens.append("uu")
|
|
105
|
+
i += 2
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
# single-character consonant or vowel
|
|
109
|
+
ch = text[i]
|
|
110
|
+
|
|
111
|
+
if ch in CONSONANTS or ch in VOWELS:
|
|
112
|
+
tokens.append(ch)
|
|
113
|
+
i += 1
|
|
114
|
+
continue
|
|
115
|
+
if ch == "<":
|
|
116
|
+
tokens.append("ʔ")
|
|
117
|
+
i += 1
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
# punctuation
|
|
121
|
+
if ch in PUNCTUATION:
|
|
122
|
+
tokens.append(ch)
|
|
123
|
+
i += 1
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
# fallback: pass through unknown characters
|
|
127
|
+
tokens.append(ch)
|
|
128
|
+
i += 1
|
|
129
|
+
|
|
130
|
+
return tokens
|
|
131
|
+
|
|
132
|
+
# ---------------------------------------------------------------------------
|
|
133
|
+
# IPA Assembly
|
|
134
|
+
# ---------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
def apply_doubling(prev_token, prev_ipa):
|
|
137
|
+
"""
|
|
138
|
+
Mantoq doubling rule:
|
|
139
|
+
- If previous token is a vowel token: lengthen it.
|
|
140
|
+
- If previous token is a consonant: mark gemination using ː.
|
|
141
|
+
"""
|
|
142
|
+
if prev_token in VOWELS:
|
|
143
|
+
# ensure single long marker; long tokens already contain ː
|
|
144
|
+
if prev_ipa.endswith("ː"):
|
|
145
|
+
return prev_ipa + "ː"
|
|
146
|
+
return prev_ipa + "ː"
|
|
147
|
+
|
|
148
|
+
if prev_token in CONSONANTS:
|
|
149
|
+
# consonant gemination: use length mark, not duplication
|
|
150
|
+
if prev_ipa.endswith("ː"):
|
|
151
|
+
return prev_ipa # already geminated
|
|
152
|
+
return prev_ipa + "ː"
|
|
153
|
+
|
|
154
|
+
return prev_ipa
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def mantoq_to_ipa(text):
|
|
158
|
+
tokens = tokenize_mantoq(text)
|
|
159
|
+
|
|
160
|
+
ipa_out = []
|
|
161
|
+
last_token = None
|
|
162
|
+
last_ipa = None
|
|
163
|
+
|
|
164
|
+
for tok in tokens:
|
|
165
|
+
|
|
166
|
+
# doubling applies to the previous symbol
|
|
167
|
+
if tok == "_dbl_":
|
|
168
|
+
if last_token is None:
|
|
169
|
+
continue
|
|
170
|
+
new_ipa = apply_doubling(last_token, last_ipa)
|
|
171
|
+
ipa_out[-1] = new_ipa
|
|
172
|
+
last_ipa = new_ipa
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
# explicit word separation
|
|
176
|
+
if tok == "_+_":
|
|
177
|
+
ipa_out.append(" ")
|
|
178
|
+
last_token = tok
|
|
179
|
+
last_ipa = " "
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
# vowels
|
|
183
|
+
if tok in VOWELS:
|
|
184
|
+
ipa_val = VOWELS[tok]
|
|
185
|
+
ipa_out.append(ipa_val)
|
|
186
|
+
last_token = tok
|
|
187
|
+
last_ipa = ipa_val
|
|
188
|
+
continue
|
|
189
|
+
|
|
190
|
+
# consonants
|
|
191
|
+
if tok in CONSONANTS:
|
|
192
|
+
ipa_val = CONSONANTS[tok]
|
|
193
|
+
ipa_out.append(ipa_val)
|
|
194
|
+
last_token = tok
|
|
195
|
+
last_ipa = ipa_val
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
# punctuation and fallthrough
|
|
199
|
+
ipa_out.append(tok)
|
|
200
|
+
last_token = tok
|
|
201
|
+
last_ipa = tok
|
|
202
|
+
|
|
203
|
+
return "".join(ipa_out)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# backwards compat alias
|
|
207
|
+
bw2ipa = mantoq_to_ipa
|