# Language System Migration Guide

This document details all changes made to migrate from hardcoded languages to a database-driven language management system. Use this guide to apply the same changes to additional sites.

## Overview

**Before:** Languages were hardcoded in multiple files (PostController, PostResource, language switcher component, app.js)

**After:** Languages are stored in database with admin panel for management (enable/disable, add, edit, delete)

---

## Step 1: Create Language Model and Migration

### 1.1 Create Model and Migration
```bash
php artisan make:model Language -m
```

### 1.2 Update Migration File
**File:** `database/migrations/YYYY_MM_DD_HHMMSS_create_languages_table.php`

```php
public function up(): void
{
    Schema::create('languages', function (Blueprint $table) {
        $table->id();
        $table->string('code', 10)->unique()->comment('Language code (e.g., de, en, no)');
        $table->string('name')->comment('Display name (e.g., Österreich, Norsk)');
        $table->string('flag', 10)->nullable()->comment('Flag emoji (e.g., 🇦🇹, 🇳🇴)');
        $table->string('gtranslate_code', 20)->nullable()->comment('GTranslate code (e.g., de, de|ch, no)');
        $table->boolean('is_enabled')->default(true)->comment('Whether language is enabled in switcher');
        $table->integer('sort_order')->default(0)->comment('Sort order for display');
        $table->timestamps();
    });
}
```

### 1.3 Update Language Model
**File:** `app/Models/Language.php`

Add fillable, casts, and scopes:

```php
protected $fillable = [
    'code',
    'name',
    'flag',
    'gtranslate_code',
    'is_enabled',
    'sort_order',
];

protected $casts = [
    'is_enabled' => 'boolean',
    'sort_order' => 'integer',
];

public function scopeEnabled($query)
{
    return $query->where('is_enabled', true);
}

public function scopeOrdered($query)
{
    return $query->orderBy('sort_order')->orderBy('name');
}

public static function getEnabled()
{
    return static::enabled()->ordered()->get();
}

public static function getOptions()
{
    return static::enabled()->ordered()->pluck('name', 'code')->toArray();
}

public static function getForSwitcher()
{
    return static::enabled()->ordered()->get()->map(function ($lang) {
        return [
            'code' => $lang->code,
            'name' => $lang->name,
            'flag' => $lang->flag,
            'gtranslate_code' => $lang->gtranslate_code ?? $lang->code,
        ];
    })->toArray();
}
```

---

## Step 2: Create Filament Language Resource

### 2.1 Create Resource
```bash
php artisan make:filament-resource Language --generate
```

### 2.2 Update LanguageResource
**File:** `app/Filament/Resources/LanguageResource.php`

**Key changes:**
- Set navigation icon to `heroicon-o-globe-alt`
- Set navigation label to `Languages`
- Set navigation group to `Settings`
- Form fields: code, name, gtranslate_code, is_enabled, sort_order (NO flag field)
- Table columns: code, name, gtranslate_code, is_enabled, sort_order (NO flag column)
- Default sort by `sort_order`

**Form Schema:**
```php
Forms\Components\TextInput::make('code')
    ->required()
    ->unique(ignoreRecord: true)
    ->maxLength(10)
    ->label('Language Code')
    ->helperText('ISO language code (e.g., de, en, no)')
    ->placeholder('de'),
Forms\Components\TextInput::make('name')
    ->required()
    ->maxLength(255)
    ->label('Display Name')
    ->helperText('Name shown in language switcher (e.g., Österreich, Norsk)')
    ->placeholder('Österreich'),
Forms\Components\TextInput::make('gtranslate_code')
    ->maxLength(20)
    ->label('GTranslate Code')
    ->helperText('GTranslate language code (e.g., de, de|ch, no). Leave empty to use language code.')
    ->placeholder('de'),
Forms\Components\Toggle::make('is_enabled')
    ->label('Enabled')
    ->helperText('Enable this language in the language switcher')
    ->default(true),
Forms\Components\TextInput::make('sort_order')
    ->label('Sort Order')
    ->numeric()
    ->default(0)
    ->helperText('Lower numbers appear first in the language switcher'),
```

