From 28bed4c1fd9efa2f9b815954d311148dcb8149e9 Mon Sep 17 00:00:00 2001 From: Jatin K Malik Date: Tue, 28 Jul 2026 19:02:37 -0700 Subject: [PATCH] feat(languages): expand speech catalog with Hungarian and more (#616) Expose Hungarian and other high-demand Whisper languages in Settings/CLI, wire official VOSK models where Alphacephei ships them, keep CLI choices derived from SUPPORTED_LANGUAGES, and update the languages marketing page with honest per-engine support. Fixes #565 Co-authored-by: Cursor Agent Co-authored-by: Jatin K Malik --- src/vocalinux/main.py | 25 +- src/vocalinux/utils/vosk_model_info.py | 211 ++++++++++++++-- tests/test_main.py | 25 +- tests/test_speech_recognition.py | 30 +++ web/src/app/languages/page.tsx | 320 ++++++++++++++++++++----- 5 files changed, 488 insertions(+), 123 deletions(-) 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() {

- 9+ Languages + {languageCount}+ Languages

Multilingual Voice Dictation for Linux

- 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.

@@ -232,7 +424,7 @@ export default function LanguagesPage() {
  • - Use "Auto" for automatic language detection (whisper.cpp/Whisper) + Use "Auto" for automatic language detection (whisper.cpp/Whisper)
  • @@ -249,7 +441,7 @@ export default function LanguagesPage() {
  • - "tiny" model works for all supported languages + Use a Standard multilingual whisper.cpp model for non-English dictation