aboutsummaryrefslogtreecommitdiff
path: root/app/converter/clients/languages.py
blob: 092595387a97eb267bf6710f9235603ce9e5ebc0 (plain)
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
"""Language tables shared by the TTS clients and their UIs."""

from typing import Optional

# Languages the Qwen3-TTS demo accepts as display names (its API silently
# falls back to "Auto" for anything else, so unknown names are rejected
# before a run starts instead of mispronouncing a whole book). Arabic,
# Hindi and Vietnamese come from the audio.cpp WebUI language menus
# (MagpieTTS / IndexTTS2); on Qwen they degrade to Auto.
TTS_LANGUAGES = (
    "Auto", "Chinese", "English", "German", "Italian", "Portuguese",
    "Spanish", "Japanese", "Korean", "French", "Russian", "Arabic",
    "Hindi", "Vietnamese",
)

# Short aliases accepted on the command line (ISO 639-1 codes and common
# shorthands), mapped to the display names above. The Arabic regional
# variants collapse to plain Arabic.
TTS_LANGUAGE_ALIASES = {
    "zh": "Chinese",
    "en": "English",
    "de": "German",
    "it": "Italian",
    "pt": "Portuguese",
    "es": "Spanish",
    "ja": "Japanese",
    "ko": "Korean",
    "fr": "French",
    "ru": "Russian",
    "ar": "Arabic",
    "hi": "Hindi",
    "vi": "Vietnamese",
    "zh-cn": "Chinese",
    "zh-tw": "Chinese",
    "pt-br": "Portuguese",
    "en-us": "English",
    "en-gb": "English",
    "ar-ae": "Arabic",
    "ar-msa": "Arabic",
    "ar-sa": "Arabic",
}

# Qwen display names -> ISO 639-1 codes, for audio.cpp families whose
# language request option takes a code instead of a display name. "Auto"
# has no code and maps to None so the field is omitted and the server
# applies its own default.
LANGUAGE_ISO_CODES = {
    "Chinese": "zh",
    "English": "en",
    "German": "de",
    "Italian": "it",
    "Portuguese": "pt",
    "Spanish": "es",
    "Japanese": "ja",
    "Korean": "ko",
    "French": "fr",
    "Russian": "ru",
    "Arabic": "ar",
    "Hindi": "hi",
    "Vietnamese": "vi",
}

# Static selection-list order for the TUI Language menus (Settings and
# Generate Audiobooks): the languages of audio.cpp's WebUI language menus
# (MagpieTTS + IndexTTS2) plus the shared display names, with common
# languages near the top. A static list — new audio.cpp menu languages
# need this tuple updated by hand.
LANGUAGE_CHOICES = (
    "English", "Spanish", "Chinese", "French", "German", "Italian",
    "Portuguese", "Japanese", "Korean", "Russian", "Arabic", "Hindi",
    "Vietnamese", "Auto",
)


def normalize_language(value: Optional[str]) -> str:
    """Normalize a user-provided language name to a Qwen3-TTS display name.

    Accepts the display names in TTS_LANGUAGES case-insensitively as
    well as the short aliases in TTS_LANGUAGE_ALIASES (ISO 639-1 codes
    and common shorthands). Raises ValueError for anything else, since the
    Qwen3-TTS demo silently falls back to "Auto" for unrecognized languages.
    """
    if value is None:
        raise ValueError("Language must not be None")
    candidate = value.strip()
    if not candidate:
        raise ValueError("Language must not be empty")
    for name in TTS_LANGUAGES:
        if candidate.lower() == name.lower():
            return name
    alias = TTS_LANGUAGE_ALIASES.get(candidate.lower())
    if alias:
        return alias
    raise ValueError(
        f"Unknown language: {value!r}. Expected one of "
        f"{', '.join(TTS_LANGUAGES)} (or an alias: "
        f"{', '.join(sorted(TTS_LANGUAGE_ALIASES))})."
    )