---

## Step 3: Create Language Seeder

### 3.1 Create Seeder
```bash
php artisan make:seeder LanguageSeeder
```

### 3.2 Update LanguageSeeder
**File:** `database/seeders/LanguageSeeder.php`

The seeder should:
1. Include base languages (de, no, en, es, fr, it, pt, fi)
2. Check posts table for languages actually in use
3. Add any additional languages found in posts
4. Use `updateOrCreate` to avoid duplicates

**Important:** Only `de` and `no` should be enabled by default (`is_enabled => true`)

---

## Step 4: Update PostController

**File:** `app/Http/Controllers/PostController.php`

### 4.1 Add Language Import
```php
use App\Models\Language;
```

### 4.2 Replace All Hardcoded Language Arrays

**Find and replace in ALL methods:**
- `getCasinoPosts()`
- `getCombinedReviews()`
- `fetchUpdatedSections()`
- `show()`

**Old code pattern:**
```php
if (!in_array($language, ['de', 'en', 'es', 'fr', 'it', 'pt', 'no'])) {
    $language = 'de';
}
```

**New code pattern:**
```php
// Get valid language codes from database
$validCodes = Language::enabled()->pluck('code')->toArray();
// Also check GTranslate codes for matching
$langByGt = Language::enabled()->where('gtranslate_code', $detectedLang)->first();
if ($langByGt) {
    $language = $langByGt->code;
} elseif (in_array($detectedLang, $validCodes)) {
    $language = $detectedLang;
} else {
    // Default to first enabled language or 'de'
    $defaultLang = Language::enabled()->ordered()->first();
    $language = $defaultLang ? $defaultLang->code : 'de';
}
```

**Also update default language assignment:**
```php
// Old: $language = 'de';
// New:
$defaultLang = Language::enabled()->ordered()->first();
$language = $defaultLang ? $defaultLang->code : 'de';
```

---

## Step 5: Update PostResource

**File:** `app/Filament/Resources/PostResource.php`

### 5.1 Remove Hardcoded $supportedLanguages Array

**Remove:**
```php
protected static ?array $supportedLanguages = [
    'en' => 'Ireland/English',
    'de' => 'Deutsch/German',
    // ... etc
];
```

**Replace with:**
```php
/**
 * Get supported languages from database
 */
protected static function getSupportedLanguages(): array
{
    return \App\Models\Language::enabled()->ordered()->pluck('name', 'code')->toArray();
}
```

### 5.2 Update All References

**Find all instances of:**
- `self::$supportedLanguages`
- `PostResource::$supportedLanguages`

**Replace with:**
- `self::getSupportedLanguages()`

**Locations to update:**
1. MultiSelect for languages field
2. Localized Deposit & Withdrawal section
3. Welcome Bonus Localized section
4. Post Ordering section
5. Language filter in table
6. Order display formatStateUsing method

---

## Step 6: Update Language Switcher Component

**File:** `resources/views/components/pirate-language-switcher.blade.php`

### 6.1 Replace Hardcoded Languages Array

**Old code (at top of file):**
```php
$languages = [
    ['code' => 'de', 'name' => 'Österreich', 'flag' => '🇦🇹'],
    ['code' => 'no', 'name' => 'Norsk', 'flag' => '🇳🇴'],
];
```

