How FluidVoice Handles Different Languages: A Complete Technical Guide

FluidVoice manages multilingual speech recognition through a central VoiceEngineLanguageCatalog that defines language metadata, generates model-specific routes, and applies user selections to the transcription pipeline.

The FluidVoice iOS application supports dozens of languages across multiple speech-to-text engines including Apple Speech, Whisper, Cohere, Nemotron, and Parakeet. This article examines the complete architecture—from language definitions through UI selection to provider-level execution—based on the actual source code implementation.


The VoiceEngineLanguageCatalog: Central Language Registry

All language handling in FluidVoice originates in [VoiceEngineLanguageCatalog.swift](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Persistence/VoiceEngineLanguageCatalog.swift). This file contains three critical responsibilities: static language definitions, route generation for available models, and persisting user selections.

Language Definitions Structure

The catalog maintains a comprehensive list of supported languages through the languageDefinitions property. Each language carries:

  • id – unique identifier (e.g., "en", "es", "ja")
  • displayName – localized human-readable name
  • aliases – alternative names for search matching
  • isPopular – flag for prominent UI placement
// Conceptual representation based on implementation patterns
struct LanguageDefinition {
    let id: String
    let displayName: String
    let aliases: [String]
    let isPopular: Bool
}

The catalog exposes convenience accessors for different UI contexts:

// Retrieve all languages with at least one compatible model route
let allLanguages = VoiceEngineLanguageCatalog.allLanguages()

// Get prominently displayed languages for onboarding
let popularLanguages = VoiceEngineLanguageCatalog.popularLanguages()

// Search with normalized query matching against name, ID, and aliases
let searchResults = VoiceEngineLanguageCatalog.searchableLanguages(query: "espanol")

LanguageBinding: Mapping Languages to Speech Engines

The inner LanguageBinding enum defines how each speech engine consumes language information. This abstraction allows the same language definition to route differently depending on the target provider.

Binding Type Behavior Example ID
automatic Engine infers language from audio whisper-automatic
appleSpeech Uses Apple-specific locale identifier apple-speech-en-US
cohere Explicit language code for Cohere API cohere-en
nemotron Explicit language code for Nemotron API nemotron-es
whisper Explicit language code for Whisper model whisper-fr

Reference: LanguageBinding enum definition

Each binding generates a unique route identifier through its id computation, ensuring no collisions between different engine-language combinations.


Route Generation: Building Compatible Pipelines

The routeCandidates(for:) method transforms a language definition into actionable transcription pipelines. This function evaluates:

  1. Model availability – whether Parakeet V3, Cohere, Nemotron, or Whisper support the language
  2. Apple Speech compatibility – checking both modern analyzer locales and legacy speech recognition locales
  3. User preference eligibility – filtering to routes actually usable by installed models
// Generate all possible routes for a specific language
if let japanese = VoiceEngineLanguageCatalog.language(id: "ja") {
    let candidates = VoiceEngineLanguageCatalog.routeCandidates(for: japanese)
    
    // candidates might include:
    // - Whisper-japanese (automatic or explicit)
    // - Nemotron-japanese
    // - Apple Speech with ja-JP locale
}

The returned [VoiceEngineLanguageRoute] objects encapsulate both the language and the specific binding required by each engine. Routes are then filtered against SettingsStore.shared.availableModels, ensuring only operational pipelines are presented to users.

Reference: routeCandidates implementation


Persisting Selections: The Apply Mechanism

Once a user selects a language-route combination, apply(_:, to:) commits the choice to persistent storage. This method:

  1. Extracts the LanguageDefinition and LanguageBinding from the route
  2. Updates SettingsStore.shared.selectedLanguage
  3. Sets the appropriate model-specific language property (e.g., selectedWhisperLanguage, selectedNemotronLanguage)
  4. Triggers transcription engine reconfiguration
