import streamlit as st import pyphen import re from typing import List, Tuple import string import os # Initialize pyphen for syllable splitting dic = pyphen.Pyphen(lang='en') # Configure Streamlit page st.set_page_config( page_title="Text Pronunciation Analyzer", page_icon="๐ฃ๏ธ", layout="wide", initial_sidebar_state="collapsed" ) # Custom CSS for styling st.markdown(""" """, unsafe_allow_html=True) class PronunciationAnalyzer: def __init__(self): self.dic = pyphen.Pyphen(lang='en') self.color_classes = [ 'color-1', 'color-2', 'color-3', 'color-4', 'color-5', 'color-6', 'color-7', 'color-8' ] def get_syllables(self, word: str) -> List[str]: """Get syllables for a word using pyphen""" # Remove punctuation and convert to lowercase clean_word = word.lower().strip(string.punctuation) if not clean_word: return [word] # Use pyphen to split into syllables syllables = self.dic.inserted(clean_word).split('-') # If pyphen couldn't split (returns original word), try basic vowel-based splitting if len(syllables) == 1 and len(clean_word) > 3: syllables = self._basic_syllable_split(clean_word) return syllables if syllables else [word] def _basic_syllable_split(self, word: str) -> List[str]: """Basic vowel-based syllable splitting as fallback""" vowels = 'aeiouy' syllables = [] current_syllable = '' for i, char in enumerate(word): current_syllable += char # Look ahead for vowel patterns if i < len(word) - 1: if char in vowels and word[i + 1] not in vowels: # Vowel followed by consonant - potential syllable break if len(current_syllable) >= 2: syllables.append(current_syllable) current_syllable = '' if current_syllable: syllables.append(current_syllable) return syllables if syllables else [word] def tokenize_text(self, text: str) -> List[str]: """Tokenize text into words while preserving punctuation""" # Enhanced regex-based tokenization # This pattern matches: # - Words (including contractions like "don't") # - Numbers # - Punctuation marks # - Preserves spacing # Split text into tokens while preserving structure pattern = r"(?:\w+(?:'\w+)?|\d+|[^\w\s])" tokens = re.findall(pattern, text) # Add spaces back where needed result = [] text_pos = 0 for token in tokens: # Find the token's position in the original text token_pos = text.find(token, text_pos) # Add any whitespace before the token if token_pos > text_pos: whitespace = text[text_pos:token_pos] if whitespace.strip() == '': # Only add if it's pure whitespace result.extend(list(whitespace)) result.append(token) text_pos = token_pos + len(token) # Filter out empty strings and normalize return [token for token in result if token.strip()] def analyze_text(self, text: str) -> Tuple[List[Tuple[str, List[str]]], dict]: """Analyze text and return word-syllable pairs and statistics""" if not text.strip(): return [], {} # Tokenize the text words = self.tokenize_text(text) # Filter out pure punctuation tokens for analysis content_words = [word for word in words if any(c.isalnum() for c in word)] # Get syllables for each word word_syllables = [] total_syllables = 0 for word in words: if any(c.isalnum() for c in word): # Only analyze words with alphanumeric characters syllables = self.get_syllables(word) word_syllables.append((word, syllables)) total_syllables += len(syllables) else: word_syllables.append((word, [word])) # Keep punctuation as-is # Calculate statistics stats = { 'total_words': len(content_words), 'total_syllables': total_syllables, 'avg_syllables': round(total_syllables / len(content_words), 2) if content_words else 0, 'longest_word': max(content_words, key=len) if content_words else '', 'most_syllables': max(content_words, key=lambda w: len(self.get_syllables(w))) if content_words else '' } return word_syllables, stats def render_highlighted_text(word_syllables: List[Tuple[str, List[str]]], analyzer: PronunciationAnalyzer): """Render original text with word highlighting""" html_parts = [] word_index = 0 for word, syllables in word_syllables: if any(c.isalnum() for c in word): color_class = analyzer.color_classes[word_index % len(analyzer.color_classes)] html_parts.append(f'{word}') word_index += 1 else: html_parts.append(word) # Add space after word (except for punctuation that shouldn't have spaces) if word not in '.,!?;:': html_parts.append(' ') return ''.join(html_parts) def render_pronunciation(word_syllables: List[Tuple[str, List[str]]], analyzer: PronunciationAnalyzer): """Render pronunciation with syllable breakdown""" html_parts = [] word_index = 0 for word, syllables in word_syllables: if any(c.isalnum() for c in word): color_class = analyzer.color_classes[word_index % len(analyzer.color_classes)] # Join syllables with dots syllable_text = 'ยท'.join(syllables) html_parts.append(f'{syllable_text}') word_index += 1 else: html_parts.append(f'{word}') # Add space after word (except for punctuation that shouldn't have spaces) if word not in '.,!?;:': html_parts.append(' ') return ''.join(html_parts) def main(): # Initialize analyzer analyzer = PronunciationAnalyzer() # Header st.markdown('
Enter any text below to see its pronunciation breakdown with advanced syllable detection
', unsafe_allow_html=True) # Input section st.markdown("### ๐ Enter Your Text") # Sample texts sample_texts = [ "Hello world", "Pronunciation analyzer", "Supercalifragilisticexpialidocious", "Linguistics and phonetics", "The quick brown fox jumps over the lazy dog" ] # Sample buttons st.markdown("**Try these samples:**") cols = st.columns(len(sample_texts)) for i, sample in enumerate(sample_texts): if cols[i].button(sample, key=f"sample_{i}"): st.session_state.input_text = sample # Text input text_input = st.text_area( "Text to analyze:", value=st.session_state.get('input_text', ''), height=120, placeholder="Type or paste your text here...", key="text_input" ) # Update session state if text_input: st.session_state.input_text = text_input # Analyze button col1, col2, col3 = st.columns([1, 1, 1]) with col2: analyze_button = st.button("๐ Analyze Text", type="primary", use_container_width=True) # Clear button if st.button("๐๏ธ Clear"): st.session_state.input_text = "" st.rerun() # Analysis results if analyze_button and text_input.strip(): with st.spinner("Analyzing text..."): word_syllables, stats = analyzer.analyze_text(text_input) if word_syllables: st.markdown("---") st.markdown("## ๐ Analysis Results") # Statistics st.markdown("### ๐ Text Statistics") col1, col2, col3, col4 = st.columns(4) with col1: st.markdown(f"""