**New code:**
```php
// Get enabled languages from database
$dbLanguages = \App\Models\Language::getForSwitcher();

// Build languages array for frontend
$languages = [];
$languagesMap = [];

foreach ($dbLanguages as $lang) {
    $languages[] = [
        'code' => $lang['code'],
        'name' => $lang['name'],
        'flag' => $lang['flag'] ?? '',
    ];
    $languagesMap[$lang['code']] = [
        'name' => $lang['name'],
        'flag' => $lang['flag'] ?? '',
        'code' => $lang['code'],
        'gtranslate_code' => $lang['gtranslate_code'] ?? $lang['code'],
    ];
}

// Get current language from cookie
$currentLang = 'de';
if (isset($_COOKIE['googtrans'])) {
    $parts = explode('/', $_COOKIE['googtrans']);
    $detectedLang = end($parts) ?: 'de';
    // Normalize GTranslate format like "de|ch" or "no|no"
    if (strpos($detectedLang, '|') !== false) {
        $detectedLang = explode('|', $detectedLang)[0];
    }
    
    // Try to find language by GTranslate code first
    $foundLang = null;
    foreach ($dbLanguages as $lang) {
        $gtCode = $lang['gtranslate_code'] ?? $lang['code'];
        if ($gtCode === $detectedLang || $lang['code'] === $detectedLang) {
            $foundLang = $lang;
            break;
        }
    }
    
    if ($foundLang) {
        $currentLang = $foundLang['code'];
    } else {
        // Default to first enabled language or 'de'
        $firstLang = !empty($dbLanguages) ? $dbLanguages[0] : null;
        $currentLang = $firstLang ? $firstLang['code'] : 'de';
    }
} else {
    // Default to first enabled language or 'de'
    $firstLang = !empty($dbLanguages) ? $dbLanguages[0] : null;
    $currentLang = $firstLang ? $firstLang['code'] : 'de';
}

$currentLanguage = $languagesMap[$currentLang] ?? ($languagesMap['de'] ?? (!empty($languagesMap) ? reset($languagesMap) : ['name' => 'Deutsch', 'flag' => '🇦🇹', 'code' => 'de']));

// JSON encode languages array once
$languagesJson = json_encode($languages, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
$languagesMapJson = json_encode($languagesMap, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);

// Also create GTranslate mapping for JavaScript
$gtMapping = [];
foreach ($dbLanguages as $lang) {
    $gtMapping[$lang['code']] = $lang['gtranslate_code'] ?? $lang['code'];
}
$gtMappingJson = json_encode($gtMapping, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
```

### 6.2 Add GTranslate Mapping to Component HTML

**Find:**
```html
<div x-data="pirateLanguageSwitcherData()" 
     class="relative pirate-lang-switcher notranslate"
     data-gt-notranslate="true"
     @click.away="open = false">
```

**Replace with:**
```html
<div x-data="pirateLanguageSwitcherData()" 
     class="relative pirate-lang-switcher notranslate"
     data-gt-notranslate="true"
     data-gt-mapping="{!! htmlspecialchars($gtMappingJson, ENT_QUOTES, 'UTF-8') !!}"
     @click.away="open = false">
```

---

## Step 7: Update app.js

**File:** `resources/js/app.js`

### 7.1 Update GTranslate Mapping

**Find:**
```javascript
const langMap = {
    'de': 'de',           // German (Germany)
    'ch': 'de|ch',        // German (Switzerland)
    'no': 'no'            // Norwegian
};

const gtLangCode = langMap[langCode] || langCode;
```

**Replace with:**
```javascript
// Get GTranslate mapping from database (via data attribute on language switcher)
let langMap = {};
const switcher = document.querySelector('.pirate-lang-switcher');
if (switcher && switcher.dataset.gtMapping) {
    try {
        langMap = JSON.parse(switcher.dataset.gtMapping);
    } catch (e) {
        console.warn('Failed to parse GTranslate mapping, using fallback');
        // Fallback to default mapping
        langMap = {
            'de': 'de',
            'no': 'no'
        };
    }
} else {
    // Fallback if no mapping found
    langMap = {
        'de': 'de',
        'no': 'no'
    };
}

const gtLangCode = langMap[langCode] || langCode;
```

---

## Step 8: Update Other Controllers/Services

### 8.1 LandingPageQueryService
**File:** `app/Services/LandingPageQueryService.php`