// Apply a selected route to configure the transcription pipeline
let selectedRoute: VoiceEngineLanguageRoute = /* user selection */
VoiceEngineLanguageCatalog.apply(selectedRoute)

The SettingsStore persists these values across app launches and makes them observable for real-time UI updates.

Reference: apply method


UI Integration: Onboarding and Language Selection

FluidVoice's onboarding flow surfaces language selection through [WelcomeView.swift](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/UI/WelcomeView.swift) and [OnboardingTryoutStepView.swift](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/UI/OnboardingTryoutStepView.swift).

WelcomeView Language Interface

The welcome screen presents:

  • Popular languages as tappable cards for immediate selection
  • Search interface querying searchableLanguages(query:) with debounced input
  • Full language list for manual browsing
// Simplified representation of WelcomeView language selection
struct LanguageSelectionSection: View {
    @State private var searchQuery = ""
    
    var body: some View {
        if searchQuery.isEmpty {
            PopularLanguagesGrid(
                languages: VoiceEngineLanguageCatalog.popularLanguages()
            )
        } else {
            SearchResultsList(
                languages: VoiceEngineLanguageCatalog.searchableLanguages(query: searchQuery)
            )
        }
    }
}

Reference: WelcomeView language UI section

Try-It-Out Flow

After language selection, OnboardingTryoutStepView displays example prompts in the chosen language and initiates a test transcription. This validates that the selected route functions correctly before completing onboarding.

Reference: OnboardingTryoutStepView examples and flow


Provider-Level Language Execution

Individual speech engines consume the persisted language settings through model-specific properties in [SettingsStore.swift](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Persistence/SettingsStore.swift). The NemotronProvider exemplifies this pattern:

// NemotronProvider checking and applying language configuration
func setTargetLanguageIfNeeded() async throws {
    let targetLanguage = SettingsStore.shared.selectedNemotronLanguage
    
    // Only update if language actually changed
    if currentLanguage != targetLanguage {
        try await apiClient.setLanguage(targetLanguage)
        currentLanguage = targetLanguage
    }
}

Similar patterns exist across WhisperProvider, CohereProvider, and the Apple Speech integration. Each provider maps the generic LanguageDefinition to engine-specific parameters.

Reference: NemotronProvider language switch implementation


Adding New Language Support

The modular architecture enables straightforward language expansion:

  1. Add definition – append to languageDefinitions in VoiceEngineLanguageCatalog.swift
  2. Declare model support – update routeCandidates(for:) to include new language-model combinations
  3. Configure provider mappings – ensure each relevant provider can consume the new language code

No UI changes are required unless the language qualifies for the "popular" designation.


Summary

  • VoiceEngineLanguageCatalog serves as the single source of truth for language metadata and routing
  • LanguageBinding abstracts engine-specific language consumption patterns
  • routeCandidates(for:) dynamically generates compatible transcription pipelines
  • apply(_:, to:) persists selections to SettingsStore for cross-session retention
  • Onboarding UI leverages catalog search and popular language APIs for intuitive selection
  • Provider implementations execute language-specific configuration at runtime

Frequently Asked Questions

How does FluidVoice detect which languages are available on a device?

The catalog filters routeCandidates against SettingsStore.shared.availableModels, which reflects installed transcription engines and their capabilities. Apple Speech availability additionally checks SFSpeechRecognizer.supportedLocales() at runtime.

Can FluidVoice automatically detect spoken language without user selection?

Yes. The LanguageBinding.automatic option enables Whisper and other models to infer language from audio content. This route appears in candidate generation when the underlying model supports auto-detection.

What happens when a selected language has no compatible models?

The allLanguages() and popularLanguages() methods pre-filter to languages with at least one viable route. If model availability changes (e.g., via in-app purchase or update), the catalog automatically adjusts presented options.

How are language names localized for different UI languages?

The displayName property in LanguageDefinition returns localized strings through the standard iOS localization infrastructure. Aliases support cross-language search—entering "espanol" matches Spanish regardless of device language.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →