Spaces:
Building
Building
File size: 8,832 Bytes
81e4201 a4ca872 81e4201 a4ca872 81e4201 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 17a90d6 c461a97 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
"""
TTS Text Preprocessing Utilities with Multilingual Support
"""
import re
import json
from typing import Dict, Set, Optional
from num2words import num2words
from pathlib import Path
from locale_manager import LocaleManager
class TTSPreprocessor:
"""Text preprocessor for TTS providers with multilingual support"""
# Preprocessing flags
PREPROCESS_NUMBERS = "numbers"
PREPROCESS_CURRENCY = "currency"
PREPROCESS_TIME = "time"
PREPROCESS_DATE = "date"
PREPROCESS_CODES = "codes"
PREPROCESS_PERCENTAGE = "percentage"
def __init__(self, language: str = "tr"):
self.language = language
self.locale_data = LocaleManager.get_locale(language)
def preprocess(self, text: str, flags: Set[str]) -> str:
"""Apply preprocessing based on flags"""
if self.PREPROCESS_CURRENCY in flags:
text = self._process_currency(text)
if self.PREPROCESS_TIME in flags:
text = self._process_time(text)
if self.PREPROCESS_DATE in flags:
text = self._process_date(text)
if self.PREPROCESS_CODES in flags:
text = self._process_codes(text)
if self.PREPROCESS_PERCENTAGE in flags:
text = self._process_percentage(text)
# Numbers should be processed last to avoid conflicts
if self.PREPROCESS_NUMBERS in flags:
text = self._process_numbers(text)
return text
def _process_numbers(self, text: str) -> str:
"""Convert numbers to words based on locale"""
decimal_sep = self.locale_data["numbers"]["decimal_separator"]
thousands_sep = self.locale_data["numbers"]["thousands_separator"]
decimal_word = self.locale_data["numbers"]["decimal_word"]
threshold = self.locale_data.get("small_number_threshold", 100)
def replace_number(match):
num_str = match.group()
# Normalize number format
if self.language == "tr":
# Turkish: 1.234,56 -> 1234.56
num_str = num_str.replace('.', '').replace(',', '.')
else:
# English: 1,234.56 -> 1234.56
num_str = num_str.replace(',', '')
try:
num = float(num_str)
if num.is_integer():
num = int(num)
# Keep small numbers as is based on threshold
if isinstance(num, int) and 0 <= num <= threshold:
return str(num)
# Convert large numbers to words
if isinstance(num, int):
try:
return num2words(num, lang=self.language)
except NotImplementedError:
# Fallback to English if language not supported
return num2words(num, lang='en')
else:
# Handle decimal
integer_part = int(num)
decimal_part = int((num - integer_part) * 100)
try:
int_words = num2words(integer_part, lang=self.language)
dec_words = num2words(decimal_part, lang=self.language)
return f"{int_words} {decimal_word} {dec_words}"
except NotImplementedError:
# Fallback
int_words = num2words(integer_part, lang='en')
dec_words = num2words(decimal_part, lang='en')
return f"{int_words} {decimal_word} {dec_words}"
except:
return num_str
# Match numbers with locale-specific format
if self.language == "tr":
pattern = r'\b\d{1,3}(?:\.\d{3})*(?:,\d+)?\b|\b\d+(?:,\d+)?\b'
else:
pattern = r'\b\d{1,3}(?:,\d{3})*(?:\.\d+)?\b|\b\d+(?:\.\d+)?\b'
return re.sub(pattern, replace_number, text)
def _process_codes(self, text: str) -> str:
"""Process codes like PNR, flight numbers - language agnostic"""
def spell_code(match):
code = match.group()
return ' '.join(code)
# Match uppercase letters followed by numbers
pattern = r'\b[A-Z]{2,5}\d{2,5}\b'
return re.sub(pattern, spell_code, text)
def _process_currency(self, text: str) -> str:
"""Process currency symbols and amounts based on locale"""
currency_data = self.locale_data.get("currency", {})
if not isinstance(currency_data, dict):
return text
symbol = currency_data.get("symbol", "")
word = currency_data.get("word", "")
code = currency_data.get("code", "")
position = currency_data.get("position", "before")
if symbol and word:
# Replace standalone symbols
text = text.replace(symbol, f" {word} ")
# Replace symbol with amount
if position == "before":
# $100 -> 100 dollar
pattern = rf'{re.escape(symbol)}\s*(\d+(?:[.,]\d+)?)'
text = re.sub(pattern, rf'\1 {word}', text)
else:
# 100₺ -> 100 lira
pattern = rf'(\d+(?:[.,]\d+)?)\s*{re.escape(symbol)}'
text = re.sub(pattern, rf'\1 {word}', text)
# Process currency codes
if code and word:
pattern = rf'(\d+(?:[.,]\d+)?)\s*{code}\b'
text = re.sub(pattern, rf'\1 {word}', text, flags=re.IGNORECASE)
return text
def _process_percentage(self, text: str) -> str:
"""Process percentage symbols based on locale"""
percentage = self.locale_data.get("percentage", {})
if not isinstance(percentage, dict):
return text
word = percentage.get("word", "percent")
position = percentage.get("position", "after")
if position == "before":
# %50 -> yüzde 50
pattern = r'%\s*(\d+(?:[.,]\d+)?)'
replacement = rf'{word} \1'
else:
# 50% -> 50 percent
pattern = r'(\d+(?:[.,]\d+)?)\s*%'
replacement = rf'\1 {word}'
return re.sub(pattern, replacement, text)
def _process_date(self, text: str) -> str:
"""Process date formats based on locale"""
months = self.locale_data.get("months", {})
date_format = self.locale_data.get("date_format", "YYYY-MM-DD")
if not isinstance(months, dict):
return text
# Convert ISO format dates
def replace_date(match):
year, month, day = match.groups()
month_name = months.get(month, month)
# Format based on locale preference
if "DD.MM.YYYY" in date_format:
# Turkish format with month name
return f"{int(day)} {month_name} {year}"
elif "MM/DD/YYYY" in date_format:
# US format with month name
return f"{month_name} {int(day)}, {year}"
else:
return match.group()
pattern = r'(\d{4})-(\d{2})-(\d{2})'
return re.sub(pattern, replace_date, text)
def _process_time(self, text: str) -> str:
"""Process time formats based on locale"""
time_data = self.locale_data.get("time", {})
if not isinstance(time_data, dict):
time_format = "word"
separator = " "
else:
time_format = time_data.get("format", "word")
separator = time_data.get("separator", " ")
def replace_time(match):
hour, minute = match.groups()
hour_int = int(hour)
minute_int = int(minute)
if time_format == "word":
try:
hour_word = num2words(hour_int, lang=self.language)
minute_word = num2words(minute_int, lang=self.language) if minute_int > 0 else ""
if minute_int == 0:
return hour_word
else:
return f"{hour_word}{separator}{minute_word}"
except NotImplementedError:
return f"{hour} {minute}"
else:
return f"{hour} {minute}"
pattern = r'(\d{1,2}):(\d{2})'
return re.sub(pattern, replace_time, text) |