refactor: Language Enum with ISO codes

- Language enum: en-US, en-GB, es, fr, hi, it, ja, pt-BR, zh
- Engine-specific mappings (kokoro → single-letter) live in pipeline_factory and synthesize
- spacy_utils uses Language enum keys for model mapping
- split_pattern uses Language enum properties (is_cjk)
- Updated all tests to use ISO codes
This commit is contained in:
Artem Akymenko
2026-07-22 10:54:39 +00:00
parent 4aef73ff85
commit 0805e9fdae
11 changed files with 206 additions and 50 deletions
+14 -4
View File
@@ -41,16 +41,26 @@ class TestCreatePipelineForJob:
def test_kokoro_provider(self, _dev, _reg, mock_create):
mock_create.return_value = MagicMock()
result = create_pipeline_for_job("kokoro", "en", use_gpu=False)
mock_create.assert_called_once_with("kokoro", lang_code="en", device="cpu")
# "en" → fallback to EN_US → kokoro code "a"
mock_create.assert_called_once_with("kokoro", lang_code="a", device="cpu")
assert result is mock_create.return_value
@patch("abogen.domain.pipeline_factory.create_pipeline")
@patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=True)
@patch("abogen.domain.pipeline_factory.resolve_device", return_value="cpu")
def test_kokoro_provider_iso_code(self, _dev, _reg, mock_create):
mock_create.return_value = MagicMock()
result = create_pipeline_for_job("kokoro", "en-GB", use_gpu=False)
# "en-GB" → EN_GB → kokoro code "b"
mock_create.assert_called_once_with("kokoro", lang_code="b", device="cpu")
@patch("abogen.domain.pipeline_factory.create_pipeline")
@patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=False)
@patch("abogen.domain.pipeline_factory.resolve_device", return_value="cpu")
def test_unknown_provider_falls_back_to_kokoro(self, _dev, _reg, mock_create):
mock_create.return_value = MagicMock()
result = create_pipeline_for_job("unknown_provider", "en", use_gpu=False)
mock_create.assert_called_once_with("kokoro", lang_code="en", device="cpu")
mock_create.assert_called_once_with("kokoro", lang_code="a", device="cpu")
@patch("abogen.domain.pipeline_factory.create_pipeline")
@patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=True)
@@ -58,7 +68,7 @@ class TestCreatePipelineForJob:
def test_empty_provider_defaults_to_kokoro(self, _dev, _reg, mock_create):
mock_create.return_value = MagicMock()
result = create_pipeline_for_job("", "en", use_gpu=False)
mock_create.assert_called_once_with("kokoro", lang_code="en", device="cpu")
mock_create.assert_called_once_with("kokoro", lang_code="a", device="cpu")
@patch("abogen.domain.pipeline_factory.create_pipeline")
@patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=True)
@@ -66,7 +76,7 @@ class TestCreatePipelineForJob:
def test_none_provider_defaults_to_kokoro(self, _dev, _reg, mock_create):
mock_create.return_value = MagicMock()
result = create_pipeline_for_job(None, "en", use_gpu=False)
mock_create.assert_called_once_with("kokoro", lang_code="en", device="cpu")
mock_create.assert_called_once_with("kokoro", lang_code="a", device="cpu")
class TestDisposePipelines:
+32
View File
@@ -5,6 +5,7 @@ from pathlib import Path
from abogen.domain.enums import (
InputFormat,
Language,
OutputFormat,
SaveMode,
SubtitleFormat,
@@ -97,3 +98,34 @@ class TestInputFormat:
def test_dot_ext(self):
assert InputFormat.EPUB.dot_ext == ".epub"
assert InputFormat.SRT.dot_ext == ".srt"
class TestLanguage:
def test_iso_codes(self):
assert Language.EN_US == "en-US"
assert Language.EN_GB == "en-GB"
assert Language.ZH == "zh"
assert Language.JA == "ja"
def test_display_name(self):
assert Language.EN_US.display_name == "American English"
assert Language.JA.display_name == "Japanese"
def test_is_cjk(self):
assert Language.ZH.is_cjk is True
assert Language.JA.is_cjk is True
assert Language.EN_US.is_cjk is False
def test_supports_subtitle_tokens(self):
assert Language.EN_US.supports_subtitle_tokens is True
assert Language.EN_GB.supports_subtitle_tokens is True
assert Language.ZH.supports_subtitle_tokens is False
def test_from_str_case_insensitive(self):
assert Language.from_str("EN-US") == Language.EN_US
assert Language.from_str("en-gb") == Language.EN_GB
assert Language.from_str("ZH") == Language.ZH
def test_from_str_invalid(self):
with pytest.raises(ValueError, match="Invalid Language"):
Language.from_str("en")
+20 -20
View File
@@ -12,49 +12,49 @@ from abogen.domain.split_pattern import get_split_pattern
class TestEnglish:
def test_english_sentence(self):
assert get_split_pattern("a", "Sentence") == "\n"
assert get_split_pattern("en-US", "Sentence") == "\n"
def test_english_sentence_comma(self):
assert get_split_pattern("a", "Sentence + Comma") == "\n"
assert get_split_pattern("en-US", "Sentence + Comma") == "\n"
def test_english_line(self):
assert get_split_pattern("a", "Line") == "\n"
assert get_split_pattern("en-US", "Line") == "\n"
def test_english_disabled(self):
assert get_split_pattern("a", "Disabled") == "\n"
assert get_split_pattern("en-US", "Disabled") == "\n"
def test_english_b(self):
assert get_split_pattern("b", "Sentence") == "\n"
def test_english_gb(self):
assert get_split_pattern("en-GB", "Sentence") == "\n"
# --- CJK languages ---
class TestCJK:
def test_chinese_disabled(self):
pattern = get_split_pattern("z", "Disabled")
pattern = get_split_pattern("zh", "Disabled")
assert pattern != "\n"
assert r"\n+" in pattern
def test_chinese_line(self):
pattern = get_split_pattern("z", "Line")
pattern = get_split_pattern("zh", "Line")
assert pattern != "\n"
assert r"\n+" in pattern
def test_chinese_sentence(self):
pattern = get_split_pattern("z", "Sentence")
pattern = get_split_pattern("zh", "Sentence")
assert r"\n+" in pattern
def test_chinese_sentence_comma(self):
pattern = get_split_pattern("z", "Sentence + Comma")
pattern = get_split_pattern("zh", "Sentence + Comma")
assert r"\n+" in pattern
def test_japanese_disabled(self):
pattern = get_split_pattern("j", "Disabled")
pattern = get_split_pattern("ja", "Disabled")
assert pattern != "\n"
assert r"\n+" in pattern
def test_japanese_sentence(self):
pattern = get_split_pattern("j", "Sentence")
pattern = get_split_pattern("ja", "Sentence")
assert r"\n+" in pattern
@@ -62,18 +62,18 @@ class TestCJK:
class TestOtherLanguages:
def test_spanish_sentence(self):
pattern = get_split_pattern("e", "Sentence")
pattern = get_split_pattern("es", "Sentence")
assert r"\n+" in pattern
def test_spanish_line(self):
assert get_split_pattern("e", "Line") == "\n"
assert get_split_pattern("es", "Line") == "\n"
def test_spanish_disabled(self):
# canonical: \n+ for non-CJK Disabled
assert get_split_pattern("e", "Disabled") == r"\n+"
assert get_split_pattern("es", "Disabled") == r"\n+"
def test_french_sentence_comma(self):
pattern = get_split_pattern("f", "Sentence + Comma")
pattern = get_split_pattern("fr", "Sentence + Comma")
assert r"\n+" in pattern
def test_unknown_lang(self):
@@ -85,17 +85,17 @@ class TestOtherLanguages:
class TestPatternStructure:
def test_sentence_has_lookbehind(self):
pattern = get_split_pattern("e", "Sentence")
pattern = get_split_pattern("es", "Sentence")
assert r"(?<=" in pattern
def test_sentence_comma_has_comma_chars(self):
pattern = get_split_pattern("e", "Sentence + Comma")
pattern = get_split_pattern("es", "Sentence + Comma")
assert "," in pattern
def test_cjk_spacing_uses_star(self):
pattern = get_split_pattern("z", "Sentence")
pattern = get_split_pattern("zh", "Sentence")
assert r"\s*" in pattern
def test_non_cjk_spacing_uses_plus(self):
pattern = get_split_pattern("e", "Sentence")
pattern = get_split_pattern("es", "Sentence")
assert r"\s+" in pattern