# Language Preferences Feature

This document provides comprehensive information about the Language Preferences feature in MPV.Rocks Installer.

## Overview

The Language Preferences feature allows users to configure audio and subtitle language settings for MPV media player. This feature is available in both TUI (Terminal User Interface) and Web UI.

## Table of Contents

- [Feature Description](#feature-description)
- [TUI Implementation](#tui-implementation)
- [Web UI Implementation](#web-ui-implementation)
- [API Reference](#api-reference)
- [Data Structures](#data-structures)
- [Database](#database)
- [Configuration](#configuration)

---

## Feature Description

### Purpose

Allow users to select preferred audio and subtitle languages for MPV playback. MPV will use these languages in the specified priority order.

### Key Features

**Language Database:**
- 123+ languages from 7 language families
- Regional variants (en-US, zh-CN, es-ES, etc.)
- Native language names
- English language names
- ISO 639-1 language codes
- Text direction support (ltr/rtl)

**Selection Capabilities:**
- Multiple language selection
- Priority ordering (audio languages used in order specified)
- Separate audio and subtitle language lists
- Regional variant selection
- Search functionality (Web UI only)

**Configuration Integration:**
- Direct MPV configuration editing
- Automatic config backup
- Config preservation with comments
- Retry logic for save operations

---

## TUI Implementation

### Data Structures

**LanguagePreferences Struct:**
```go
type LanguagePreferences struct {
    AudioLanguage    []string `json:"audio_language"`
    SubtitleLanguage []string `json:"subtitle_language"`
    AudioPriority    []string `json:"audio_priority"`
    SubtitlePriority []string `json:"subtitle_priority"`
}
```

### Model State Fields

**Language Preferences Fields:**
```go
type Model struct {
    // ... other fields ...
    
    // Language Preferences
    languagePrefs              LanguagePreferences
    audioLanguageList         []string
    subtitleLanguageList      []string
    selectedAudioLanguage      int
    selectedSubtitleLanguage    int
    availableAudioLanguages    []Language
    availableSubtitleLanguages []Language
    currentMajorLanguage      string
    
    // State
    state                      State
}
```

### TUI States

**Language Preferences State:**
- `StateLanguagePrefs` - Main language preferences page

### Load Preferences

**Function:** `LoadPreferences(m Model) LanguagePreferences`

**Purpose:** Load language preferences from MPV config on page open

**Implementation:**
```go
func LoadPreferences(m Model) LanguagePreferences {
    prefs := LanguagePreferences{}
    
    // Load audio languages
    prefs.AudioLanguage = config.GetConfigValue(constants.ConfigKeyAudioLanguage)
    
    // Load subtitle languages
    prefs.SubtitleLanguage = config.GetConfigValue(constants.ConfigKeySubtitleLanguage)
    
    // Set priority lists
    prefs.AudioPriority = prefs.AudioLanguage
    prefs.SubtitlePriority = prefs.SubtitleLanguage
    
    return prefs
}
```

**Configuration Keys:**
- `alang` - Audio languages (comma-separated: `en,es,fr`)
- `slang` - Subtitle languages (comma-separated: `en,ja,ko`)

### Save Preferences

**Function:** `SavePreferences(m Model, prefs LanguagePreferences) error`

**Purpose:** Save language preferences to MPV config

**Implementation:**
```go
func SavePreferences(m Model, prefs LanguagePreferences) error {
    // Save audio languages
    if err := config.SetConfigValue(constants.ConfigKeyAudioLanguage, prefs.AudioLanguage, true); err != nil {
        return fmt.Errorf("failed to save audio languages: %w", err)
    }
    
    // Save subtitle languages
    if err := config.SetConfigValue(constants.ConfigKeySubtitleLanguage, prefs.SubtitleLanguage, true); err != nil {
        return fmt.Errorf("failed to save subtitle languages: %w", err)
    }
    
    return nil
}
```

**ConfigPreservationHandler Integration:**
- Backup created on first write automatically
- Comments in config preserved
- File permissions maintained

### TUI Commands

**Load Command:**
```go
func runLoadLanguagePreferencesCmd() tea.Cmd {
    return func() tea.Msg {
        return languagePrefsLoadedMsg{}
    }
}
```

**Save Command:**
```go
func runSaveLanguagePreferencesCmd() tea.Cmd {
    return func() tea.Msg {
        if err := SavePreferences(m, m.languagePrefs); err != nil {
            return languagePrefsErrorMsg{Error: err}
        }
        return languagePrefsSavedMsg{}
    }
}
```

### Update Handler

**State Transition:**
```go
case languagePrefsSavedMsg:
    m.successMessage = "Language preferences saved successfully"
    m.state = StateMainMenu
    return m, nil

case languagePrefsErrorMsg:
    m.errorMessage = fmt.Sprintf("Failed to save: %v", msg.Error)
    return m, nil
```

---

## Web UI Implementation

### Pages and Routes

**Language Preferences Page:**
- **Route:** `/languages`
- **Template:** `internal/webassets/templates/languages.html`
- **Static Assets:** `internal/webassets/static/languages.js`

### API Endpoints

**Load Preferences:**
- **Endpoint:** `GET /api/languages/load`
- **Response:**
  ```json
  {
    "audio_languages": ["en", "es", "fr"],
    "subtitle_languages": ["en", "ja", "ko"]
  }
  ```

**Save Preferences:**
- **Endpoint:** `POST /api/languages/save`
- **Request:**
  ```json
  {
    "audio_languages": ["en", "de", "fr"],
    "subtitle_languages": ["en", "es", "pt"]
  }
  ```
- **Response:**
  ```json
  {
    "status": "success"
  }
  ```
- **Error Response:**
  ```json
  {
    "status": "error",
    "message": "failed after 3 attempts: audio: permission denied"
  }
  ```

### JavaScript Functionality

**Search Functionality:**
- 300ms debounce for search input
- Real-time filtering of major languages
- Results count badge
- Clear search button

**Language Selection:**
- Click to select/deselect languages
- Multiple selection support
- Visual selection indicators
- Priority ordering

**Regional Variants:**
- Click language to expand and see regional variants
- Click variant to select
- Automatic deselection of other variants

### UI Components

**Search Box:**
- Text input with search icon
- Clear (X) button
- Results count badge (number only)
- Positioned absolutely inside search input

**Major Language List:**
- Scrollable list (70% viewport height)
- Language items with flags
- Hover effects
- Selection indicators

**Regional Variants Panel:**
- Hidden by default
- Shows when language clicked
- Scrollable list (70% viewport height)
- Variant items with region names

**Audio/Subtitle Language Lists:**
- Separate lists for audio and subtitle
- Scrollable lists (45% viewport height)
- Drag-and-drop support for priority ordering
- Add/remove buttons

**Save/Cancel Buttons:**
- Save button at bottom of page
- Cancel button to discard changes
- Loading states during save

### Template Structure

```html
<!-- Main Container -->
<div class="min-h-screen bg-gray-900 text-white p-8">
    <!-- Page Header -->
    <h1>Language Preferences</h1>
    
    <!-- Search Section -->
    <div class="mb-4">
        <!-- Search input with clear button and count badge -->
    </div>
    
    <!-- Main Content Grid -->
    <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
        <!-- Left Column -->
        <div>
            <!-- Major Languages Section -->
            <div class="mb-4">
                <!-- Major language list (70vh) -->
            </div>
            
            <!-- Regional Variants Section -->
            <div>
                <!-- Regional variants list (70vh) -->
            </div>
        </div>
        
        <!-- Right Column -->
        <div>
            <!-- Audio Language Priority -->
            <div class="mb-4">
                <!-- Audio languages list (45vh) -->
            </div>
            
            <!-- Subtitle Language Priority -->
            <div class="mb-4">
                <!-- Subtitle languages list (45vh) -->
            </div>
            
            <!-- Action Buttons -->
            <div>
                <!-- Save and Cancel buttons -->
            </div>
        </div>
    </div>
</div>
```

### JavaScript Implementation

**Key Functions:**

```javascript
// Search functionality
const searchInput = document.getElementById('major-language-search');
let searchDebounce;

function setupSearchListener() {
    searchDebounce = debounce((query) => {
        filterMajorLanguageList(query);
    }, 300);
    
    searchInput.addEventListener('input', (e) => {
        searchDebounce(e.target.value);
    });
}

// Clear search button
const clearSearchBtn = document.getElementById('clear-search-btn');

function setupClearButton() {
    clearSearchBtn.addEventListener('click', () => {
        searchInput.value = "";
        renderMajorLanguageList();
        searchInput.focus();
    });
}

// Load preferences
async function loadPreferences() {
    const response = await fetch('/api/languages/load');
    const data = await response.json();
    
    audioLanguages = data.audio_languages;
    subtitleLanguages = data.subtitle_languages;
    
    renderMajorLanguageList();
    renderAudioLanguageList();
    renderSubtitleLanguageList();
}

// Save preferences
async function savePreferences() {
    const response = await fetch('/api/languages/save', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
            audio_languages: audioLanguages,
            subtitle_languages: subtitleLanguages
        })
    });
    
    const data = await response.json();
    
    if (data.status === 'success') {
        showSuccessMessage('Language preferences saved successfully');
    } else {
        showErrorMessage('Failed to save: ' + data.message);
    }
}
```

---

## API Reference

### GET /api/languages/load

**Purpose:** Load current language preferences from MPV config

**Request:** None (HTTP GET)

**Response:**
```json
{
  "audio_languages": ["en", "es", "fr"],
  "subtitle_languages": ["en", "ja", "ko"]
}
```

**Error Handling:**
- Returns empty arrays if config keys not found
- Returns HTTP 500 on config read errors

### POST /api/languages/save

**Purpose:** Save language preferences to MPV config

**Request:**
```json
{
  "audio_languages": ["en", "de", "fr"],
  "subtitle_languages": ["en", "es", "pt"]
}
```

**Response (Success):**
```json
{
  "status": "success"
}
```

**Response (Error):**
```json
{
  "status": "error",
  "message": "failed after 3 attempts: audio: permission denied; subtitle: file locked"
}
```

**Error Handling:**
- 3 attempts with exponential backoff (1s, 2s, 3s)
- Returns HTTP 500 on save errors
- Empty arrays clear config values
- Comprehensive logging

---

## Data Structures

### Language Data Structure

**Language Struct:**
```go
type Language struct {
    Code        string `json:"code"`
    EnglishName string `json:"english_name"`
    NativeName  string `json:"native_name"`
    Region      string `json:"region"`
    Direction   string `json:"direction"`
}
```

**Fields:**
- `Code` - ISO 639-1 language code (en, zh, es)
- `EnglishName` - English language name (English, Chinese, Spanish)
- `NativeName` - Native language name (English, 中文, Español)
- `Region` - Region/country (US, CN, ES)
- `Direction` - Text direction (ltr, rtl)

### Regional Variant Data Structure

**RegionalVariant Struct:**
```go
type RegionalVariant struct {
    Code       string `json:"code"`
    Language   string `json:"language"`
    Region     string `json:"region"`
    NativeName string `json:"native_name"`
}
```

**Fields:**
- `Code` - Regional language code (en-US, zh-CN, es-ES)
- `Language` - Base language code (en, zh, es)
- `Region` - Region name (United States, China, Spain)
- `NativeName` - Native regional name

---

## Database

### Language Database Location

**File:** `internal/assets/locales.json`

**Structure:**
```json
{
  "languages": [
    {
      "code": "en",
      "english_name": "English",
      "native_name": "English",
      "region": "",
      "direction": "ltr",
      "regional_variants": [
        {"code": "en-US", "language": "en", "region": "US", "native_name": "English (United States)"},
        {"code": "en-GB", "language": "en", "region": "GB", "native_name": "English (United Kingdom)"}
      ]
    },
    {
      "code": "zh",
      "english_name": "Chinese",
      "native_name": "中文",
      "region": "",
      "direction": "ltr",
      "regional_variants": [
        {"code": "zh-CN", "language": "zh", "region": "CN", "native_name": "简体中文"},
        {"code": "zh-TW", "language": "zh", "region": "TW", "native_name": "繁體中文"}
      ]
    }
  ]
}
```

**Total Languages:** 123+ languages from 7 language families

### Major Languages

**Definition:** Languages that appear in the major/parent language list (33 total)

**Selection Criteria:**
- High usage globally
- Comprehensive MPV localization support
- Available MPV translations

**Major Languages List (alphabetical):**
Arabic, Chinese, Czech, Danish, Dutch, English, Finnish, French, German, Greek, Hindi, Hungarian, Indonesian, Italian, Japanese, Korean, Malay, Norwegian, Polish, Portuguese, Romanian, Russian, Slovak, Spanish, Swedish, Tamil, Telugu, Thai, Turkish, Vietnamese, Fijian Hindi, Filipino, Hebrew

**Implementation:**
- Stored in `pkg/locale/selection.go`
- `commonLanguageCodes` array
- `GetMajorLanguages()` function with alphabetical sorting

---

## Configuration

### MPV Configuration Keys

**Audio Languages:**
- **Key:** `alang`
- **Value:** Comma-separated language codes (e.g., `en,es,fr`)
- **Usage:** MPV uses these languages for audio in priority order
- **Example:** `alang=en,es,fr` (try English first, then Spanish, then French)

**Subtitle Languages:**
- **Key:** `slang`
- **Value:** Comma-separated language codes (e.g., `en,ja,ko`)
- **Usage:** MPV uses these languages for subtitles in priority order
- **Example:** `slang=en,ja,ko` (try English first, then Japanese, then Korean)

### Config File Format

**Location:** `~/.config/mpv/mpv.conf`

**Example:**
```ini
# Language Preferences
alang=en,es,fr,de
slang=en,ja,ko,zh-cn

# Other settings...
hwdec=auto
vo=gpu
```

### Backup System

**Backup Location:** `~/.config/mpv/conf_backups/`

**Backup Filename Format:** `2006-01-02-150405_mpv.conf`

**Backup Strategy:**
- Automatic backup on first write
- Preserves all existing config entries
- Preserves comments and formatting
- Unlimited backup history

**Restore Functionality:**
- Web UI: Restore from backup list
- TUI: Restore from backup via menu
- Restore with full overwrite or merge options

---

## Testing

### Unit Tests

**Test Files:** `pkg/locale/locale_test.go`

**Test Coverage:**
- Language data structure validation
- Code format validation
- Regional variant parsing
- Major language filtering

### Integration Tests

**Test Scenarios:**
1. Load default config (no languages set)
2. Save languages and reload
3. Update languages and save
4. Clear languages and save
5. Priority ordering verification
6. Regional variant selection

### Manual Testing

**Test Checklist:**
- [ ] Language preferences load correctly
- [ ] Languages save to config
- [ ] Config backup created on first save
- [ ] Empty arrays clear config values
- [ ] Comments preserved
- [ ] Priority ordering works in MPV
- [ ] Multiple languages supported
- [ ] Search functionality works (Web UI)
- [ ] Clear search button works (Web UI)
- [ ] Regional variants display correctly (Web UI)
- [ ] Results count badge accurate (Web UI)

### Testing Commands

```bash
# Build and run
make build
./mpv-manager --debug

# Run tests
go test -v ./pkg/locale/...
```

---

## Troubleshooting

### Common Issues

**Languages Not Saving:**
- **Cause:** File permissions or directory inaccessible
- **Solution:** Check `~/.config/mpv/` permissions, retry logic handles transient errors

**Config Not Loading:**
- **Cause:** Config file corrupted or missing
- **Solution:** Use backup restore functionality or reset to recommended config

**Empty Arrays Clearing Config:**
- **Issue:** Empty arrays don't write config value
- **Solution:** `config.SetConfigValue()` handles empty slices by removing config entry

**Search Not Working:**
- **Cause:** JavaScript error or debounce not firing
- **Solution:** Check browser console for errors, verify `languages.js` loaded

**Regional Variants Not Showing:**
- **Cause:** Click event not bound or data not loaded
- **Solution:** Check browser console for errors, verify language data loaded

### Debug Mode

```bash
# Enable debug logging
./mpv-manager --debug

# Check language config loading
cat ~/.config/mpv/mpv-manager.log | grep -i language
```

---

## Future Enhancements

### Planned Features

1. **Language Auto-Detection**
   - Detect system language on first run
   - Auto-select system language
   - Fallback to English if unsupported

2. **Language Import/Export**
   - Export language preferences to file
   - Import language preferences from file
   - Share language settings between devices

3. **Advanced Priority Configuration**
   - Custom priority weights
   - Conditional language selection (e.g., different for video vs audio)
   - Per-file-type language preferences

4. **Language Recommendations**
   - Suggest languages based on media library
   - Detect audio/subtitle language from media files
   - Smart default selections

5. **Accessibility Improvements**
   - Screen reader support
   - Keyboard navigation for all features
   - High contrast mode
   - Larger text option

---

## Related Documentation

For more information about language preferences, see:
- [Session Summaries](../SESSION_SUMMARIES.md) - Session 2026-02-02: TUI Language Preferences Implementation
- [Architecture](../ARCHITECTURE.md) - System architecture and design
- [Troubleshooting](../TROUBLESHOOTING.md) - Common issues and solutions
- [Testing Guide](../TESTING.md) - Testing procedures and checklists

---

## Conclusion

The Language Preferences feature provides comprehensive audio and subtitle language configuration for MPV.Rocks Installer, with support for 123+ languages across 7 language families, multiple selection, priority ordering, and robust configuration management with automatic backups.
