turkce-normalize 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mehmet Veysel Delibaş
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,333 @@
1
+ Metadata-Version: 2.4
2
+ Name: turkce-normalize
3
+ Version: 0.1.0
4
+ Summary: TTS için Türkçe metin normalizasyonu: sayı, tarih, saat, para, kısaltma, Roma rakamı, kesir, e-posta ve adres okunuşu.
5
+ Author: Mehmet Veysel Delibaş
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/seslen-tr/turkce-normalize
8
+ Project-URL: Source, https://github.com/seslen-tr/turkce-normalize
9
+ Project-URL: Issues, https://github.com/seslen-tr/turkce-normalize/issues
10
+ Keywords: türkçe,turkish,tts,text-normalization,normalizasyon,speech
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Natural Language :: Turkish
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
15
+ Classifier: Topic :: Text Processing :: Linguistic
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Dynamic: license-file
20
+
21
+ # turkce-normalize
22
+
23
+ > **English:** Turkish text normalization for text-to-speech. Expands numbers, dates, times, currency, abbreviations, Roman numerals, fractions, e-mail and web addresses into how they are read aloud, following Turkish rules (`14:30'da` → `on dört otuzda`). No dependencies, Python 3.9+, includes an abbreviation-aware sentence splitter for streaming LLM output.
24
+
25
+ TTS (metinden sese) modelleri için Türkçe metin normalizasyonu.
26
+
27
+ Chatterbox, XTTS, Piper, StyleTTS2 gibi açık kaynak modeller SSML desteklemez
28
+ ve `1.250,50 ₺`, `14:05`, `XX. yüzyıl`, `TBMM'de` gibi gösterimleri Türkçe
29
+ kurallarına göre okuyamaz. Bu paket metni modele göndermeden önce "okunur"
30
+ hale getirir:
31
+
32
+ ```text
33
+ Toplantı 14:30'da, %18 KDV dahil 1.250,50 ₺.
34
+ → Toplantı on dört otuzda, yüzde on sekiz katma değer vergisi dahil bin iki yüz elli lira elli kuruş.
35
+ ```
36
+
37
+ - Bağımlılığı yoktur (yalnızca standart kütüphane), Python 3.9+.
38
+ - Kurallar sıralıdır ve her biri testlerle sabitlenmiştir.
39
+ - Akış halinde gelen LLM çıktısı için kısaltma farkında bir cümle ayırıcı içerir.
40
+
41
+ ## Kurulum
42
+
43
+ ```bash
44
+ pip install . # depo kökünde
45
+ ```
46
+
47
+ ## Kullanım
48
+
49
+ ```python
50
+ from turkce_normalize import normalize
51
+
52
+ normalize("Sözleşme 15.03.2026 tarihinde yenilenecek.")
53
+ # 'Sözleşme on beş mart iki bin yirmi altı tarihinde yenilenecek.'
54
+
55
+ normalize("XX. yüzyılda II. Abdülhamit 1876-1909 arasında hüküm sürdü.")
56
+ # 'yirminci yüzyılda ikinci Abdülhamit bin sekiz yüz yetmiş altı ile bin dokuz yüz dokuz arasında hüküm sürdü.'
57
+
58
+ normalize("TBMM'de TRT ve NATO konuşuldu.")
59
+ # 'te be me mede te re te ve nato konuşuldu.'
60
+ ```
61
+
62
+ Komut satırından:
63
+
64
+ ```bash
65
+ python -m turkce_normalize "Tarifte 1/2 kilo un, 3/4 su bardağı süt var."
66
+ # Tarifte yarım kilo un, dörtte üç su bardağı süt var.
67
+
68
+ echo "3-5 kişilik ekip -5 derecede çalıştı." | python -m turkce_normalize
69
+ # üç beş kişilik ekip eksi beş derecede çalıştı.
70
+ ```
71
+
72
+ ### Seçenekler
73
+
74
+ ```python
75
+ normalize(metin,
76
+ telaffuz=None, # telaffuz sözlüğü (varsayılan: gömülü CBS/yazılım sözlüğü)
77
+ cografi=True, # koordinat, ölçek, pafta, EPSG kuralları
78
+ kisaltma_harf_harf=True) # "TBMM" -> "te be me me"
79
+ ```
80
+
81
+ ### Sayı okuma
82
+
83
+ ```python
84
+ from turkce_normalize import sayi_oku, sira_oku, ondalik_oku, rakamlari_tek_tek
85
+
86
+ sayi_oku(1250) # 'bin iki yüz elli'
87
+ sayi_oku(1_000_000) # 'bir milyon'
88
+ sira_oku(21) # 'yirmi birinci'
89
+ ondalik_oku(3, "05") # 'üç virgül sıfır beş'
90
+ rakamlari_tek_tek("0212") # 'sıfır iki bir iki'
91
+ ```
92
+
93
+ ### Cümle ayırıcı (LLM akışı için)
94
+
95
+ Cevabın tamamı bitmeden ilk cümle seslendirilmeye başlasın diye. `Sn. Dr.`
96
+ gibi kısaltmalarda ve `15.03.2026` gibi tarihlerde bölmez; 12 karakterden
97
+ kısa cümleleri ("Evet.") sonrakiyle birleştirir.
98
+
99
+ ```python
100
+ from turkce_normalize import CumleAyirici, cumleleri_ayir
101
+
102
+ ayirici = CumleAyirici()
103
+ for parca in llm_akisi: # token token gelen metin
104
+ for cumle in ayirici.push(parca):
105
+ seslendir(normalize(cumle))
106
+ for cumle in ayirici.flush():
107
+ seslendir(normalize(cumle))
108
+
109
+ cumleleri_ayir("Merhaba. Sn. Dr. Ayşe Yılmaz geldi! Devam edelim mi?")
110
+ # ['Merhaba. Sn. Dr. Ayşe Yılmaz geldi!', 'Devam edelim mi?']
111
+ ```
112
+
113
+ `cumle_akisi(iterable)` ve `cumle_akisi_async(async_iterable)` aynı işi
114
+ üreteç olarak yapar.
115
+
116
+ ### Duraklama işaretleri
117
+
118
+ SSML olmayan modellerde duraklamayı kendin kurmak için metni `[dur]`
119
+ işaretlerinden böler:
120
+
121
+ ```python
122
+ from turkce_normalize import isaretleri_ayir, isaretleri_temizle
123
+
124
+ isaretleri_ayir("Sorgu bitti. [dur:0.8] Toplam 340 hektar.")
125
+ # [Parca(metin='Sorgu bitti.', duraklama=0.8), Parca(metin='Toplam 340 hektar.', duraklama=0)]
126
+
127
+ isaretleri_temizle("Sorgu bitti. [dur:0.8] Toplam.") # altyazı için
128
+ # 'Sorgu bitti. Toplam.'
129
+ ```
130
+
131
+ `[dur]` tek başına 0,45 sn'dir; üst sınır 5 sn. Bilinmeyen işaretler
132
+ (`[vurgu]`) bilerek silinmez — model okur, sen hatayı duyarsın.
133
+
134
+ ### Telaffuz sözlüğü
135
+
136
+ Gömülü sözlük CBS ve yazılım jargonunu Türk kulağına doğru gelen yazımla
137
+ değiştirir (`shapefile` → `şeypfayl`, `WMS` → `ve me se`, `API` → `ey pi ay`).
138
+ Kendi terimlerini ekleyebilirsin:
139
+
140
+ ```python
141
+ from turkce_normalize import normalize, sozluk_birlestir, VARSAYILAN_SOZLUK
142
+
143
+ sozluk = sozluk_birlestir(VARSAYILAN_SOZLUK, {"KENTGIS": "kent cis"})
144
+ normalize("KENTGIS açılıyor.", telaffuz=sozluk) # 'kent cis açılıyor.'
145
+ ```
146
+
147
+ Eşleşme büyük/küçük harf duyarsızdır; ek almış kelimeler (`APIyi`) bölünmez.
148
+
149
+ ## Desteklenen gösterimler
150
+
151
+ | Gösterim | Örnek | Okunuş |
152
+ |---|---|---|
153
+ | Tam sayı | `1.250` | bin iki yüz elli |
154
+ | Ondalık | `3,5` / `3,05` | üç virgül beş / üç virgül sıfır beş |
155
+ | Sıra sayısı | `3. madde` | üçüncü madde |
156
+ | Para | `1.250,50 ₺`, `2.360 TL` | bin iki yüz elli lira elli kuruş |
157
+ | Yüzde | `%18`, `% 18,5` | yüzde on sekiz |
158
+ | Tarih | `15.03.2026`, `15/03/2026` | on beş mart iki bin yirmi altı |
159
+ | Saat | `14:05`, `09:00'da` | on dört sıfır beş, dokuzda |
160
+ | Telefon | `0212 555 44 33`, `0212-555-44-33` | sıfır iki bir iki beş beş beş … |
161
+ | IBAN | `TR33 0006 …` | te re üç üç sıfır sıfır … |
162
+ | Kısaltma (sözlük) | `KDV`, `Dr.`, `vb.`, `Mah.` | katma değer vergisi, doktor, ve benzeri, mahallesi |
163
+ | Birim | `12 km`, `85 m²`, `5 kg` | on iki kilometre, … metrekare, … kilogram |
164
+ | **Roma rakamı** | `XX. yüzyıl`, `II. Abdülhamit` | yirminci yüzyıl, ikinci Abdülhamit |
165
+ | **Kesir** | `1/2 kilo`, `1/4`, `3/4`, `1/6`, `2 1/2` | yarım kilo, çeyrek, dörtte üç, altıda bir, iki buçuk |
166
+ | **Aralık** | `3-5 kişi`, `2020-2025`, `3-5. maddeler` | üç beş kişi, … ile …, üçüncü ile beşinci |
167
+ | **Eksi sayı** | `-5 derece` | eksi beş derece |
168
+ | **Büyük harfli kısaltma** | `TBMM'de`, `CHP`, `NATO` | te be me mede, ce he pe, nato |
169
+ | **E-posta** | `ali.veli@ornek.com.tr` | ali nokta veli et ornek nokta com nokta te re |
170
+ | **Web adresi** | `https://www.ornek.gov.tr/harita` | ornek nokta gov nokta te re bölü harita |
171
+ | **Simgeler** | `2+2=4`, `&`, `25°C`, `±0,5` | iki artı iki eşittir dört, ve, yirmi beş derece, artı eksi … |
172
+ | Koordinat | `39.9334, 32.8597` | enlem otuz dokuz virgül dokuz üç üç dört, boylam … |
173
+ | Derece-dakika-saniye | `39°56'12"K` | otuz dokuz derece elli altı dakika on iki saniye kuzey |
174
+ | Ölçek | `1/25.000`, `1:1000` | yirmi beş binde bir, binde bir |
175
+ | Pafta kodu | `G22-b-14c-2d` | ge yirmi iki be on dört ce iki de |
176
+ | EPSG / UTM | `EPSG:5254`, `UTM 35N` | e pe es ge beş iki beş dört, u te me otuz beşinci dilim kuzey |
177
+ | Alan birimi | `340 ha`, `12 da` | üç yüz kırk hektar, on iki dekar |
178
+
179
+ Kalın satırlar kaynak projede olmayıp bu pakette eklenenlerdir. Ayrıntıları:
180
+
181
+ ### Roma rakamları
182
+
183
+ Yalnızca **ardından nokta gelen** geçerli Roma rakamları (I–MMMM) sıra sayısı
184
+ olarak okunur:
185
+
186
+ - Nokta ve ardından **küçük harfle** başlayan kelime: `XX. yüzyıl` → yirminci yüzyıl.
187
+ - Nokta ve ardından **büyük harfle** başlayan kelime (hükümdar sırası): yalnızca
188
+ I, V, X harflerinden oluşan ve 39'u aşmayan rakamlar: `III. Selim` → üçüncü Selim,
189
+ `I. Dünya Savaşı` → birinci Dünya Savaşı.
190
+ - Tek harfli rakam büyük harfli bir kelimeden sonra geliyorsa isim baş harfi
191
+ sayılır (`Mehmet V. Delibaş` değişmez); `Sultan`, `Kral`, `Papa` gibi
192
+ unvanlar (`UNVANLAR`) istisnadır.
193
+ - `MI`, `CD`, `CM`, `MM`, `DIV` … gibi kelime/kısaltma olabilenler kara
194
+ listededir (`ROMA_KARA_LISTE`).
195
+
196
+ ### Kesirler
197
+
198
+ - `1/2` → yarım, `1/4` → çeyrek; ötekiler "payda + bulunma eki + pay":
199
+ `3/4` → dörtte üç, `2/3` → üçte iki, `1/6` → altıda bir, `3/40` → kırkta üç.
200
+ - Bulunma eki ünlü uyumuna ve ünsüz benzeşmesine uyar (`bulunma_eki()`):
201
+ ç, f, h, k, p, s, ş, t'den sonra **te/ta**, diğerlerinde **de/da**.
202
+ - Tam sayılı kesir: `2 1/2` → iki buçuk, `3 3/4` → üç tam dörtte üç; `½ ¼ ¾`
203
+ karakterleri de okunur.
204
+ - Yalnızca **bayağı kesirler** (pay < payda) okunur; `15/03` dokunulmaz. `24/7` → yirmi dört yedi.
205
+ - Ekli kesir: `1/2'si` → yarısı, `1/4'ü` → çeyreği, `3/4'ü` → dörtte üçü.
206
+ - Tarih (`15/03/2026`) ve ölçek (`1/25.000`, `1:1000`) kuralları önce çalışır.
207
+ - Adreste kapı numarası: `No: 5/7` → No: beş bölü yedi.
208
+
209
+ ### Aralıklar
210
+
211
+ - İki sayı da 100 veya küçük ve ilki küçükse günlük konuşmadaki gibi:
212
+ `3-5 kişi` → üç beş kişi, `10–20 dakika` → on yirmi dakika.
213
+ - Diğerleri "X ile Y": `2020-2025` → iki bin yirmi ile iki bin yirmi beş,
214
+ `1.000-2.000 TL` → bin ile iki bin lira.
215
+ - Sıra sayısı aralığı: `3-5. maddeler` → üçüncü ile beşinci maddeler.
216
+ - Saat ve tarih aralığı: `14:30-15:30` → on dört otuz ile on beş otuz;
217
+ yüzde aralığı: `%10-20` → yüzde on ile yirmi.
218
+ - İlki büyük ya da eşitse skor gibi okunur: `3-1` → üç bir. Sıfırla başlıyorsa (kod)
219
+ veya ikiden fazla sayı tireyle bağlıysa (`0212-555-44-33`) aralık sayılmaz.
220
+ - Önünde harf/rakam olmayan tire eksi işaretidir: `-5 derece` → eksi beş derece.
221
+
222
+ ### Büyük harfli kısaltmalar
223
+
224
+ - 2–5 büyük harften oluşan tokenlar Türkçe harf adlarıyla okunur:
225
+ `TBMM` → te be me me, `AKP` → a ka pe. Harf adları: a, be, ce, çe, de, e, fe,
226
+ ge, yumuşak ge, he, ı, i, je, ka, le, me, ne, o, ö, pe, re, se, şe, te, u, ü,
227
+ ve, ye, ze; Q kü, W çift ve, X iks. K kısaltmalardaki yaygın okunuşla "ka"dır
228
+ (TDK alfabesinde "ke"); `HARF_OKUNUSU["K"] = "ke"` ile değiştirilebilir.
229
+ - Kesmeden sonraki ek okunuşa bitişir: `TBMM'de` → te be me mede,
230
+ `ABD'li` → a be deli.
231
+ - Kelime gibi okunan kısaltmalar (`KELIME_GIBI_OKUNANLAR`) harf harf okunmaz,
232
+ modelin hecelememesi için küçük harfe çevrilir: NATO, NASA, AFAD, TOKİ,
233
+ BOTAŞ, TEDAŞ, İSKİ, ASKİ, ODTÜ, YÖK, TÜİK, İŞKUR, MİT, ASELSAN, TÜBİTAK,
234
+ HAVELSAN, ROKETSAN, KOSGEB, UNESCO, UNICEF … `ÖSYM`, `SGK`, `İETT`, `PTT`
235
+ harf harf okunur.
236
+ - Büyük harfle yazılmış sıradan kelimeler (`UYARI`, `VE`, `MI` …,
237
+ `BILINEN_KELIMELER`) ve bunların komşusu olan büyük harfli kelimeler
238
+ (`GELDİ MI`) kısaltma sayılmaz. 6+ harfli tokenlar ve yalnızca I/V/X'ten
239
+ oluşanlar (Roma rakamı) dokunulmaz.
240
+ - Sözlükteki kısaltmalar (`KDV`, `TL`, `IBAN` …) önce gelir.
241
+ - Listeler genişletilebilir kümelerdir:
242
+
243
+ ```python
244
+ from turkce_normalize import KELIME_GIBI_OKUNANLAR, BILINEN_KELIMELER
245
+ KELIME_GIBI_OKUNANLAR.add("TEKNOFEST")
246
+ BILINEN_KELIMELER.add("KAMPANYA")
247
+ ```
248
+
249
+ ### E-posta ve web adresi
250
+
251
+ - `@` → et, `.` → nokta, `/` → bölü, `-` → tire, `_` → alt çizgi.
252
+ - `http(s)://` ve `www.` atılır; sorgu (`?…`) ve çapa (`#…`) okunmaz.
253
+ - Son etiket iki harfli ülke uzantısıysa harf harf okunur (`tr` → te re);
254
+ `com`, `gov`, `org` yazıldığı gibi kalır.
255
+ - Şemasız alan adları yalnızca küçük harfle ve bilinen bir uzantıyla
256
+ (`com`, `net`, `org`, `gov`, `edu`, `tr`, `io` …) yazılmışsa yakalanır.
257
+ - Adres parçaları sonra telaffuz sözlüğünden geçer: `rapor.pdf` → rapor nokta pe de fe.
258
+
259
+ ### Simgeler
260
+
261
+ `&` → ve; `+` iki sayı arasında → artı (`+90` gibi önekler dokunulmaz);
262
+ `=` → eşittir; `°C` / `℃` → derece; `°F` → derece fahrenhayt;
263
+ sayıdan sonra `°` → derece; `±` → artı eksi.
264
+
265
+ ## Kural sırası
266
+
267
+ Sıra rastgele değildir; bütün bir birim olan gösterimler sayı kurallarından
268
+ önce gelir, yoksa içlerindeki rakamlar genel kurallara yem olur:
269
+
270
+ 1. e-posta ve web adresi → 2. sıcaklık → 3. coğrafi gösterimler →
271
+ 4. telaffuz sözlüğü → 5. IBAN → 6. telefon → 7. tarih ve saat (ISO ve tireli tarih dahil) →
272
+ 8. simgeler → 9. kesirler → 10. aralıklar → 11. eksi sayılar → 12. para →
273
+ 13. yüzde → 14. kısaltmalar ve birimler → 15. Roma rakamları →
274
+ 16. büyük harfli kısaltmalar → 17. sıra sayıları → 18. ondalık →
275
+ 19. tam sayılar → 20. kesme işaretinin kaldırılması
276
+
277
+ ## Sınırlamalar
278
+
279
+ - Ek uyumu yapılmaz: `5'te` → beşte doğru çıkar çünkü ek zaten okunuşa göre
280
+ yazılmıştır; ama yanlış yazılmış ekler düzeltilmez.
281
+ - Nokta, ardından tam üç rakam geliyorsa binlik ayırıcıdır (`3.500` → üç bin beş yüz);
282
+ aksi halde İngilizce usulü ondalık sayılır (`3.5` → üç virgül beş). Üç ve daha fazla
283
+ noktalı grupta üç rakamlı olmayan grup varsa sürüm numarasıdır (`1.2.3` → bir nokta iki
284
+ nokta üç). Noktalı saat yalnız "saat" sözcüğünden sonra tanınır (`saat 14.30`).
285
+ - Sıra sayısı ve Roma rakamı için ardından bir kelime gelmesi gerekir; cümle
286
+ sonundaki `XIV.` dokunulmaz.
287
+ - Cümle başındaki büyük harf, dönüştürülen kelimeyle birlikte kaybolur
288
+ (`XX. yüzyıl` → yirminci yüzyıl).
289
+ - Büyük harfli kısaltma tespiti sezgiseldir: listede olmayan 2–5 harfli büyük
290
+ harfli sıradan kelimeler harf harf okunabilir; tamamı büyük harfle yazılmış
291
+ metinlerde `kisaltma_harf_harf=False` kullanın. Tek harfli Roma rakamı bir
292
+ ismin baş harfiyle karışabilir (`romanı V. Hugo yazdı`).
293
+ - Tam sayılı ekli kesir (`2 1/2'si`) düzeltilmez.
294
+ - Şemasız alan adlarında Türkçe karakter (IDN) desteklenmez.
295
+ - İngilizce normalizasyon kapsam dışıdır; metin Türkçe kabul edilir.
296
+
297
+ ## Testler
298
+
299
+ ```bash
300
+ python -m unittest discover -s tests
301
+ ```
302
+
303
+ `tests/test_parite.py` kaynak TypeScript projesindeki testlerin birebir
304
+ karşılığıdır (beklenen çıktılar aynı); `tests/test_yeni.py` bu pakette
305
+ eklenen özellikleri ve yanlış pozitifleri sabitler.
306
+
307
+ ## Lisans
308
+
309
+ MIT — bkz. [LICENSE](LICENSE).
310
+
311
+ ---
312
+
313
+ ## English summary
314
+
315
+ **turkce-normalize** is a dependency-free Python (3.9+) text normalizer that
316
+ prepares Turkish text for TTS engines without SSML support (Chatterbox, XTTS,
317
+ Piper, StyleTTS2…). It spells out numbers, ordinals, decimals, money, percent,
318
+ dates, times, phone numbers, IBANs, abbreviations and units; Roman numerals
319
+ (`XX. yüzyıl` → "yirminci yüzyıl"), fractions with correct vowel harmony
320
+ (`3/4` → "dörtte üç"), numeric ranges, negative numbers, all-caps acronyms read
321
+ letter by letter with Turkish letter names (`TBMM'de` → "te be me mede", with a
322
+ configurable list of acronyms read as words such as NATO, TÜBİTAK), e-mail
323
+ addresses and URLs, common symbols, and GIS notation (coordinates, map scale,
324
+ sheet codes, EPSG/UTM). It also ships an abbreviation-aware streaming sentence
325
+ splitter for LLM output and an inline pause-marker parser (`[dur:0.8]`).
326
+
327
+ ```python
328
+ from turkce_normalize import normalize
329
+ normalize("Toplantı 14:30'da, %18 KDV dahil 1.250,50 ₺.")
330
+ # 'Toplantı on dört otuzda, yüzde on sekiz katma değer vergisi dahil bin iki yüz elli lira elli kuruş.'
331
+ ```
332
+
333
+ CLI: `python -m turkce_normalize "metin"`. Tests: `python -m unittest discover -s tests`. License: MIT.
@@ -0,0 +1,313 @@
1
+ # turkce-normalize
2
+
3
+ > **English:** Turkish text normalization for text-to-speech. Expands numbers, dates, times, currency, abbreviations, Roman numerals, fractions, e-mail and web addresses into how they are read aloud, following Turkish rules (`14:30'da` → `on dört otuzda`). No dependencies, Python 3.9+, includes an abbreviation-aware sentence splitter for streaming LLM output.
4
+
5
+ TTS (metinden sese) modelleri için Türkçe metin normalizasyonu.
6
+
7
+ Chatterbox, XTTS, Piper, StyleTTS2 gibi açık kaynak modeller SSML desteklemez
8
+ ve `1.250,50 ₺`, `14:05`, `XX. yüzyıl`, `TBMM'de` gibi gösterimleri Türkçe
9
+ kurallarına göre okuyamaz. Bu paket metni modele göndermeden önce "okunur"
10
+ hale getirir:
11
+
12
+ ```text
13
+ Toplantı 14:30'da, %18 KDV dahil 1.250,50 ₺.
14
+ → Toplantı on dört otuzda, yüzde on sekiz katma değer vergisi dahil bin iki yüz elli lira elli kuruş.
15
+ ```
16
+
17
+ - Bağımlılığı yoktur (yalnızca standart kütüphane), Python 3.9+.
18
+ - Kurallar sıralıdır ve her biri testlerle sabitlenmiştir.
19
+ - Akış halinde gelen LLM çıktısı için kısaltma farkında bir cümle ayırıcı içerir.
20
+
21
+ ## Kurulum
22
+
23
+ ```bash
24
+ pip install . # depo kökünde
25
+ ```
26
+
27
+ ## Kullanım
28
+
29
+ ```python
30
+ from turkce_normalize import normalize
31
+
32
+ normalize("Sözleşme 15.03.2026 tarihinde yenilenecek.")
33
+ # 'Sözleşme on beş mart iki bin yirmi altı tarihinde yenilenecek.'
34
+
35
+ normalize("XX. yüzyılda II. Abdülhamit 1876-1909 arasında hüküm sürdü.")
36
+ # 'yirminci yüzyılda ikinci Abdülhamit bin sekiz yüz yetmiş altı ile bin dokuz yüz dokuz arasında hüküm sürdü.'
37
+
38
+ normalize("TBMM'de TRT ve NATO konuşuldu.")
39
+ # 'te be me mede te re te ve nato konuşuldu.'
40
+ ```
41
+
42
+ Komut satırından:
43
+
44
+ ```bash
45
+ python -m turkce_normalize "Tarifte 1/2 kilo un, 3/4 su bardağı süt var."
46
+ # Tarifte yarım kilo un, dörtte üç su bardağı süt var.
47
+
48
+ echo "3-5 kişilik ekip -5 derecede çalıştı." | python -m turkce_normalize
49
+ # üç beş kişilik ekip eksi beş derecede çalıştı.
50
+ ```
51
+
52
+ ### Seçenekler
53
+
54
+ ```python
55
+ normalize(metin,
56
+ telaffuz=None, # telaffuz sözlüğü (varsayılan: gömülü CBS/yazılım sözlüğü)
57
+ cografi=True, # koordinat, ölçek, pafta, EPSG kuralları
58
+ kisaltma_harf_harf=True) # "TBMM" -> "te be me me"
59
+ ```
60
+
61
+ ### Sayı okuma
62
+
63
+ ```python
64
+ from turkce_normalize import sayi_oku, sira_oku, ondalik_oku, rakamlari_tek_tek
65
+
66
+ sayi_oku(1250) # 'bin iki yüz elli'
67
+ sayi_oku(1_000_000) # 'bir milyon'
68
+ sira_oku(21) # 'yirmi birinci'
69
+ ondalik_oku(3, "05") # 'üç virgül sıfır beş'
70
+ rakamlari_tek_tek("0212") # 'sıfır iki bir iki'
71
+ ```
72
+
73
+ ### Cümle ayırıcı (LLM akışı için)
74
+
75
+ Cevabın tamamı bitmeden ilk cümle seslendirilmeye başlasın diye. `Sn. Dr.`
76
+ gibi kısaltmalarda ve `15.03.2026` gibi tarihlerde bölmez; 12 karakterden
77
+ kısa cümleleri ("Evet.") sonrakiyle birleştirir.
78
+
79
+ ```python
80
+ from turkce_normalize import CumleAyirici, cumleleri_ayir
81
+
82
+ ayirici = CumleAyirici()
83
+ for parca in llm_akisi: # token token gelen metin
84
+ for cumle in ayirici.push(parca):
85
+ seslendir(normalize(cumle))
86
+ for cumle in ayirici.flush():
87
+ seslendir(normalize(cumle))
88
+
89
+ cumleleri_ayir("Merhaba. Sn. Dr. Ayşe Yılmaz geldi! Devam edelim mi?")
90
+ # ['Merhaba. Sn. Dr. Ayşe Yılmaz geldi!', 'Devam edelim mi?']
91
+ ```
92
+
93
+ `cumle_akisi(iterable)` ve `cumle_akisi_async(async_iterable)` aynı işi
94
+ üreteç olarak yapar.
95
+
96
+ ### Duraklama işaretleri
97
+
98
+ SSML olmayan modellerde duraklamayı kendin kurmak için metni `[dur]`
99
+ işaretlerinden böler:
100
+
101
+ ```python
102
+ from turkce_normalize import isaretleri_ayir, isaretleri_temizle
103
+
104
+ isaretleri_ayir("Sorgu bitti. [dur:0.8] Toplam 340 hektar.")
105
+ # [Parca(metin='Sorgu bitti.', duraklama=0.8), Parca(metin='Toplam 340 hektar.', duraklama=0)]
106
+
107
+ isaretleri_temizle("Sorgu bitti. [dur:0.8] Toplam.") # altyazı için
108
+ # 'Sorgu bitti. Toplam.'
109
+ ```
110
+
111
+ `[dur]` tek başına 0,45 sn'dir; üst sınır 5 sn. Bilinmeyen işaretler
112
+ (`[vurgu]`) bilerek silinmez — model okur, sen hatayı duyarsın.
113
+
114
+ ### Telaffuz sözlüğü
115
+
116
+ Gömülü sözlük CBS ve yazılım jargonunu Türk kulağına doğru gelen yazımla
117
+ değiştirir (`shapefile` → `şeypfayl`, `WMS` → `ve me se`, `API` → `ey pi ay`).
118
+ Kendi terimlerini ekleyebilirsin:
119
+
120
+ ```python
121
+ from turkce_normalize import normalize, sozluk_birlestir, VARSAYILAN_SOZLUK
122
+
123
+ sozluk = sozluk_birlestir(VARSAYILAN_SOZLUK, {"KENTGIS": "kent cis"})
124
+ normalize("KENTGIS açılıyor.", telaffuz=sozluk) # 'kent cis açılıyor.'
125
+ ```
126
+
127
+ Eşleşme büyük/küçük harf duyarsızdır; ek almış kelimeler (`APIyi`) bölünmez.
128
+
129
+ ## Desteklenen gösterimler
130
+
131
+ | Gösterim | Örnek | Okunuş |
132
+ |---|---|---|
133
+ | Tam sayı | `1.250` | bin iki yüz elli |
134
+ | Ondalık | `3,5` / `3,05` | üç virgül beş / üç virgül sıfır beş |
135
+ | Sıra sayısı | `3. madde` | üçüncü madde |
136
+ | Para | `1.250,50 ₺`, `2.360 TL` | bin iki yüz elli lira elli kuruş |
137
+ | Yüzde | `%18`, `% 18,5` | yüzde on sekiz |
138
+ | Tarih | `15.03.2026`, `15/03/2026` | on beş mart iki bin yirmi altı |
139
+ | Saat | `14:05`, `09:00'da` | on dört sıfır beş, dokuzda |
140
+ | Telefon | `0212 555 44 33`, `0212-555-44-33` | sıfır iki bir iki beş beş beş … |
141
+ | IBAN | `TR33 0006 …` | te re üç üç sıfır sıfır … |
142
+ | Kısaltma (sözlük) | `KDV`, `Dr.`, `vb.`, `Mah.` | katma değer vergisi, doktor, ve benzeri, mahallesi |
143
+ | Birim | `12 km`, `85 m²`, `5 kg` | on iki kilometre, … metrekare, … kilogram |
144
+ | **Roma rakamı** | `XX. yüzyıl`, `II. Abdülhamit` | yirminci yüzyıl, ikinci Abdülhamit |
145
+ | **Kesir** | `1/2 kilo`, `1/4`, `3/4`, `1/6`, `2 1/2` | yarım kilo, çeyrek, dörtte üç, altıda bir, iki buçuk |
146
+ | **Aralık** | `3-5 kişi`, `2020-2025`, `3-5. maddeler` | üç beş kişi, … ile …, üçüncü ile beşinci |
147
+ | **Eksi sayı** | `-5 derece` | eksi beş derece |
148
+ | **Büyük harfli kısaltma** | `TBMM'de`, `CHP`, `NATO` | te be me mede, ce he pe, nato |
149
+ | **E-posta** | `ali.veli@ornek.com.tr` | ali nokta veli et ornek nokta com nokta te re |
150
+ | **Web adresi** | `https://www.ornek.gov.tr/harita` | ornek nokta gov nokta te re bölü harita |
151
+ | **Simgeler** | `2+2=4`, `&`, `25°C`, `±0,5` | iki artı iki eşittir dört, ve, yirmi beş derece, artı eksi … |
152
+ | Koordinat | `39.9334, 32.8597` | enlem otuz dokuz virgül dokuz üç üç dört, boylam … |
153
+ | Derece-dakika-saniye | `39°56'12"K` | otuz dokuz derece elli altı dakika on iki saniye kuzey |
154
+ | Ölçek | `1/25.000`, `1:1000` | yirmi beş binde bir, binde bir |
155
+ | Pafta kodu | `G22-b-14c-2d` | ge yirmi iki be on dört ce iki de |
156
+ | EPSG / UTM | `EPSG:5254`, `UTM 35N` | e pe es ge beş iki beş dört, u te me otuz beşinci dilim kuzey |
157
+ | Alan birimi | `340 ha`, `12 da` | üç yüz kırk hektar, on iki dekar |
158
+
159
+ Kalın satırlar kaynak projede olmayıp bu pakette eklenenlerdir. Ayrıntıları:
160
+
161
+ ### Roma rakamları
162
+
163
+ Yalnızca **ardından nokta gelen** geçerli Roma rakamları (I–MMMM) sıra sayısı
164
+ olarak okunur:
165
+
166
+ - Nokta ve ardından **küçük harfle** başlayan kelime: `XX. yüzyıl` → yirminci yüzyıl.
167
+ - Nokta ve ardından **büyük harfle** başlayan kelime (hükümdar sırası): yalnızca
168
+ I, V, X harflerinden oluşan ve 39'u aşmayan rakamlar: `III. Selim` → üçüncü Selim,
169
+ `I. Dünya Savaşı` → birinci Dünya Savaşı.
170
+ - Tek harfli rakam büyük harfli bir kelimeden sonra geliyorsa isim baş harfi
171
+ sayılır (`Mehmet V. Delibaş` değişmez); `Sultan`, `Kral`, `Papa` gibi
172
+ unvanlar (`UNVANLAR`) istisnadır.
173
+ - `MI`, `CD`, `CM`, `MM`, `DIV` … gibi kelime/kısaltma olabilenler kara
174
+ listededir (`ROMA_KARA_LISTE`).
175
+
176
+ ### Kesirler
177
+
178
+ - `1/2` → yarım, `1/4` → çeyrek; ötekiler "payda + bulunma eki + pay":
179
+ `3/4` → dörtte üç, `2/3` → üçte iki, `1/6` → altıda bir, `3/40` → kırkta üç.
180
+ - Bulunma eki ünlü uyumuna ve ünsüz benzeşmesine uyar (`bulunma_eki()`):
181
+ ç, f, h, k, p, s, ş, t'den sonra **te/ta**, diğerlerinde **de/da**.
182
+ - Tam sayılı kesir: `2 1/2` → iki buçuk, `3 3/4` → üç tam dörtte üç; `½ ¼ ¾`
183
+ karakterleri de okunur.
184
+ - Yalnızca **bayağı kesirler** (pay < payda) okunur; `15/03` dokunulmaz. `24/7` → yirmi dört yedi.
185
+ - Ekli kesir: `1/2'si` → yarısı, `1/4'ü` → çeyreği, `3/4'ü` → dörtte üçü.
186
+ - Tarih (`15/03/2026`) ve ölçek (`1/25.000`, `1:1000`) kuralları önce çalışır.
187
+ - Adreste kapı numarası: `No: 5/7` → No: beş bölü yedi.
188
+
189
+ ### Aralıklar
190
+
191
+ - İki sayı da 100 veya küçük ve ilki küçükse günlük konuşmadaki gibi:
192
+ `3-5 kişi` → üç beş kişi, `10–20 dakika` → on yirmi dakika.
193
+ - Diğerleri "X ile Y": `2020-2025` → iki bin yirmi ile iki bin yirmi beş,
194
+ `1.000-2.000 TL` → bin ile iki bin lira.
195
+ - Sıra sayısı aralığı: `3-5. maddeler` → üçüncü ile beşinci maddeler.
196
+ - Saat ve tarih aralığı: `14:30-15:30` → on dört otuz ile on beş otuz;
197
+ yüzde aralığı: `%10-20` → yüzde on ile yirmi.
198
+ - İlki büyük ya da eşitse skor gibi okunur: `3-1` → üç bir. Sıfırla başlıyorsa (kod)
199
+ veya ikiden fazla sayı tireyle bağlıysa (`0212-555-44-33`) aralık sayılmaz.
200
+ - Önünde harf/rakam olmayan tire eksi işaretidir: `-5 derece` → eksi beş derece.
201
+
202
+ ### Büyük harfli kısaltmalar
203
+
204
+ - 2–5 büyük harften oluşan tokenlar Türkçe harf adlarıyla okunur:
205
+ `TBMM` → te be me me, `AKP` → a ka pe. Harf adları: a, be, ce, çe, de, e, fe,
206
+ ge, yumuşak ge, he, ı, i, je, ka, le, me, ne, o, ö, pe, re, se, şe, te, u, ü,
207
+ ve, ye, ze; Q kü, W çift ve, X iks. K kısaltmalardaki yaygın okunuşla "ka"dır
208
+ (TDK alfabesinde "ke"); `HARF_OKUNUSU["K"] = "ke"` ile değiştirilebilir.
209
+ - Kesmeden sonraki ek okunuşa bitişir: `TBMM'de` → te be me mede,
210
+ `ABD'li` → a be deli.
211
+ - Kelime gibi okunan kısaltmalar (`KELIME_GIBI_OKUNANLAR`) harf harf okunmaz,
212
+ modelin hecelememesi için küçük harfe çevrilir: NATO, NASA, AFAD, TOKİ,
213
+ BOTAŞ, TEDAŞ, İSKİ, ASKİ, ODTÜ, YÖK, TÜİK, İŞKUR, MİT, ASELSAN, TÜBİTAK,
214
+ HAVELSAN, ROKETSAN, KOSGEB, UNESCO, UNICEF … `ÖSYM`, `SGK`, `İETT`, `PTT`
215
+ harf harf okunur.
216
+ - Büyük harfle yazılmış sıradan kelimeler (`UYARI`, `VE`, `MI` …,
217
+ `BILINEN_KELIMELER`) ve bunların komşusu olan büyük harfli kelimeler
218
+ (`GELDİ MI`) kısaltma sayılmaz. 6+ harfli tokenlar ve yalnızca I/V/X'ten
219
+ oluşanlar (Roma rakamı) dokunulmaz.
220
+ - Sözlükteki kısaltmalar (`KDV`, `TL`, `IBAN` …) önce gelir.
221
+ - Listeler genişletilebilir kümelerdir:
222
+
223
+ ```python
224
+ from turkce_normalize import KELIME_GIBI_OKUNANLAR, BILINEN_KELIMELER
225
+ KELIME_GIBI_OKUNANLAR.add("TEKNOFEST")
226
+ BILINEN_KELIMELER.add("KAMPANYA")
227
+ ```
228
+
229
+ ### E-posta ve web adresi
230
+
231
+ - `@` → et, `.` → nokta, `/` → bölü, `-` → tire, `_` → alt çizgi.
232
+ - `http(s)://` ve `www.` atılır; sorgu (`?…`) ve çapa (`#…`) okunmaz.
233
+ - Son etiket iki harfli ülke uzantısıysa harf harf okunur (`tr` → te re);
234
+ `com`, `gov`, `org` yazıldığı gibi kalır.
235
+ - Şemasız alan adları yalnızca küçük harfle ve bilinen bir uzantıyla
236
+ (`com`, `net`, `org`, `gov`, `edu`, `tr`, `io` …) yazılmışsa yakalanır.
237
+ - Adres parçaları sonra telaffuz sözlüğünden geçer: `rapor.pdf` → rapor nokta pe de fe.
238
+
239
+ ### Simgeler
240
+
241
+ `&` → ve; `+` iki sayı arasında → artı (`+90` gibi önekler dokunulmaz);
242
+ `=` → eşittir; `°C` / `℃` → derece; `°F` → derece fahrenhayt;
243
+ sayıdan sonra `°` → derece; `±` → artı eksi.
244
+
245
+ ## Kural sırası
246
+
247
+ Sıra rastgele değildir; bütün bir birim olan gösterimler sayı kurallarından
248
+ önce gelir, yoksa içlerindeki rakamlar genel kurallara yem olur:
249
+
250
+ 1. e-posta ve web adresi → 2. sıcaklık → 3. coğrafi gösterimler →
251
+ 4. telaffuz sözlüğü → 5. IBAN → 6. telefon → 7. tarih ve saat (ISO ve tireli tarih dahil) →
252
+ 8. simgeler → 9. kesirler → 10. aralıklar → 11. eksi sayılar → 12. para →
253
+ 13. yüzde → 14. kısaltmalar ve birimler → 15. Roma rakamları →
254
+ 16. büyük harfli kısaltmalar → 17. sıra sayıları → 18. ondalık →
255
+ 19. tam sayılar → 20. kesme işaretinin kaldırılması
256
+
257
+ ## Sınırlamalar
258
+
259
+ - Ek uyumu yapılmaz: `5'te` → beşte doğru çıkar çünkü ek zaten okunuşa göre
260
+ yazılmıştır; ama yanlış yazılmış ekler düzeltilmez.
261
+ - Nokta, ardından tam üç rakam geliyorsa binlik ayırıcıdır (`3.500` → üç bin beş yüz);
262
+ aksi halde İngilizce usulü ondalık sayılır (`3.5` → üç virgül beş). Üç ve daha fazla
263
+ noktalı grupta üç rakamlı olmayan grup varsa sürüm numarasıdır (`1.2.3` → bir nokta iki
264
+ nokta üç). Noktalı saat yalnız "saat" sözcüğünden sonra tanınır (`saat 14.30`).
265
+ - Sıra sayısı ve Roma rakamı için ardından bir kelime gelmesi gerekir; cümle
266
+ sonundaki `XIV.` dokunulmaz.
267
+ - Cümle başındaki büyük harf, dönüştürülen kelimeyle birlikte kaybolur
268
+ (`XX. yüzyıl` → yirminci yüzyıl).
269
+ - Büyük harfli kısaltma tespiti sezgiseldir: listede olmayan 2–5 harfli büyük
270
+ harfli sıradan kelimeler harf harf okunabilir; tamamı büyük harfle yazılmış
271
+ metinlerde `kisaltma_harf_harf=False` kullanın. Tek harfli Roma rakamı bir
272
+ ismin baş harfiyle karışabilir (`romanı V. Hugo yazdı`).
273
+ - Tam sayılı ekli kesir (`2 1/2'si`) düzeltilmez.
274
+ - Şemasız alan adlarında Türkçe karakter (IDN) desteklenmez.
275
+ - İngilizce normalizasyon kapsam dışıdır; metin Türkçe kabul edilir.
276
+
277
+ ## Testler
278
+
279
+ ```bash
280
+ python -m unittest discover -s tests
281
+ ```
282
+
283
+ `tests/test_parite.py` kaynak TypeScript projesindeki testlerin birebir
284
+ karşılığıdır (beklenen çıktılar aynı); `tests/test_yeni.py` bu pakette
285
+ eklenen özellikleri ve yanlış pozitifleri sabitler.
286
+
287
+ ## Lisans
288
+
289
+ MIT — bkz. [LICENSE](LICENSE).
290
+
291
+ ---
292
+
293
+ ## English summary
294
+
295
+ **turkce-normalize** is a dependency-free Python (3.9+) text normalizer that
296
+ prepares Turkish text for TTS engines without SSML support (Chatterbox, XTTS,
297
+ Piper, StyleTTS2…). It spells out numbers, ordinals, decimals, money, percent,
298
+ dates, times, phone numbers, IBANs, abbreviations and units; Roman numerals
299
+ (`XX. yüzyıl` → "yirminci yüzyıl"), fractions with correct vowel harmony
300
+ (`3/4` → "dörtte üç"), numeric ranges, negative numbers, all-caps acronyms read
301
+ letter by letter with Turkish letter names (`TBMM'de` → "te be me mede", with a
302
+ configurable list of acronyms read as words such as NATO, TÜBİTAK), e-mail
303
+ addresses and URLs, common symbols, and GIS notation (coordinates, map scale,
304
+ sheet codes, EPSG/UTM). It also ships an abbreviation-aware streaming sentence
305
+ splitter for LLM output and an inline pause-marker parser (`[dur:0.8]`).
306
+
307
+ ```python
308
+ from turkce_normalize import normalize
309
+ normalize("Toplantı 14:30'da, %18 KDV dahil 1.250,50 ₺.")
310
+ # 'Toplantı on dört otuzda, yüzde on sekiz katma değer vergisi dahil bin iki yüz elli lira elli kuruş.'
311
+ ```
312
+
313
+ CLI: `python -m turkce_normalize "metin"`. Tests: `python -m unittest discover -s tests`. License: MIT.