diff --git a/src/vocalinux/main.py b/src/vocalinux/main.py
index 21aa2b7..4e5a6ff 100644
--- a/src/vocalinux/main.py
+++ b/src/vocalinux/main.py
@@ -8,6 +8,7 @@ import atexit
import logging
import sys
+from .utils.vosk_model_info import SUPPORTED_LANGUAGES
from .version import __version__
# Configure logging
@@ -20,6 +21,9 @@ logger = logging.getLogger(__name__)
# Note: GTK-dependent modules (tray_indicator) are imported lazily after
# dependency checking to provide better error messages for pip/pipx users
+# Keep CLI --language choices in sync with the Settings catalog.
+LANGUAGE_CHOICES = tuple(SUPPORTED_LANGUAGES.keys())
+
def _should_append_trailing_space() -> bool:
"""Return whether completed transcriptions should get a trailing space.
@@ -68,25 +72,10 @@ def parse_arguments():
parser.add_argument(
"--language",
type=str,
- choices=[
- "auto",
- "en-us",
- "en-in",
- "hi",
- "es",
- "fr",
- "de",
- "it",
- "pt",
- "ru",
- "zh",
- "ja",
- "ko",
- "ar",
- ],
+ choices=LANGUAGE_CHOICES,
help=(
- "Speech recognition language (auto for auto-detect, en-us, "
- "hi, es, fr, de, it, pt, ru, zh, etc.)"
+ "Speech recognition language (auto for auto-detect, or a code "
+ "from the Settings language catalog such as en-us, hu, ja, …)"
),
)
parser.add_argument(
diff --git a/src/vocalinux/utils/vosk_model_info.py b/src/vocalinux/utils/vosk_model_info.py
index be2a32b..cd47cf0 100644
--- a/src/vocalinux/utils/vosk_model_info.py
+++ b/src/vocalinux/utils/vosk_model_info.py
@@ -1,5 +1,10 @@
# Language definitions with display names and Whisper/VOSK codes
# Supported languages for speech recognition
+#
+# Whisper / whisper.cpp / remote_api use the catalog key (or the "whisper"
+# code for English-only model checks). VOSK uses per-language model zips from
+# VOSK_MODEL_INFO; set "vosk" to None when no official Alphacephei model exists.
+# Medium/large must cover every language present in small (see issue #550).
SUPPORTED_LANGUAGES = {
"auto": {
"name": "Auto-detect",
@@ -12,15 +17,50 @@ SUPPORTED_LANGUAGES = {
"whisper": "en",
"vosk": "vosk-model-small-en-us-0.15",
},
- "hi": {
- "name": "Hindi",
- "whisper": "hi",
- "vosk": "vosk-model-small-hi-0.22",
+ "en-in": {
+ "name": "English (India)",
+ "whisper": "en",
+ "vosk": "vosk-model-small-en-in-0.4",
},
- "es": {
- "name": "Spanish",
- "whisper": "es",
- "vosk": "vosk-model-small-es-0.42",
+ "ar": {
+ "name": "Arabic",
+ "whisper": "ar",
+ "vosk": "vosk-model-small-ar-0.3",
+ },
+ "bn": {
+ "name": "Bengali",
+ "whisper": "bn",
+ "vosk": None,
+ },
+ "ca": {
+ "name": "Catalan",
+ "whisper": "ca",
+ "vosk": "vosk-model-small-ca-0.4",
+ },
+ "zh": {
+ "name": "Chinese",
+ "whisper": "zh",
+ "vosk": "vosk-model-small-cn-0.22",
+ },
+ "cs": {
+ "name": "Czech",
+ "whisper": "cs",
+ "vosk": "vosk-model-small-cs-0.4-rhasspy",
+ },
+ "da": {
+ "name": "Danish",
+ "whisper": "da",
+ "vosk": None,
+ },
+ "nl": {
+ "name": "Dutch",
+ "whisper": "nl",
+ "vosk": "vosk-model-small-nl-0.22",
+ },
+ "fi": {
+ "name": "Finnish",
+ "whisper": "fi",
+ "vosk": None,
},
"fr": {
"name": "French",
@@ -32,30 +72,117 @@ SUPPORTED_LANGUAGES = {
"whisper": "de",
"vosk": "vosk-model-small-de-0.15",
},
+ "el": {
+ "name": "Greek",
+ "whisper": "el",
+ "vosk": None,
+ },
+ "he": {
+ "name": "Hebrew",
+ "whisper": "he",
+ "vosk": None,
+ },
+ "hi": {
+ "name": "Hindi",
+ "whisper": "hi",
+ "vosk": "vosk-model-small-hi-0.22",
+ },
+ "hu": {
+ "name": "Hungarian",
+ "whisper": "hu",
+ "vosk": None,
+ },
+ "id": {
+ "name": "Indonesian",
+ "whisper": "id",
+ "vosk": None,
+ },
"it": {
"name": "Italian",
"whisper": "it",
"vosk": "vosk-model-small-it-0.22",
},
+ "ja": {
+ "name": "Japanese",
+ "whisper": "ja",
+ "vosk": "vosk-model-small-ja-0.22",
+ },
+ "ko": {
+ "name": "Korean",
+ "whisper": "ko",
+ "vosk": "vosk-model-small-ko-0.22",
+ },
+ "no": {
+ "name": "Norwegian",
+ "whisper": "no",
+ "vosk": None,
+ },
+ "fa": {
+ "name": "Persian",
+ "whisper": "fa",
+ "vosk": "vosk-model-small-fa-0.42",
+ },
+ "pl": {
+ "name": "Polish",
+ "whisper": "pl",
+ "vosk": "vosk-model-small-pl-0.22",
+ },
"pt": {
"name": "Portuguese",
"whisper": "pt",
"vosk": "vosk-model-small-pt-0.3",
},
+ "ro": {
+ "name": "Romanian",
+ "whisper": "ro",
+ "vosk": None,
+ },
"ru": {
"name": "Russian",
"whisper": "ru",
"vosk": "vosk-model-small-ru-0.22",
},
- "zh": {
- "name": "Chinese",
- "whisper": "zh",
- "vosk": "vosk-model-small-cn-0.22",
+ "es": {
+ "name": "Spanish",
+ "whisper": "es",
+ "vosk": "vosk-model-small-es-0.42",
+ },
+ "sv": {
+ "name": "Swedish",
+ "whisper": "sv",
+ "vosk": "vosk-model-small-sv-rhasspy-0.15",
+ },
+ "ta": {
+ "name": "Tamil",
+ "whisper": "ta",
+ "vosk": None,
+ },
+ "th": {
+ "name": "Thai",
+ "whisper": "th",
+ "vosk": None,
+ },
+ "tr": {
+ "name": "Turkish",
+ "whisper": "tr",
+ "vosk": "vosk-model-small-tr-0.3",
+ },
+ "uk": {
+ "name": "Ukrainian",
+ "whisper": "uk",
+ "vosk": "vosk-model-small-uk-v3-small",
+ },
+ "vi": {
+ "name": "Vietnamese",
+ "whisper": "vi",
+ "vosk": "vosk-model-small-vn-0.4",
},
}
-# VOSK model metadata for display
+# VOSK model metadata for display and download path resolution.
+# When Alphacephei only ships a small model, medium/large point at that same
+# zip so size selection never resolves to None (issue #550).
VOSK_MODEL_INFO = {
"small": {
"size_mb": 40,
@@ -63,14 +190,26 @@ VOSK_MODEL_INFO = {
"languages": {
"en-us": "vosk-model-small-en-us-0.15",
"en-in": "vosk-model-small-en-in-0.4",
- "hi": "vosk-model-small-hi-0.22",
- "es": "vosk-model-small-es-0.42",
+ "ar": "vosk-model-small-ar-0.3",
+ "ca": "vosk-model-small-ca-0.4",
+ "zh": "vosk-model-small-cn-0.22",
+ "cs": "vosk-model-small-cs-0.4-rhasspy",
+ "nl": "vosk-model-small-nl-0.22",
"fr": "vosk-model-small-fr-0.22",
"de": "vosk-model-small-de-0.15",
+ "hi": "vosk-model-small-hi-0.22",
"it": "vosk-model-small-it-0.22",
+ "ja": "vosk-model-small-ja-0.22",
+ "ko": "vosk-model-small-ko-0.22",
+ "fa": "vosk-model-small-fa-0.42",
+ "pl": "vosk-model-small-pl-0.22",
"pt": "vosk-model-small-pt-0.3",
"ru": "vosk-model-small-ru-0.22",
- "zh": "vosk-model-small-cn-0.22",
+ "es": "vosk-model-small-es-0.42",
+ "sv": "vosk-model-small-sv-rhasspy-0.15",
+ "tr": "vosk-model-small-tr-0.3",
+ "uk": "vosk-model-small-uk-v3-small",
+ "vi": "vosk-model-small-vn-0.4",
},
},
"medium": {
@@ -79,14 +218,26 @@ VOSK_MODEL_INFO = {
"languages": {
"en-us": "vosk-model-en-us-0.22",
"en-in": "vosk-model-en-in-0.5",
+ "ar": "vosk-model-ar-0.22-linto-1.1.0",
+ "ca": "vosk-model-small-ca-0.4",
+ "zh": "vosk-model-cn-0.22",
+ "cs": "vosk-model-small-cs-0.4-rhasspy",
+ "nl": "vosk-model-nl-spraakherkenning-0.6",
"fr": "vosk-model-fr-0.22",
"de": "vosk-model-de-0.21",
- "it": "vosk-model-it-0.22",
- "ru": "vosk-model-ru-0.22",
"hi": "vosk-model-hi-0.22",
- "es": "vosk-model-es-0.42",
+ "it": "vosk-model-it-0.22",
+ "ja": "vosk-model-ja-0.22",
+ "ko": "vosk-model-small-ko-0.22",
+ "fa": "vosk-model-fa-0.42",
+ "pl": "vosk-model-small-pl-0.22",
"pt": "vosk-model-pt-0.4",
- "zh": "vosk-model-cn-0.22",
+ "ru": "vosk-model-ru-0.22",
+ "es": "vosk-model-es-0.42",
+ "sv": "vosk-model-small-sv-rhasspy-0.15",
+ "tr": "vosk-model-small-tr-0.3",
+ "uk": "vosk-model-uk-v3",
+ "vi": "vosk-model-vn-0.4",
},
},
"large": {
@@ -95,14 +246,26 @@ VOSK_MODEL_INFO = {
"languages": {
"en-us": "vosk-model-en-us-0.22",
"en-in": "vosk-model-en-in-0.5",
+ "ar": "vosk-model-ar-0.22-linto-1.1.0",
+ "ca": "vosk-model-small-ca-0.4",
+ "zh": "vosk-model-cn-0.22",
+ "cs": "vosk-model-small-cs-0.4-rhasspy",
+ "nl": "vosk-model-nl-spraakherkenning-0.6",
"fr": "vosk-model-fr-0.22",
"de": "vosk-model-de-0.21",
- "it": "vosk-model-it-0.22",
- "ru": "vosk-model-ru-0.22",
"hi": "vosk-model-hi-0.22",
- "es": "vosk-model-es-0.42",
+ "it": "vosk-model-it-0.22",
+ "ja": "vosk-model-ja-0.22",
+ "ko": "vosk-model-small-ko-0.22",
+ "fa": "vosk-model-fa-0.42",
+ "pl": "vosk-model-small-pl-0.22",
"pt": "vosk-model-pt-0.4",
- "zh": "vosk-model-cn-0.22",
+ "ru": "vosk-model-ru-0.22",
+ "es": "vosk-model-es-0.42",
+ "sv": "vosk-model-small-sv-rhasspy-0.15",
+ "tr": "vosk-model-small-tr-0.3",
+ "uk": "vosk-model-uk-v3",
+ "vi": "vosk-model-vn-0.4",
},
},
}
diff --git a/tests/test_main.py b/tests/test_main.py
index 5bb6924..964e8fb 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -90,23 +90,14 @@ class TestMainModule(unittest.TestCase):
self.assertEqual(args.engine, "whisper")
def test_parse_arguments_language_choices(self):
- """Test that language only accepts valid choices."""
- for lang in [
- "auto",
- "en-us",
- "en-in",
- "hi",
- "es",
- "fr",
- "de",
- "it",
- "pt",
- "ru",
- "zh",
- "ja",
- "ko",
- "ar",
- ]:
+ """Test that language only accepts valid choices from the catalog."""
+ from vocalinux.main import LANGUAGE_CHOICES
+ from vocalinux.utils.vosk_model_info import SUPPORTED_LANGUAGES
+
+ self.assertEqual(LANGUAGE_CHOICES, tuple(SUPPORTED_LANGUAGES.keys()))
+ # Spot-check newly exposed languages (issue #565 and catalog expansion).
+ for lang in ["auto", "en-us", "hu", "ja", "ko", "ar", "nl", "pl", "uk"]:
+ self.assertIn(lang, LANGUAGE_CHOICES)
with patch("sys.argv", ["vocalinux", "--language", lang]):
args = parse_arguments()
self.assertEqual(args.language, lang)
diff --git a/tests/test_speech_recognition.py b/tests/test_speech_recognition.py
index 6a91b26..36e44b9 100644
--- a/tests/test_speech_recognition.py
+++ b/tests/test_speech_recognition.py
@@ -275,6 +275,36 @@ class TestSpeechRecognition(unittest.TestCase):
"otherwise initialization crashes with a NoneType path error.",
)
+ def test_supported_languages_vosk_entries_match_model_tables(self):
+ """Every catalog language with a VOSK model must appear in size tables."""
+ from vocalinux.utils.vosk_model_info import SUPPORTED_LANGUAGES
+
+ for lang_code, lang_info in SUPPORTED_LANGUAGES.items():
+ vosk_model = lang_info.get("vosk")
+ if not vosk_model:
+ continue
+ self.assertEqual(
+ VOSK_MODEL_INFO["small"]["languages"].get(lang_code),
+ vosk_model,
+ f"SUPPORTED_LANGUAGES['{lang_code}']['vosk'] must match "
+ f"VOSK_MODEL_INFO['small']['languages']['{lang_code}']",
+ )
+ for size in ("medium", "large"):
+ self.assertIn(
+ lang_code,
+ VOSK_MODEL_INFO[size]["languages"],
+ f"VOSK language '{lang_code}' missing from '{size}' table",
+ )
+
+ def test_hungarian_is_whisper_only_in_catalog(self):
+ """Hungarian (issue #565) is exposed for Whisper engines, not VOSK."""
+ from vocalinux.utils.vosk_model_info import SUPPORTED_LANGUAGES
+
+ hu = SUPPORTED_LANGUAGES["hu"]
+ self.assertEqual(hu["whisper"], "hu")
+ self.assertIsNone(hu["vosk"])
+ self.assertNotIn("hu", VOSK_MODEL_INFO["small"]["languages"])
+
def test_vosk_init_italian_medium_model_resolves_correctly(self):
"""Selecting Italian + the 'medium' VOSK model must not crash (issue #550)."""
manager = SpeechRecognitionManager(engine="vosk", language="it", model_size="medium")
diff --git a/web/src/app/languages/page.tsx b/web/src/app/languages/page.tsx
index e52ee71..4b40215 100644
--- a/web/src/app/languages/page.tsx
+++ b/web/src/app/languages/page.tsx
@@ -4,6 +4,9 @@ import { CheckCircle2, ChevronRight, Globe, Languages, Mic } from "lucide-react"
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
+const WHISPER_ENGINES = ["whisper.cpp", "Whisper"] as const;
+const ALL_ENGINES = ["whisper.cpp", "Whisper", "VOSK"] as const;
+
const supportedLanguages = [
{
code: "en",
@@ -11,62 +14,39 @@ const supportedLanguages = [
nativeName: "English",
flag: "🇺🇸",
description: "Full support across all engines (whisper.cpp, Whisper, VOSK)",
- engines: ["whisper.cpp", "Whisper", "VOSK"],
- modelSizes: ["tiny", "base", "small", "medium", "large"],
+ engines: [...ALL_ENGINES],
},
{
- code: "es",
- name: "Spanish",
- nativeName: "Español",
- flag: "🇪🇸",
- description: "Complete Spanish language support for dictation",
- engines: ["whisper.cpp", "Whisper", "VOSK"],
- modelSizes: ["tiny", "base", "small", "medium", "large"],
+ code: "en-in",
+ name: "English (India)",
+ nativeName: "English (India)",
+ flag: "🇮🇳",
+ description: "Indian English dictation with a dedicated VOSK model",
+ engines: [...ALL_ENGINES],
},
{
- code: "fr",
- name: "French",
- nativeName: "Français",
- flag: "🇫🇷",
- description: "French language voice typing and transcription",
- engines: ["whisper.cpp", "Whisper", "VOSK"],
- modelSizes: ["tiny", "base", "small", "medium", "large"],
+ code: "ar",
+ name: "Arabic",
+ nativeName: "العربية",
+ flag: "🇸🇦",
+ description: "Arabic speech recognition across Whisper and VOSK",
+ engines: [...ALL_ENGINES],
},
{
- code: "de",
- name: "German",
- nativeName: "Deutsch",
- flag: "🇩🇪",
- description: "German dictation with high accuracy",
- engines: ["whisper.cpp", "Whisper", "VOSK"],
- modelSizes: ["tiny", "base", "small", "medium", "large"],
+ code: "bn",
+ name: "Bengali",
+ nativeName: "বাংলা",
+ flag: "🇧🇩",
+ description: "Bengali dictation with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
},
{
- code: "it",
- name: "Italian",
- nativeName: "Italiano",
- flag: "🇮🇹",
- description: "Italian voice recognition and text dictation",
- engines: ["whisper.cpp", "Whisper", "VOSK"],
- modelSizes: ["tiny", "base", "small", "medium", "large"],
- },
- {
- code: "pt",
- name: "Portuguese",
- nativeName: "Português",
- flag: "🇧🇷",
- description: "Brazilian and European Portuguese support",
- engines: ["whisper.cpp", "Whisper", "VOSK"],
- modelSizes: ["tiny", "base", "small", "medium", "large"],
- },
- {
- code: "ru",
- name: "Russian",
- nativeName: "Русский",
- flag: "🇷🇺",
- description: "Russian language speech-to-text transcription",
- engines: ["whisper.cpp", "Whisper", "VOSK"],
- modelSizes: ["tiny", "base", "small", "medium", "large"],
+ code: "ca",
+ name: "Catalan",
+ nativeName: "Català",
+ flag: "🇦🇩",
+ description: "Catalan voice typing with Whisper and VOSK",
+ engines: [...ALL_ENGINES],
},
{
code: "zh",
@@ -74,8 +54,71 @@ const supportedLanguages = [
nativeName: "中文",
flag: "🇨🇳",
description: "Mandarin Chinese voice dictation support",
- engines: ["whisper.cpp", "Whisper", "VOSK"],
- modelSizes: ["tiny", "base", "small", "medium", "large"],
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "cs",
+ name: "Czech",
+ nativeName: "Čeština",
+ flag: "🇨🇿",
+ description: "Czech dictation with Whisper and VOSK",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "da",
+ name: "Danish",
+ nativeName: "Dansk",
+ flag: "🇩🇰",
+ description: "Danish speech-to-text with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
+ },
+ {
+ code: "nl",
+ name: "Dutch",
+ nativeName: "Nederlands",
+ flag: "🇳🇱",
+ description: "Dutch voice recognition across Whisper and VOSK",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "fi",
+ name: "Finnish",
+ nativeName: "Suomi",
+ flag: "🇫🇮",
+ description: "Finnish dictation with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
+ },
+ {
+ code: "fr",
+ name: "French",
+ nativeName: "Français",
+ flag: "🇫🇷",
+ description: "French language voice typing and transcription",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "de",
+ name: "German",
+ nativeName: "Deutsch",
+ flag: "🇩🇪",
+ description: "German dictation with high accuracy",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "el",
+ name: "Greek",
+ nativeName: "Ελληνικά",
+ flag: "🇬🇷",
+ description: "Greek speech recognition with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
+ },
+ {
+ code: "he",
+ name: "Hebrew",
+ nativeName: "עברית",
+ flag: "🇮🇱",
+ description: "Hebrew dictation with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
},
{
code: "hi",
@@ -83,11 +126,156 @@ const supportedLanguages = [
nativeName: "हिन्दी",
flag: "🇮🇳",
description: "Hindi language voice typing and transcription",
- engines: ["whisper.cpp", "Whisper", "VOSK"],
- modelSizes: ["tiny", "base", "small", "medium", "large"],
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "hu",
+ name: "Hungarian",
+ nativeName: "Magyar",
+ flag: "🇭🇺",
+ description: "Hungarian dictation with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
+ },
+ {
+ code: "id",
+ name: "Indonesian",
+ nativeName: "Bahasa Indonesia",
+ flag: "🇮🇩",
+ description: "Indonesian voice typing with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
+ },
+ {
+ code: "it",
+ name: "Italian",
+ nativeName: "Italiano",
+ flag: "🇮🇹",
+ description: "Italian voice recognition and text dictation",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "ja",
+ name: "Japanese",
+ nativeName: "日本語",
+ flag: "🇯🇵",
+ description: "Japanese speech-to-text across Whisper and VOSK",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "ko",
+ name: "Korean",
+ nativeName: "한국어",
+ flag: "🇰🇷",
+ description: "Korean dictation with Whisper and VOSK",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "no",
+ name: "Norwegian",
+ nativeName: "Norsk",
+ flag: "🇳🇴",
+ description: "Norwegian speech recognition with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
+ },
+ {
+ code: "fa",
+ name: "Persian",
+ nativeName: "فارسی",
+ flag: "🇮🇷",
+ description: "Persian (Farsi) dictation with Whisper and VOSK",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "pl",
+ name: "Polish",
+ nativeName: "Polski",
+ flag: "🇵🇱",
+ description: "Polish voice typing with Whisper and VOSK",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "pt",
+ name: "Portuguese",
+ nativeName: "Português",
+ flag: "🇧🇷",
+ description: "Brazilian and European Portuguese support",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "ro",
+ name: "Romanian",
+ nativeName: "Română",
+ flag: "🇷🇴",
+ description: "Romanian dictation with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
+ },
+ {
+ code: "ru",
+ name: "Russian",
+ nativeName: "Русский",
+ flag: "🇷🇺",
+ description: "Russian language speech-to-text transcription",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "es",
+ name: "Spanish",
+ nativeName: "Español",
+ flag: "🇪🇸",
+ description: "Complete Spanish language support for dictation",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "sv",
+ name: "Swedish",
+ nativeName: "Svenska",
+ flag: "🇸🇪",
+ description: "Swedish voice recognition with Whisper and VOSK",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "ta",
+ name: "Tamil",
+ nativeName: "தமிழ்",
+ flag: "🇮🇳",
+ description: "Tamil dictation with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
+ },
+ {
+ code: "th",
+ name: "Thai",
+ nativeName: "ไทย",
+ flag: "🇹🇭",
+ description: "Thai speech-to-text with multilingual Whisper models",
+ engines: [...WHISPER_ENGINES],
+ },
+ {
+ code: "tr",
+ name: "Turkish",
+ nativeName: "Türkçe",
+ flag: "🇹🇷",
+ description: "Turkish dictation with Whisper and VOSK",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "uk",
+ name: "Ukrainian",
+ nativeName: "Українська",
+ flag: "🇺🇦",
+ description: "Ukrainian voice typing with Whisper and VOSK",
+ engines: [...ALL_ENGINES],
+ },
+ {
+ code: "vi",
+ name: "Vietnamese",
+ nativeName: "Tiếng Việt",
+ flag: "🇻🇳",
+ description: "Vietnamese speech recognition with Whisper and VOSK",
+ engines: [...ALL_ENGINES],
},
];
+const languageCount = supportedLanguages.length;
+
const features = [
{
title: "Automatic Language Detection",
@@ -96,9 +284,9 @@ const features = [
icon: Globe,
},
{
- title: "Multi-Engine Support",
+ title: "Engine-Aware Catalog",
description:
- "All supported languages work across whisper.cpp, OpenAI Whisper, and VOSK engines.",
+ "Whisper engines cover the full language list. VOSK appears only where an official model exists.",
icon: Mic,
},
{
@@ -110,15 +298,19 @@ const features = [
];
export const metadata: Metadata = buildPageMetadata({
- title: "Multilingual Voice Dictation - Spanish, French, German, & More | Vocalinux",
+ title: "Multilingual Voice Dictation - 30+ Languages | Vocalinux",
description:
- "Dictate in 9+ languages including Spanish, French, German, Italian, Portuguese, Russian, Chinese, and Hindi. Free offline multilingual voice typing for Linux.",
+ "Dictate in 30+ languages including Spanish, French, German, Hungarian, Japanese, Korean, Arabic, and more. Free offline multilingual voice typing for Linux.",
path: "/languages",
keywords: [
"multilingual voice dictation",
"Spanish voice typing",
"French speech to text",
"German dictation software",
+ "Hungarian voice recognition",
+ "Japanese speech to text",
+ "Korean voice typing",
+ "Arabic dictation",
"Italian voice recognition",
"Portuguese voice typing",
"Russian speech recognition",
@@ -132,10 +324,10 @@ export default function LanguagesPage() {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "Article",
- headline: "Multilingual Voice Dictation for Linux - 9+ Languages Supported",
+ headline: `Multilingual Voice Dictation for Linux - ${languageCount}+ Languages Supported`,
description:
- "Complete guide to Vocalinux multilingual voice dictation. Dictate in Spanish, French, German, Italian, Portuguese, Russian, Chinese, Hindi, and more.",
- dateModified: "2026-02-22",
+ "Complete guide to Vocalinux multilingual voice dictation. Dictate in Spanish, French, German, Hungarian, Japanese, Korean, Arabic, Hindi, and more.",
+ dateModified: "2026-07-29",
author: {
"@type": "Person",
name: "Jatin K Malik",
@@ -162,14 +354,14 @@ export default function LanguagesPage() {
- Dictate in your native language. Vocalinux supports 9+ languages with full offline
- processing - no cloud required, no data leaves your machine.
+ Dictate in your native language. Vocalinux lists {languageCount} languages in Settings,
+ and whisper.cpp / Whisper can auto-detect many more - all offline, with no cloud upload.
Multilingual Voice Dictation for Linux