Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -262,6 +262,67 @@ PHONEME_TO_ENGLISH = {
|
|
262 |
'ˌ': '', # secondary stress (remove)
|
263 |
}
|
264 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
265 |
def clean_word_for_phonemes(word: str) -> str:
|
266 |
"""
|
267 |
Clean word by removing punctuation and extra spaces for phoneme processing.
|
@@ -776,22 +837,33 @@ def create_character_level_feedback(word: str, expected_normalized: str,
|
|
776 |
word_letters = phoneme_mapping[i] # Keep natural case
|
777 |
# CRITICAL: Skip empty mappings (used for extra phonemes in diphthongs)
|
778 |
if word_letters: # Only add non-empty letters
|
779 |
-
# Get the English equivalent for the expected phoneme
|
780 |
expected_english = ""
|
|
|
781 |
if expected_char in PHONEME_TO_ENGLISH:
|
782 |
expected_english = PHONEME_TO_ENGLISH[expected_char]
|
|
|
783 |
|
784 |
-
# Get the English equivalent for what was detected
|
785 |
detected_english = ""
|
|
|
786 |
if detected_char in PHONEME_TO_ENGLISH:
|
787 |
detected_english = PHONEME_TO_ENGLISH[detected_char]
|
|
|
788 |
elif detected_char == ' ':
|
789 |
detected_english = "silence"
|
|
|
790 |
else:
|
791 |
detected_english = "?"
|
|
|
792 |
|
793 |
-
# Create tooltip text
|
794 |
-
|
|
|
|
|
|
|
|
|
|
|
795 |
|
796 |
# Create span with inline tooltip for each mispronounced letter/group
|
797 |
formatted_letters = f'<span class="phoneme-error" data-expected="{expected_english}" data-detected="{detected_english}" title="{tooltip_text}"><strong><u>{word_letters}</u></strong></span>'
|
@@ -802,8 +874,16 @@ def create_character_level_feedback(word: str, expected_normalized: str,
|
|
802 |
else:
|
803 |
# Fallback for cases without mapping
|
804 |
expected_english = PHONEME_TO_ENGLISH.get(expected_char, "?")
|
|
|
805 |
detected_english = PHONEME_TO_ENGLISH.get(detected_char, "?")
|
806 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
807 |
|
808 |
formatted_char = f'<span class="phoneme-error" data-expected="{expected_english}" data-detected="{detected_english}" title="{tooltip_text}"><strong><u>{expected_char}</u></strong></span>'
|
809 |
test_result.append(formatted_char)
|
|
|
262 |
'ˌ': '', # secondary stress (remove)
|
263 |
}
|
264 |
|
265 |
+
# Phoneme example words - showing the sound in context
|
266 |
+
PHONEME_EXAMPLES = {
|
267 |
+
# Vowels (monophthongs)
|
268 |
+
'ɪ': 'bit', # IH sound
|
269 |
+
'ɛ': 'bed', # EH sound
|
270 |
+
'æ': 'cat', # AE sound
|
271 |
+
'ʌ': 'but', # UH sound (stressed)
|
272 |
+
'ɑ': 'father', # AH sound
|
273 |
+
'ɔ': 'law', # AW sound
|
274 |
+
'ʊ': 'book', # UU sound
|
275 |
+
'u': 'boot', # OO sound
|
276 |
+
'i': 'beat', # EE sound
|
277 |
+
'ə': 'about', # schwa (unstressed)
|
278 |
+
'ɝ': 'bird', # ER sound (stressed)
|
279 |
+
'ɚ': 'letter', # ER sound (unstressed)
|
280 |
+
|
281 |
+
# Diphthongs
|
282 |
+
'eɪ': 'day', # AY sound
|
283 |
+
'aɪ': 'my', # EYE sound
|
284 |
+
'ɔɪ': 'boy', # OY sound
|
285 |
+
'aʊ': 'now', # OW sound
|
286 |
+
'oʊ': 'go', # OH sound
|
287 |
+
|
288 |
+
# R-colored vowels
|
289 |
+
'ɪr': 'near', # EER sound
|
290 |
+
'ɛr': 'care', # AIR sound
|
291 |
+
'ɑr': 'car', # AR sound
|
292 |
+
'ɔr': 'for', # OR sound
|
293 |
+
'ʊr': 'tour', # OOR sound
|
294 |
+
'ər': 'letter', # ER sound
|
295 |
+
|
296 |
+
# Consonants
|
297 |
+
'p': 'pat', # P sound
|
298 |
+
'b': 'bat', # B sound
|
299 |
+
't': 'tap', # T sound
|
300 |
+
'd': 'dap', # D sound
|
301 |
+
'k': 'cat', # K sound
|
302 |
+
'g': 'gap', # G sound (ASCII)
|
303 |
+
'ɡ': 'gap', # G sound (IPA)
|
304 |
+
'f': 'fat', # F sound
|
305 |
+
'v': 'vat', # V sound
|
306 |
+
'θ': 'think', # TH sound (voiceless)
|
307 |
+
'ð': 'this', # TH sound (voiced)
|
308 |
+
's': 'sap', # S sound
|
309 |
+
'z': 'zap', # Z sound
|
310 |
+
'ʃ': 'ship', # SH sound
|
311 |
+
'ʒ': 'measure', # ZH sound
|
312 |
+
'h': 'hat', # H sound
|
313 |
+
'm': 'mat', # M sound
|
314 |
+
'n': 'nat', # N sound
|
315 |
+
'ŋ': 'sing', # NG sound
|
316 |
+
'l': 'lap', # L sound
|
317 |
+
'r': 'rap', # R sound
|
318 |
+
'j': 'yes', # Y sound
|
319 |
+
'w': 'wet', # W sound
|
320 |
+
|
321 |
+
# Affricates
|
322 |
+
'tʃ': 'chip', # CH sound
|
323 |
+
'dʒ': 'jump', # J sound
|
324 |
+
}
|
325 |
+
|
326 |
def clean_word_for_phonemes(word: str) -> str:
|
327 |
"""
|
328 |
Clean word by removing punctuation and extra spaces for phoneme processing.
|
|
|
837 |
word_letters = phoneme_mapping[i] # Keep natural case
|
838 |
# CRITICAL: Skip empty mappings (used for extra phonemes in diphthongs)
|
839 |
if word_letters: # Only add non-empty letters
|
840 |
+
# Get the English equivalent and example word for the expected phoneme
|
841 |
expected_english = ""
|
842 |
+
expected_example = ""
|
843 |
if expected_char in PHONEME_TO_ENGLISH:
|
844 |
expected_english = PHONEME_TO_ENGLISH[expected_char]
|
845 |
+
expected_example = PHONEME_EXAMPLES.get(expected_char, word_letters)
|
846 |
|
847 |
+
# Get the English equivalent and example for what was detected
|
848 |
detected_english = ""
|
849 |
+
detected_example = ""
|
850 |
if detected_char in PHONEME_TO_ENGLISH:
|
851 |
detected_english = PHONEME_TO_ENGLISH[detected_char]
|
852 |
+
detected_example = PHONEME_EXAMPLES.get(detected_char, "")
|
853 |
elif detected_char == ' ':
|
854 |
detected_english = "silence"
|
855 |
+
detected_example = ""
|
856 |
else:
|
857 |
detected_english = "?"
|
858 |
+
detected_example = ""
|
859 |
|
860 |
+
# Create tooltip text with example words
|
861 |
+
if expected_example and detected_example:
|
862 |
+
tooltip_text = f"Expected '{expected_english}' as in '{expected_example}', You said '{detected_english}' as in '{detected_example}'"
|
863 |
+
elif expected_example:
|
864 |
+
tooltip_text = f"Expected '{expected_english}' as in '{expected_example}', You said '{detected_english}'"
|
865 |
+
else:
|
866 |
+
tooltip_text = f"Expected '{expected_english}', You said '{detected_english}'"
|
867 |
|
868 |
# Create span with inline tooltip for each mispronounced letter/group
|
869 |
formatted_letters = f'<span class="phoneme-error" data-expected="{expected_english}" data-detected="{detected_english}" title="{tooltip_text}"><strong><u>{word_letters}</u></strong></span>'
|
|
|
874 |
else:
|
875 |
# Fallback for cases without mapping
|
876 |
expected_english = PHONEME_TO_ENGLISH.get(expected_char, "?")
|
877 |
+
expected_example = PHONEME_EXAMPLES.get(expected_char, "")
|
878 |
detected_english = PHONEME_TO_ENGLISH.get(detected_char, "?")
|
879 |
+
detected_example = PHONEME_EXAMPLES.get(detected_char, "")
|
880 |
+
|
881 |
+
if expected_example and detected_example:
|
882 |
+
tooltip_text = f"Expected '{expected_english}' as in '{expected_example}', You said '{detected_english}' as in '{detected_example}'"
|
883 |
+
elif expected_example:
|
884 |
+
tooltip_text = f"Expected '{expected_english}' as in '{expected_example}', You said '{detected_english}'"
|
885 |
+
else:
|
886 |
+
tooltip_text = f"Expected '{expected_english}', You said '{detected_english}'"
|
887 |
|
888 |
formatted_char = f'<span class="phoneme-error" data-expected="{expected_english}" data-detected="{detected_english}" title="{tooltip_text}"><strong><u>{expected_char}</u></strong></span>'
|
889 |
test_result.append(formatted_char)
|