**Find:**
```php
if (in_array($language, ['de', 'en', 'es', 'fr', 'it', 'pt', 'no'])) {
    return $language;
}
return 'de'; // Default to German
```

**Replace with:**
```php
$validCodes = \App\Models\Language::enabled()->pluck('code')->toArray();
if (in_array($language, $validCodes)) {
    return $language;
}
// Default to first enabled language or 'de'
$defaultLang = \App\Models\Language::enabled()->ordered()->first();
return $defaultLang ? $defaultLang->code : 'de';
```

### 8.2 LandingPageController
**File:** `app/Http/Controllers/LandingPageController.php`

**Same replacement as LandingPageQueryService**

---

## Step 9: Set Admin Locale to English

**File:** `app/Providers/AppServiceProvider.php`

**Add locale detection in boot() method:**

```php
public function boot()
{
    // Set locale based on route - English for Filament admin, German for frontend
    if ($this->app->runningInConsole()) {
        // Console commands - use default
        return;
    }

    $request = $this->app['request'];
    $filamentPath = config('filament.path', 'admin');
    $filamentCorePath = config('filament.core_path', 'filament');
    
    if ($request->is($filamentPath . '/*') || 
        $request->is($filamentCorePath . '/*') ||
        $request->is($filamentPath) ||
        $request->is($filamentCorePath)) {
        // Filament admin panel - use English
        app()->setLocale('en');
    } else {
        // Frontend - use German (from config)
        app()->setLocale(config('app.locale', 'de'));
    }

    // ... rest of boot method
}
```

---

## Step 10: Run Migrations and Seeders

### 10.1 Run Migration
```bash
php artisan migrate --path=database/migrations/YYYY_MM_DD_HHMMSS_create_languages_table.php
```

### 10.2 Run Seeder
```bash
php artisan db:seed --class=LanguageSeeder
```

---

## Files Created

1. `app/Models/Language.php`
2. `database/migrations/YYYY_MM_DD_HHMMSS_create_languages_table.php`
3. `app/Filament/Resources/LanguageResource.php`
4. `app/Filament/Resources/LanguageResource/Pages/ListLanguages.php`
5. `app/Filament/Resources/LanguageResource/Pages/CreateLanguage.php`
6. `app/Filament/Resources/LanguageResource/Pages/EditLanguage.php`
7. `database/seeders/LanguageSeeder.php`

## Files Modified

1. `app/Http/Controllers/PostController.php`
2. `app/Filament/Resources/PostResource.php`
3. `resources/views/components/pirate-language-switcher.blade.php`
4. `resources/js/app.js`
5. `app/Services/LandingPageQueryService.php`
6. `app/Http/Controllers/LandingPageController.php`
7. `app/Providers/AppServiceProvider.php`

---

## Testing Checklist

After migration, verify:

- [ ] Languages table created successfully
- [ ] Seeder runs without errors
- [ ] Admin panel shows Languages in Settings group
- [ ] Can create/edit/delete languages in admin
- [ ] Can enable/disable languages
- [ ] Language switcher shows only enabled languages
- [ ] Post language filtering works with database languages
- [ ] GTranslate integration works with database gtranslate_code values
- [ ] Admin panel displays in English
- [ ] Frontend still displays in German
- [ ] No hardcoded language arrays remain in codebase

---

## Important Notes

1. **Flag Field:** Removed from admin form/table but kept in database for frontend use
2. **Default Languages:** Only `de` and `no` enabled by default
3. **GTranslate Codes:** Can be different from language code (e.g., `de|ch` for Swiss German)
4. **Backward Compatibility:** Seeder detects languages from existing posts
5. **Admin Locale:** Set to English automatically for Filament routes
6. **Frontend Locale:** Remains German (from config/app.php)

---

## Rollback Plan

If issues occur:

1. Languages table can be dropped: `php artisan migrate:rollback --step=1`
2. Old hardcoded arrays can be restored in files
3. Component can revert to hardcoded languages array

---

## End of Migration Guide
