fix(pyqt): migrate GUI to Language enums, fix subtitle mode gating

Complete the Language enum unification (commit 0dc491e) for the PyQt
GUI, which was left resolving languages from kokoro letter codes
(voice[0]) while domain and WebUI already used Language enums.

- selected_lang is now always a Language enum: voices resolve via
  language_for_voice_id(), profiles via resolve_profile_language()
- kokoro letter->Language mapping stays in the engine (new public
  language_for_code()); the Language.from_code() domain shim is removed
- legacy profile files with letter codes ("a", "e", ...) are tolerated
  only at the profile read boundary; new profiles save ISO codes
- conversion.py letter comparisons replaced with enum comparisons, and
  SynthParams lang_code= -> language= (would TypeError at runtime)
- subtitle dropdown enabled for all languages; word-count and
  highlighting modes restricted to English with auto-switch to Sentence
- tests: engine mapping + profile language resolution added; 1566 pass
This commit is contained in:
Deniz Şafak
2026-08-20 21:28:30 +03:00
parent f340b976db
commit 11274ad6bf
13 changed files with 183 additions and 68 deletions
+15 -5
View File
@@ -73,17 +73,27 @@ def engine_language(lang: Language) -> str:
return _KOKORO_LANG_MAP.get(lang, "a")
def language_for_voice_id(voice_id: str) -> Language:
def language_for_code(code: str | None) -> Language:
"""Map a kokoro engine language code (single letter) to a Language enum.
Used to resolve legacy data such as old profile files that stored
kokoro letter codes. This is kokoro-specific knowledge that stays
inside the engine. Unparseable values fall back to EN_US.
"""
letter = str(code or "").strip()[:1].lower()
if letter in _CODE_TO_LANGUAGE:
return _CODE_TO_LANGUAGE[letter]
return Language.EN_US
def language_for_voice_id(voice_id: str | None) -> Language:
"""Determine which Language a voice belongs to from its voice ID.
Kokoro voice IDs encode language as a prefix (e.g. "af_heart""a" → EN_US).
This is kokoro-specific knowledge that stays inside the engine.
Callers pass a voice ID string; the engine returns a Language enum.
"""
prefix = str(voice_id or "").strip()[:1].lower()
if prefix in _CODE_TO_LANGUAGE:
return _CODE_TO_LANGUAGE[prefix]
return Language.EN_US
return language_for_code(voice_id)
class KokoroSession: