scriptconv 0.0.4a3__tar.gz → 0.0.4a4__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.4a3/scriptconv.egg-info → scriptconv-0.0.4a4}/PKG-INFO +12 -1
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/README.md +11 -0
- scriptconv-0.0.4a4/scriptconv/diacritics.py +153 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/base.py +2 -2
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/registry.py +4 -1
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/version.py +1 -1
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4/scriptconv.egg-info}/PKG-INFO +12 -1
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv.egg-info/SOURCES.txt +2 -0
- scriptconv-0.0.4a4/tests/test_diacritics_graph.py +104 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/LICENSE +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/pyproject.toml +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/requirements.txt +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/__init__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/__main__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/cangjie.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/conventions.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/data/__init__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/data/cangjie5_tc.tsv.gz +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/graph.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/notation.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/__init__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/__init__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/bw2ipa.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/hangul2ipa.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/aspiration.csv +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/assimilation.csv +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/double_coda.csv +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/hanja.tsv +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/ipa.csv +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/neutralization.csv +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/tensification.csv +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/yale.csv +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/__init__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/codeswitch.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/diacritize.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/dialectal.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/english_g2p.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/espeak.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/frontend.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/levantine_g2p.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/normalize.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/phoneme_inventory.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/zh_num.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/__init__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/kog2p/LICENSE.md +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/kog2p/__init__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/kog2p/rulebook.txt +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/LICENSE.md +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/__init__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/buck/__init__.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/buck/phonetise_buckwalter.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/buck/symbols.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/buck/tokenization.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/num2words.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/unicode_symbol2label.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/ar.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/en.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/enums.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/eu.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/fa.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/gl.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/he.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/ja.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/ko.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/mul.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/mwl.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/o2ipa.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/pt.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/shami.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/vi.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/zh.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/py.typed +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/readings.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/scripts.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/translit.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv.egg-info/dependency_links.txt +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv.egg-info/requires.txt +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv.egg-info/top_level.txt +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/setup.cfg +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_arpa_stress.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_cangjie.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_cli.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_conventions.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_errors_policy.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_examples.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_graph.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_notation.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_phonemizers_base.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_phonemizers_cjk_ar.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_readings.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_readings_zh.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_scripts.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/tests/test_scripts_stressonnx_compat.py +0 -0
- {scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/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.4a4
|
|
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
|
|
@@ -391,6 +391,17 @@ p = GraphemePhonemizer()
|
|
|
391
391
|
p.add_diacritics("Tenho muita sede hoje.", "pt") # 'Tenho muita sêde hoje.' (thirst)
|
|
392
392
|
```
|
|
393
393
|
|
|
394
|
+
Diacritization also joins the graph, like phonemization, via
|
|
395
|
+
`scriptconv.diacritics.register` — a `text -> text-diacritized` edge, opt-in,
|
|
396
|
+
with `text -> ipa` routing unchanged:
|
|
397
|
+
|
|
398
|
+
```python
|
|
399
|
+
from scriptconv import diacritics
|
|
400
|
+
g = DEFAULT_GRAPH.extend(diacritics.register).extend(phonemizers.register)
|
|
401
|
+
g.convert("Tenho muita sede hoje.", "text", "text-diacritized", lang="pt")
|
|
402
|
+
# 'Tenho muita sêde hoje.'
|
|
403
|
+
```
|
|
404
|
+
|
|
394
405
|
Stress is unwritten or under-marked in all 26 covered languages; East Slavic
|
|
395
406
|
is the clearest case, where unstressed vowels also reduce (e.g. Russian
|
|
396
407
|
о→[ɐ]/[ə]), so a missing mark corrupts more than prosody there. stressonnx is
|
|
@@ -265,6 +265,17 @@ p = GraphemePhonemizer()
|
|
|
265
265
|
p.add_diacritics("Tenho muita sede hoje.", "pt") # 'Tenho muita sêde hoje.' (thirst)
|
|
266
266
|
```
|
|
267
267
|
|
|
268
|
+
Diacritization also joins the graph, like phonemization, via
|
|
269
|
+
`scriptconv.diacritics.register` — a `text -> text-diacritized` edge, opt-in,
|
|
270
|
+
with `text -> ipa` routing unchanged:
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
from scriptconv import diacritics
|
|
274
|
+
g = DEFAULT_GRAPH.extend(diacritics.register).extend(phonemizers.register)
|
|
275
|
+
g.convert("Tenho muita sede hoje.", "text", "text-diacritized", lang="pt")
|
|
276
|
+
# 'Tenho muita sêde hoje.'
|
|
277
|
+
```
|
|
278
|
+
|
|
268
279
|
Stress is unwritten or under-marked in all 26 covered languages; East Slavic
|
|
269
280
|
is the clearest case, where unstressed vowels also reduce (e.g. Russian
|
|
270
281
|
о→[ɐ]/[ə]), so a missing mark corrupts more than prosody there. stressonnx is
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Diacritization as a graph extension — parallel to :mod:`scriptconv.phonemizers`.
|
|
2
|
+
|
|
3
|
+
Diacritization (Arabic tashkeel, Hebrew niqqud, East-Slavic/Turkic/Caucasian
|
|
4
|
+
word stress, European-Portuguese homograph sense marks) is, architecturally,
|
|
5
|
+
just another transform between text representations: it maps the ``"text"``
|
|
6
|
+
node to a lang-contextual ``"text-diacritized"`` node. Like
|
|
7
|
+
:func:`scriptconv.phonemizers.register` it is opt-in — ``DEFAULT_GRAPH`` stays
|
|
8
|
+
free of it until a caller extends a graph::
|
|
9
|
+
|
|
10
|
+
from scriptconv.graph import DEFAULT_GRAPH
|
|
11
|
+
from scriptconv import diacritics, phonemizers
|
|
12
|
+
g = DEFAULT_GRAPH.extend(diacritics.register).extend(phonemizers.register)
|
|
13
|
+
|
|
14
|
+
g.convert("замок стоит на горе", "text", "text-diacritized", lang="ru")
|
|
15
|
+
# 'за́мок сто́ит на горе́' — just the diacritized text
|
|
16
|
+
|
|
17
|
+
g.convert("Tenho muita sede hoje.", "text", "text-diacritized", lang="pt")
|
|
18
|
+
# 'Tenho muita sêde hoje.'
|
|
19
|
+
|
|
20
|
+
Routing ``"text" -> "ipa"`` still takes the direct phonemization edge by
|
|
21
|
+
default: the diacritization edge is model-based (``lossless=False``), so it
|
|
22
|
+
never out-prices a direct phonemization, and enabling this extension does not
|
|
23
|
+
silently change phonemization output. The diacritized route is taken only
|
|
24
|
+
when a caller asks for ``"text-diacritized"`` explicitly.
|
|
25
|
+
|
|
26
|
+
A future per-engine *stance* would push this further into topology rather than
|
|
27
|
+
a flag: an engine that *requires* vocalized input would carry only a
|
|
28
|
+
``"text-diacritized" -> "ipa"`` edge (forcing the detour), one that *forbids*
|
|
29
|
+
diacritics would carry only ``"text" -> "ipa"`` (making the detour
|
|
30
|
+
unroutable), and a tolerant engine (e.g. arbtok, which self-vocalizes) carries
|
|
31
|
+
both. The strip direction (``"text-diacritized" -> "text"``) is registered too, but
|
|
32
|
+
gated to languages whose marks are removable *overlay* diacritics —
|
|
33
|
+
Arabic/Hebrew vocalization and East-Slavic/Turkic/Caucasian stress. Strip
|
|
34
|
+
removes only the specific overlay codepoints each backend adds (combining
|
|
35
|
+
acute/grave for stress, tashkeel for Arabic, niqqud for Hebrew) — never a
|
|
36
|
+
blanket combining-mark filter — so precomposed native letters survive
|
|
37
|
+
(Cyrillic й/ё, Latvian macrons, Azerbaijani ç/ö, Arabic hamza carriers أ إ آ ؤ
|
|
38
|
+
ئ). The gate uses exact primary-subtag matching, so Aragonese (``arg``),
|
|
39
|
+
Herero (``her``), Mapudungun (``arn``) etc. are never misread as Arabic/Hebrew.
|
|
40
|
+
Languages whose diacritics are native orthography (European Portuguese, via
|
|
41
|
+
bifonia) refuse the strip with :class:`ValueError` instead of silently
|
|
42
|
+
corrupting the spelling (``café`` must never become ``cafe``). This split
|
|
43
|
+
tracks which backend/model produced the marks: overlay backends (phonikud,
|
|
44
|
+
tashkeel, stressonnx) are strippable; spelling-integral backends (bifonia) are
|
|
45
|
+
not.
|
|
46
|
+
"""
|
|
47
|
+
from scriptconv.graph import Edge
|
|
48
|
+
from scriptconv.phonemizers.base import STRESS_LANGS, _primary_subtag
|
|
49
|
+
|
|
50
|
+
#: The lang-contextual node produced by diacritization. Like ``"text"`` it is
|
|
51
|
+
#: meaningful only with ``lang=`` context and exists only in opted-in graphs.
|
|
52
|
+
DIACRITIZED = "text-diacritized"
|
|
53
|
+
|
|
54
|
+
_DEFAULT_PHONEMIZER = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _get_phonemizer(phonikud_model=None):
|
|
58
|
+
global _DEFAULT_PHONEMIZER
|
|
59
|
+
if phonikud_model:
|
|
60
|
+
from scriptconv.phonemizers.base import GraphemePhonemizer
|
|
61
|
+
return GraphemePhonemizer(phonikud_model=phonikud_model)
|
|
62
|
+
if _DEFAULT_PHONEMIZER is None:
|
|
63
|
+
from scriptconv.phonemizers.base import GraphemePhonemizer
|
|
64
|
+
_DEFAULT_PHONEMIZER = GraphemePhonemizer()
|
|
65
|
+
return _DEFAULT_PHONEMIZER
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def diacritize(text: str, lang: str = "und", model=None, **context) -> str:
|
|
69
|
+
"""Add pronunciation-disambiguating diacritics to *text* for *lang*.
|
|
70
|
+
|
|
71
|
+
Thin wrapper over :meth:`BasePhonemizer.add_diacritics` — the single
|
|
72
|
+
dispatch (he→phonikud, ar→tashkeel, ru/uk/be/…→stressonnx, pt→bifonia).
|
|
73
|
+
Reuses a cached default phonemizer so repeated calls don't reload models; a
|
|
74
|
+
per-call Hebrew ``phonikud_model`` path (via context) builds a fresh one.
|
|
75
|
+
"""
|
|
76
|
+
return _get_phonemizer(context.get("phonikud_model")).add_diacritics(text, lang, model)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# Combining marks each diacritization backend overlays onto the bare text.
|
|
80
|
+
# Strip removes ONLY these codepoints — never a blanket combining-mark filter,
|
|
81
|
+
# which (via NFD) would decompose and destroy precomposed native letters:
|
|
82
|
+
# Cyrillic й/ё, Latvian ī, Azerbaijani ç/ö, and Arabic hamza carriers أ إ آ ؤ ئ.
|
|
83
|
+
_STRESS_MARKS = frozenset({0x0300, 0x0301}) # combining grave / acute
|
|
84
|
+
_ARABIC_MARKS = frozenset(range(0x064B, 0x0660)) | frozenset({0x0670}) # tashkeel + dagger alef
|
|
85
|
+
_HEBREW_MARKS = (frozenset(range(0x05B0, 0x05BE))
|
|
86
|
+
| frozenset({0x05BF, 0x05C1, 0x05C2, 0x05C4, 0x05C5, 0x05C7})) # niqqud
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _overlay_marks(lang: str) -> "frozenset | None":
|
|
90
|
+
"""The overlay codepoints for *lang*'s diacritization backend, or None.
|
|
91
|
+
|
|
92
|
+
Uses exact primary-subtag matching (never ``startswith``) so Aragonese
|
|
93
|
+
(``arg``), Herero (``her``), Mapudungun (``arn``) etc. are NOT misread as
|
|
94
|
+
Arabic/Hebrew and stripped.
|
|
95
|
+
"""
|
|
96
|
+
p = _primary_subtag(lang)
|
|
97
|
+
if p in STRESS_LANGS:
|
|
98
|
+
return _STRESS_MARKS
|
|
99
|
+
if p == "ar":
|
|
100
|
+
return _ARABIC_MARKS
|
|
101
|
+
if p == "he":
|
|
102
|
+
return _HEBREW_MARKS
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _supports_strip(lang: str) -> bool:
|
|
107
|
+
"""True for languages whose diacritics are removable overlays (stress marks,
|
|
108
|
+
Arabic tashkeel, Hebrew niqqud) rather than native orthography."""
|
|
109
|
+
return _overlay_marks(lang) is not None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def strip_diacritics(text: str, lang: str = "und", **_) -> str:
|
|
113
|
+
"""Remove the overlay diacritics *lang*'s backend adds, recovering the bare
|
|
114
|
+
text WITHOUT touching native letters.
|
|
115
|
+
|
|
116
|
+
Removes only the specific overlay codepoints (combining acute/grave for
|
|
117
|
+
stress, tashkeel for Arabic, niqqud for Hebrew) — precomposed native
|
|
118
|
+
letters (Cyrillic й/ё, Latvian ī, Azerbaijani ç, Arabic hamza carriers) are
|
|
119
|
+
left intact. Raises :class:`ValueError` for languages whose diacritics are
|
|
120
|
+
part of the native orthography (e.g. European Portuguese), where any removal
|
|
121
|
+
would corrupt the spelling.
|
|
122
|
+
"""
|
|
123
|
+
marks = _overlay_marks(lang)
|
|
124
|
+
if marks is None:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"cannot strip diacritics for lang={lang!r}: its diacritics are "
|
|
127
|
+
"part of the native orthography, so removal would corrupt the "
|
|
128
|
+
"spelling. strip is defined only for overlay diacritics "
|
|
129
|
+
"(Arabic/Hebrew vocalization, East-Slavic/Turkic stress).")
|
|
130
|
+
return "".join(c for c in text if ord(c) not in marks)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def register(graph) -> None:
|
|
134
|
+
"""Opt-in graph integration: add the diacritize/strip edge pair.
|
|
135
|
+
|
|
136
|
+
``text -> text-diacritized`` is model-based (``lossless=False``), so a
|
|
137
|
+
direct ``text -> ipa`` phonemization always out-prices the detour —
|
|
138
|
+
enabling this extension is safe and non-invasive. Pair with
|
|
139
|
+
:func:`scriptconv.phonemizers.register` to make
|
|
140
|
+
``"text-diacritized" -> "ipa"`` reachable.
|
|
141
|
+
|
|
142
|
+
``text-diacritized -> text`` (:func:`strip_diacritics`) is lossless and
|
|
143
|
+
cheap, but gated: it raises for languages whose diacritics are native
|
|
144
|
+
orthography rather than removable overlays.
|
|
145
|
+
"""
|
|
146
|
+
graph.register(
|
|
147
|
+
Edge("text", DIACRITIZED,
|
|
148
|
+
lambda text, lang="und", model=None, **c: diacritize(text, lang, model, **c),
|
|
149
|
+
lossless=False))
|
|
150
|
+
graph.register(
|
|
151
|
+
Edge(DIACRITIZED, "text",
|
|
152
|
+
lambda text, lang="und", **_: strip_diacritics(text, lang),
|
|
153
|
+
lossless=True))
|
|
@@ -199,9 +199,9 @@ class BasePhonemizer(metaclass=abc.ABCMeta):
|
|
|
199
199
|
``ImportError`` naming its extra when the optional dependency is
|
|
200
200
|
missing — scriptconv never installs anything on the caller's behalf.
|
|
201
201
|
"""
|
|
202
|
-
if lang
|
|
202
|
+
if _primary_subtag(lang) == "he":
|
|
203
203
|
return self.phonikud.add_diacritics(text)
|
|
204
|
-
elif lang
|
|
204
|
+
elif _primary_subtag(lang) == "ar":
|
|
205
205
|
return self.tashkeel(model).diacritize(text)
|
|
206
206
|
elif _primary_subtag(lang) in STRESS_LANGS:
|
|
207
207
|
return self._stress(text, lang, model)
|
|
@@ -219,7 +219,9 @@ def register(graph) -> None:
|
|
|
219
219
|
routing context, and present only in graphs that opted in (the
|
|
220
220
|
:data:`scriptconv.graph.DEFAULT_GRAPH` stays orthography-only by
|
|
221
221
|
design) — and one dispatching ``text -> ipa`` edge that resolves the
|
|
222
|
-
per-language default (honouring an ``override=`` context key).
|
|
222
|
+
per-language default (honouring an ``override=`` context key). It also
|
|
223
|
+
accepts already-diacritized input via a ``"text-diacritized" -> "ipa"``
|
|
224
|
+
edge (the same phonemization; pair with :func:`scriptconv.diacritics.register`).
|
|
223
225
|
|
|
224
226
|
Usage::
|
|
225
227
|
|
|
@@ -236,3 +238,4 @@ def register(graph) -> None:
|
|
|
236
238
|
override).phonemize_string(text, lang)
|
|
237
239
|
|
|
238
240
|
graph.register(Edge("text", "ipa", _text_to_ipa, lossless=False))
|
|
241
|
+
graph.register(Edge("text-diacritized", "ipa", _text_to_ipa, lossless=False))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scriptconv
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4a4
|
|
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
|
|
@@ -391,6 +391,17 @@ p = GraphemePhonemizer()
|
|
|
391
391
|
p.add_diacritics("Tenho muita sede hoje.", "pt") # 'Tenho muita sêde hoje.' (thirst)
|
|
392
392
|
```
|
|
393
393
|
|
|
394
|
+
Diacritization also joins the graph, like phonemization, via
|
|
395
|
+
`scriptconv.diacritics.register` — a `text -> text-diacritized` edge, opt-in,
|
|
396
|
+
with `text -> ipa` routing unchanged:
|
|
397
|
+
|
|
398
|
+
```python
|
|
399
|
+
from scriptconv import diacritics
|
|
400
|
+
g = DEFAULT_GRAPH.extend(diacritics.register).extend(phonemizers.register)
|
|
401
|
+
g.convert("Tenho muita sede hoje.", "text", "text-diacritized", lang="pt")
|
|
402
|
+
# 'Tenho muita sêde hoje.'
|
|
403
|
+
```
|
|
404
|
+
|
|
394
405
|
Stress is unwritten or under-marked in all 26 covered languages; East Slavic
|
|
395
406
|
is the clearest case, where unstressed vowels also reduce (e.g. Russian
|
|
396
407
|
о→[ɐ]/[ə]), so a missing mark corrupts more than prosody there. stressonnx is
|
|
@@ -6,6 +6,7 @@ scriptconv/__init__.py
|
|
|
6
6
|
scriptconv/__main__.py
|
|
7
7
|
scriptconv/cangjie.py
|
|
8
8
|
scriptconv/conventions.py
|
|
9
|
+
scriptconv/diacritics.py
|
|
9
10
|
scriptconv/graph.py
|
|
10
11
|
scriptconv/notation.py
|
|
11
12
|
scriptconv/py.typed
|
|
@@ -77,6 +78,7 @@ tests/test_arpa_stress.py
|
|
|
77
78
|
tests/test_cangjie.py
|
|
78
79
|
tests/test_cli.py
|
|
79
80
|
tests/test_conventions.py
|
|
81
|
+
tests/test_diacritics_graph.py
|
|
80
82
|
tests/test_errors_policy.py
|
|
81
83
|
tests/test_examples.py
|
|
82
84
|
tests/test_graph.py
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import types
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest import mock
|
|
5
|
+
|
|
6
|
+
from scriptconv.graph import DEFAULT_GRAPH
|
|
7
|
+
from scriptconv import diacritics, phonemizers
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestDiacriticsGraphExtension(unittest.TestCase):
|
|
11
|
+
def setUp(self):
|
|
12
|
+
self.graph = DEFAULT_GRAPH.extend(diacritics.register).extend(phonemizers.register)
|
|
13
|
+
|
|
14
|
+
def test_default_graph_has_no_diacritized_node(self):
|
|
15
|
+
self.assertFalse(DEFAULT_GRAPH.can_convert("text", "text-diacritized"))
|
|
16
|
+
|
|
17
|
+
def test_extend_adds_diacritized_node(self):
|
|
18
|
+
self.assertTrue(self.graph.can_convert("text", "text-diacritized"))
|
|
19
|
+
self.assertTrue(self.graph.can_convert("text-diacritized", "ipa"))
|
|
20
|
+
|
|
21
|
+
def test_text_to_ipa_prefers_direct_route(self):
|
|
22
|
+
route = self.graph.route("text", "ipa")
|
|
23
|
+
self.assertEqual(len(route), 1)
|
|
24
|
+
edge = route[0]
|
|
25
|
+
self.assertEqual(edge.src, "text")
|
|
26
|
+
self.assertEqual(edge.dst, "ipa")
|
|
27
|
+
|
|
28
|
+
def test_portuguese_diacritization_via_graph(self):
|
|
29
|
+
out = self.graph.convert("Tenho muita sede hoje.", "text",
|
|
30
|
+
"text-diacritized", lang="pt")
|
|
31
|
+
self.assertEqual(out, "Tenho muita sêde hoje.")
|
|
32
|
+
out2 = self.graph.convert("A sede da empresa fica em Lisboa.", "text",
|
|
33
|
+
"text-diacritized", lang="pt")
|
|
34
|
+
self.assertEqual(out2, "A séde da empresa fica em Lisboa.")
|
|
35
|
+
|
|
36
|
+
def test_slavic_diacritization_routes_to_stressonnx_stub(self):
|
|
37
|
+
calls = []
|
|
38
|
+
mod = types.ModuleType("stressonnx")
|
|
39
|
+
|
|
40
|
+
def stress(text, lang, model=None):
|
|
41
|
+
calls.append((text, lang, model))
|
|
42
|
+
return "STRESSED"
|
|
43
|
+
|
|
44
|
+
mod.stress = stress
|
|
45
|
+
with mock.patch.dict(sys.modules, {"stressonnx": mod}):
|
|
46
|
+
out = self.graph.convert("замок стоит", "text",
|
|
47
|
+
"text-diacritized", lang="ru")
|
|
48
|
+
self.assertEqual(out, "STRESSED")
|
|
49
|
+
self.assertEqual(calls, [("замок стоит", "ru", None)])
|
|
50
|
+
|
|
51
|
+
def test_strip_recovers_bare_russian(self):
|
|
52
|
+
out = self.graph.convert("за́мок", "text-diacritized", "text", lang="ru")
|
|
53
|
+
self.assertEqual(out, "замок")
|
|
54
|
+
|
|
55
|
+
def test_strip_recovers_bare_arabic(self):
|
|
56
|
+
vocalized = "مُحَمَّد"
|
|
57
|
+
bare = "".join(c for c in vocalized if not (0x064B <= ord(c) <= 0x065F
|
|
58
|
+
or ord(c) == 0x0670))
|
|
59
|
+
out = self.graph.convert(vocalized, "text-diacritized", "text", lang="ar")
|
|
60
|
+
self.assertEqual(out, bare)
|
|
61
|
+
self.assertEqual(out, "محمد")
|
|
62
|
+
|
|
63
|
+
def test_strip_refused_for_portuguese(self):
|
|
64
|
+
with self.assertRaises(ValueError) as ctx:
|
|
65
|
+
self.graph.convert("sêde", "text-diacritized", "text", lang="pt")
|
|
66
|
+
self.assertIn("native orthography", str(ctx.exception))
|
|
67
|
+
|
|
68
|
+
def test_strip_edge_does_not_change_text_to_ipa_route(self):
|
|
69
|
+
route = self.graph.route("text", "ipa")
|
|
70
|
+
self.assertEqual(len(route), 1)
|
|
71
|
+
edge = route[0]
|
|
72
|
+
self.assertEqual(edge.src, "text")
|
|
73
|
+
self.assertEqual(edge.dst, "ipa")
|
|
74
|
+
|
|
75
|
+
def test_strip_preserves_cyrillic_native_letters(self):
|
|
76
|
+
out = self.graph.convert("мой родно́й край", "text-diacritized",
|
|
77
|
+
"text", lang="ru")
|
|
78
|
+
self.assertEqual(out, "мой родной край")
|
|
79
|
+
out2 = self.graph.convert("ёлка", "text-diacritized", "text", lang="ru")
|
|
80
|
+
self.assertEqual(out2, "ёлка")
|
|
81
|
+
|
|
82
|
+
def test_strip_preserves_latin_stress_lang_diacritics(self):
|
|
83
|
+
# native macron (ī) plus an added combining acute overlay
|
|
84
|
+
out = self.graph.convert("Rī́ga", "text-diacritized", "text",
|
|
85
|
+
lang="lv")
|
|
86
|
+
self.assertEqual(out, "Rīga")
|
|
87
|
+
out2 = self.graph.convert("Rīga", "text-diacritized", "text", lang="lv")
|
|
88
|
+
self.assertEqual(out2, "Rīga")
|
|
89
|
+
|
|
90
|
+
def test_strip_preserves_arabic_hamza(self):
|
|
91
|
+
out = self.graph.convert("أَحْمَد", "text-diacritized", "text", lang="ar")
|
|
92
|
+
self.assertEqual(out, "أحمد")
|
|
93
|
+
|
|
94
|
+
def test_strip_refuses_aragonese(self):
|
|
95
|
+
with self.assertRaises(ValueError):
|
|
96
|
+
self.graph.convert("Cristián", "text-diacritized", "text", lang="arg")
|
|
97
|
+
|
|
98
|
+
def test_add_diacritics_does_not_misroute_herero(self):
|
|
99
|
+
from scriptconv.phonemizers.base import GraphemePhonemizer
|
|
100
|
+
self.assertEqual(GraphemePhonemizer().add_diacritics("teste", "her"), "teste")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
unittest.main()
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/hanja.tsv
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/ipa.csv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/ko_tables/yale.csv
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/__init__.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/codeswitch.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/diacritize.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/dialectal.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/english_g2p.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/espeak.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/frontend.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/levantine_g2p.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_thirdparty/shami/normalize.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/kog2p/__init__.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/kog2p/rulebook.txt
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/LICENSE.md
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/__init__.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/buck/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/buck/symbols.py
RENAMED
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a3 → scriptconv-0.0.4a4}/scriptconv/phonemizers/_vendored/mantoq/num2words.py
RENAMED
|
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
|
|
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
|
|
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
|