🌍 PopLang

Popmundo script ekosistemi için merkezi çeviri ve dil modülü

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name        🌍 PopLang
// @name:tr     🌍 PopLang
// @name:en     🌍 PopLang
// @name:pt-BR  🌍 PopLang
// @namespace   popmundo.poplang
// @version     1.0
// @description Popmundo script ekosistemi için merkezi çeviri ve dil modülü
// @description:en Centralized language & translation module for the Popmundo script ecosystem
// @description:pt-BR Módulo central de idioma e tradução para o ecossistema de scripts Popmundo
// @author      luke-james-gibson
// @license     MIT
// @match       https://*.popmundo.com/*
// @run-at      document-start
// @grant       unsafeWindow
// ==/UserScript==

(function () {
'use strict';

// ─── COOKIE HELPER ─────────────────────────────────────────────────────────────
const CK = {
    get: k => { const m = document.cookie.match(new RegExp('(?:^|; )' + k + '=([^;]*)')); return m ? decodeURIComponent(m[1]) : null; },
    set: (k, v) => { document.cookie = `${k}=${encodeURIComponent(v)};domain=.popmundo.com;path=/;max-age=31536000`; },
};

// ─── DOM HELPER ────────────────────────────────────────────────────────────────
const mk = (tag, txt) => { const e = document.createElement(tag); if (txt != null) e.textContent = txt; return e; };
const mkB = (txt, fn) => Object.assign(mk('button', txt), { onclick: fn, type: 'button' });

// ─── LANGUAGE STATE ────────────────────────────────────────────────────────────
// 'PT' cookie değerini PT-BR'ye normalize et (eski sürüm uyumluluğu)
const _normLang = l => (l === 'PT' ? 'PT-BR' : l) || 'TR';
let _lang = _normLang(CK.get('ppm_lang'));

// ─── LOCALSTORAGE KEYS ─────────────────────────────────────────────────────────
const _SCRIPT_IDS = ['popcontrol', 'helper', 'social', 'social_mobile', 'depot', 'guide', 'route49'];
const _lsKey    = id => 'ppl_lc_' + id;  // yeni prefix
const _lsKeyOld = id => 'ppc_lc_' + id;  // eski prefix (migration için)

// ─── MIGRATION: ppc_lc_* → ppl_lc_* ───────────────────────────────────────────
_SCRIPT_IDS.forEach(id => {
    const nk = _lsKey(id), ok = _lsKeyOld(id);
    if (!localStorage.getItem(nk) && localStorage.getItem(ok)) {
        localStorage.setItem(nk, localStorage.getItem(ok));
        localStorage.removeItem(ok);
    }
});

// ─── CUSTOM OVERRIDES ──────────────────────────────────────────────────────────
// { scriptId: { key: value } } — localStorage'dan yüklenir
const _custom = {};
_SCRIPT_IDS.forEach(id => {
    try { const v = localStorage.getItem(_lsKey(id)); if (v) _custom[id] = JSON.parse(v); } catch {}
});

// ═══════════════════════════════════════════════════════════════════════════════
// STRING REGISTRY
// Format: { scriptId: { TR: {}, EN: {}, 'PT-BR': {} } }
// ═══════════════════════════════════════════════════════════════════════════════
const _S = {

// ─── POPLANG (kendi UI metinleri) ──────────────────────────────────────────────
poplang: {
    TR: {
        menuLabel:      '🌍 Dil Ayarları',
        title:          '🌍 PopLang — Dil Ayarları',
        langLabel:      'Dil Seçin',
        customizeBtn:   '🌍 Özel Dil',
        custTitle:      '🌍 Özel Dil (Customize)',
        custInfo:       'Tüm scriptlerin arayüzünü istediğiniz dile çevirebilirsiniz:',
        custStep1:      '📋 "Export JSON\'u Kopyala" düğmesine bas',
        custStep2:      'Google Gemini\'ye yapıştır, hedef dili belirt',
        custStep3:      'Dönen JSON\'u "Import" ile yükle',
        custStep4:      'Tüm scriptler anında yeni dile geçer',
        exportLabel:    '📤 Export',
        exportBtn:      '📋 Export JSON\'u Kopyala',
        exportEmpty:    '⚠️ Henüz kayıtlı string yok',
        exportCopied:   '✅ Kopyalandı!',
        importLabel:    '📥 Import',
        importPH:       '{ "depot": { "btnClose": "Schließen", ... }, ... }',
        importApply:    '✅ Uygula & Yenile',
        importPasteFirst: 'Önce çevrilmiş JSON yapıştırın.',
        importNoMatch:  'Eşleşen script anahtarı bulunamadı.',
        importApplied:  '✅ {n} script\'e uygulandı — yenileniyor...',
        importError:    '❌ Geçersiz JSON: ',
        resetBtn:       '🗑️ Varsayılan Dile Dön',
        resetConfirm:   'Tüm özel dil verisi kaldırılsın mı?',
        close:          'Kapat',
        hasCustomBadge: 'ÖZEL DİL AKTİF',
    },
    EN: {
        menuLabel:      '🌍 Language Settings',
        title:          '🌍 PopLang — Language Settings',
        langLabel:      'Select Language',
        customizeBtn:   '🌍 Custom Language',
        custTitle:      '🌍 Custom Language (Customize)',
        custInfo:       'You can translate all script interfaces to any language:',
        custStep1:      '📋 Click "Copy Export JSON"',
        custStep2:      'Paste into Google Gemini, specify the target language',
        custStep3:      'Load the returned JSON via Import',
        custStep4:      'All scripts switch to the new language instantly',
        exportLabel:    '📤 Export',
        exportBtn:      '📋 Copy Export JSON',
        exportEmpty:    '⚠️ No strings registered yet',
        exportCopied:   '✅ Copied!',
        importLabel:    '📥 Import',
        importPH:       '{ "depot": { "btnClose": "Schließen", ... }, ... }',
        importApply:    '✅ Apply & Reload',
        importPasteFirst: 'Paste translated JSON first.',
        importNoMatch:  'No matching script keys found.',
        importApplied:  '✅ Applied to {n} script(s) — reloading...',
        importError:    '❌ Invalid JSON: ',
        resetBtn:       '🗑️ Reset to Default Language',
        resetConfirm:   'Remove all custom language data and restore defaults?',
        close:          'Close',
        hasCustomBadge: 'CUSTOM LANG ACTIVE',
    },
    'PT-BR': {
        menuLabel:      '🌍 Configurações de Idioma',
        title:          '🌍 PopLang — Configurações de Idioma',
        langLabel:      'Selecionar Idioma',
        customizeBtn:   '🌍 Idioma Personalizado',
        custTitle:      '🌍 Idioma Personalizado (Customize)',
        custInfo:       'Você pode traduzir todos os scripts para qualquer idioma:',
        custStep1:      '📋 Clique em "Copiar JSON de Exportação"',
        custStep2:      'Cole no Google Gemini, especifique o idioma alvo',
        custStep3:      'Carregue o JSON retornado via Importar',
        custStep4:      'Todos os scripts mudam para o novo idioma instantaneamente',
        exportLabel:    '📤 Exportar',
        exportBtn:      '📋 Copiar JSON de Exportação',
        exportEmpty:    '⚠️ Nenhuma string registrada ainda',
        exportCopied:   '✅ Copiado!',
        importLabel:    '📥 Importar',
        importPH:       '{ "depot": { "btnClose": "Schließen", ... }, ... }',
        importApply:    '✅ Aplicar & Recarregar',
        importPasteFirst: 'Cole o JSON traduzido primeiro.',
        importNoMatch:  'Nenhuma chave de script correspondente encontrada.',
        importApplied:  '✅ Aplicado a {n} script(s) — recarregando...',
        importError:    '❌ JSON inválido: ',
        resetBtn:       '🗑️ Restaurar Idioma Padrão',
        resetConfirm:   'Remover todos os dados de idioma personalizado?',
        close:          'Fechar',
        hasCustomBadge: 'IDIOMA PERSONALIZADO ATIVO',
    },
},

// ─── POPCONTROL ────────────────────────────────────────────────────────────────
popcontrol: {
    TR: {
        posLabel:       'Konum',
        posBottom:      '⬇ Alt',
        posTop:         '⬆ Üst',
        posLeft:        '◀ Sol',
        posRight:       '▶ Sağ',
        langLabel:      'Dil',
        langOpen:       '🌍 Dil Ayarları',
        scripts:        'Scriptler',
        order:          'Sıra',
        orderHint:      'Barda butonları sürükleyip bırakarak sıralayabilirsiniz.',
        colorSettings:  'Renk Ayarları',
        colorBar:       'Bar Rengi',
        colorText:      'Yazı Rengi',
        shortcut:       'Paneli Aç/Kapat - Alt + P',
        charMenu:       'Karakter Menüsü',
        charMenuDesc:   'Gizle, favorilere al, sırala',
        manage:         'Yönet',
        collapse:       'Küçült',
        close:          'Kapat',
        settings:       'PopControl',
        resetAll:       '🔄 Tümünü Sıfırla',
        resetConfirm:   'Tüm gizleme ve favori ayarları sıfırlansın mı?',
        readTR:         'Beni Oku',
        readEN:         'Read Me',
        readPT:         'Leia-me',
    },
    EN: {
        posLabel:       'Position',
        posBottom:      '⬇ Bottom',
        posTop:         '⬆ Top',
        posLeft:        '◀ Left',
        posRight:       '▶ Right',
        langLabel:      'Language',
        langOpen:       '🌍 Language Settings',
        scripts:        'Scripts',
        order:          'Order',
        orderHint:      'Drag & drop buttons on the bar to reorder.',
        colorSettings:  'Color Settings',
        colorBar:       'Bar Color',
        colorText:      'Text Color',
        shortcut:       'Toggle Panel - Alt + P',
        charMenu:       'Character Menu',
        charMenuDesc:   'Hide, favorite & reorder items',
        manage:         'Manage',
        collapse:       'Collapse',
        close:          'Close',
        settings:       'PopControl',
        resetAll:       '🔄 Reset All',
        resetConfirm:   'Reset all hide/favorite settings?',
        readTR:         'Beni Oku',
        readEN:         'Read Me',
        readPT:         'Leia-me',
    },
    'PT-BR': {
        posLabel:       'Posição',
        posBottom:      '⬇ Baixo',
        posTop:         '⬆ Cima',
        posLeft:        '◀ Esquerda',
        posRight:       '▶ Direita',
        langLabel:      'Idioma',
        langOpen:       '🌍 Configurações de Idioma',
        scripts:        'Scripts',
        order:          'Ordem',
        orderHint:      'Arraste os botões na barra para reordenar.',
        colorSettings:  'Configurações de Cor',
        colorBar:       'Cor da Barra',
        colorText:      'Cor do Texto',
        shortcut:       'Alternar Painel - Alt + P',
        charMenu:       'Menu do Personagem',
        charMenuDesc:   'Ocultar, favoritar e reordenar',
        manage:         'Gerenciar',
        collapse:       'Recolher',
        close:          'Fechar',
        settings:       'PopControl',
        resetAll:       '🔄 Redefinir Tudo',
        resetConfirm:   'Redefinir todas as configurações?',
        readTR:         'Beni Oku',
        readEN:         'Read Me',
        readPT:         'Leia-me',
    },
},

// ─── DEPOT ─────────────────────────────────────────────────────────────────────
depot: {
    TR: {
        scanTitle:      '🔄 Otomatik Tarama',
        scanPersonal:   '👤 Kişisel Eşyalar',
        scanVehicles:   '🚗 Kişisel Araçlar',
        scanHousing:    '🏠 Evler',
        scanArtist:     '🎵 Sanatçı / Turne Aracı',
        scanWarn:       '⚠️ Tarama sırasında sekmeler otomatik değişir.',
        scanCancel:     'İptal',
        scanStart:      '▶ Taramayı Başlat',
        scanSelectOne:  'En az bir kategori seçin.',
        scanDone:       '✅ Tarama Tamamlandı',
        scanLocCount:   'Taranan Lokasyon:',
        scanItemCount:  'Toplam Eşya:',
        scanClose:      'Kapat',
        scanAskId:      'Karakter ID girin:',
        scanBadId:      'Geçerli ID girilmedi.',
        scanVehList:    '🔍 Araç listesi:',
        scanVehUnit:    'araç.',
        scanHouseList:  '🔍 Ev listesi:',
        scanHouseUnit:  'ev.',
        scanLogOk:      'çeşit.',
        scanLogEmpty:   'eşya yok.',
        scanStop:       '🛑 DURDUR',
        panelTitle:     '📦 Depot',
        dragHint:       '⠿ Sürükle',
        btnSettings:    'Panel Ayarları',
        btnBackup:      '📤 Scripti Yedekle',
        btnRestore:     '📥 Yedeği Yükle',
        catLabel:       'Kategori CSV (Kategori,Eşya):',
        catPlaceholder: 'Kategori,Eşya Adı',
        btnCatSave:     '💾 Kategorileri Güncelle',
        btnClearAll:    '⚠️ Tüm Veriyi Sıfırla',
        btnSavePage:    '📍 Sayfayı Kaydet',
        btnDelRecord:   '🗑️ Kaydı Sil',
        btnInventory:   'Envanter İşlemleri',
        btnAutoScan:    '🔄 OTOMATİK TARA',
        btnListManage:  '📋 ENVANTERLERİ LİSTELE / YÖNET',
        btnDetailed:    '📄 Detaylı CSV',
        btnStockCSV:    '📊 Stok & Fiyatlı CSV',
        btnImportCSV:   '📥 CSV Yükle',
        btnForumList:   '💰 Fiyat Kontrolü',
        csvEditorTitle: '📊 CSV Görüntüleyici / Düzenleyici',
        csvFormat:      'CSV Formatı:',
        csvFmtDetailed: 'Detaylı CSV (Tüm Bilgiler)',
        csvFmtSummary:  'Özet CSV (Stok & Fiyat)',
        csvFmtForum:    'Forum Formatı (Fiyat Listesi)',
        csvDataLabel:   'CSV Verisi (düzenlenebilir):',
        csvCopy:        '📋 Kopyala',
        csvDownload:    '💾 İndir',
        csvApply:       '✓ Değişiklikleri Uygula',
        csvUpdated:     'CSV verisi güncellendi.',
        csvCopied:      'CSV panoya kopyalandı!',
        csvNoPrice:     'Güncellenecek fiyat bulunamadı.',
        csvApplyUnsup:  'Bu format için değişiklik uygulama desteklenmiyor.',
        csvErrPrefix:   'Hata: ',
        pcTitle:        '💰 Fiyat Kontrolü',
        pcPasteLbl:     'Forum listesi veya oyun içi mesajı buraya yapıştırın:',
        pcPastePh:      'Snowglobe|1|100m\nveya yapışık:\nPhony Bad Teeth x1Snowball x1',
        pcProcess:      'İşle',
        pcAIBtn:        '🤖 AI ile Temizle',
        pcAIHint:       "Prompt kopyalandı! Gemini'ye açıldı.",
        pcClose:        'Kapat',
        pcFound:        'Sende:',
        pcNotFound:     'Envanterde yok',
        pcForumPrice:   'Girilen fiyat:',
        pcLastPrice:    'Son fiyat:',
        pcAddPrice:     'Fiyatı Ekle',
        pcAdded:        'Eklendi ✓',
        pcHistory:      'Geçmiş',
        pcNoHistory:    'Fiyat geçmişi yok.',
        pcNoInput:      'Lütfen bir liste girin.',
        pcResults:      'Sonuçlar:',
        pcApplyAll:     'Tümünü Uygula',
        pcApplied:      'fiyat uygulandı.',
        searchPH:       'Envanterde ara (Örn: Haiku)...',
        alertNoItems:   'Bu sayfada eşya bulunamadı.',
        alertDeleted:   'Silindi.',
        alertNotFound:  'Kayıt bulunamadı.',
        alertEnterData: 'Veri girin.',
        alertCatFmt:    "Format hatalı! 'Kategori,Eşya'",
        alertClearConf: 'Tüm DB sıfırlanacak!',
        alertRestored:  'Geri yüklendi.',
        alertInvalidFile: 'Geçersiz dosya.',
        listEmpty:      'Veritabanı boş.',
        btnDelAll:      'Tümünü Sil',
        btnDel:         'SİL',
        confirmDel:     'Bu kayıt silinsin mi?',
        noResults:      'Sonuç bulunamadı.',
        totalLabel:     'Toplam',
        lastScanLabel:  'Son tarama:',
        btnCopy:        '📋 Bu sonuçları kopyala',
        btnCopied:      '✅ Kopyalandı',
        noPriceLabel:   'Teklif ediniz',
        clsPersonal:    'Eşyalar',
        clsVehicle:     'Kişisel Araç',
        clsHousing:     'Ev',
        clsArtist:      'Sanatçı Aracı',
        minTooltip:     'Paneli aç',
        btnMinimize:    'Küçült',
        btnClose:       'Kapat',
        locVehicleFb:   'Kişisel Araç',
        locLocaleFb:    'Mekan',
        locCharFb:      'Karakter',
        locInvSuffix:   'Envanteri',
        locArtistFb:    'Grup Aracı',
        locUnknown:     'Bilinmeyen',
        variantDefault: 'Standart',
    },
    EN: {
        scanTitle:      '🔄 Auto Scan',
        scanPersonal:   '👤 Personal Items',
        scanVehicles:   '🚗 Personal Vehicles',
        scanHousing:    '🏠 Housing',
        scanArtist:     '🎵 Artist / Tour Vehicle',
        scanWarn:       '⚠️ Tabs will change automatically during scan.',
        scanCancel:     'Cancel',
        scanStart:      '▶ Start Scan',
        scanSelectOne:  'Select at least one category.',
        scanDone:       '✅ Scan Complete',
        scanLocCount:   'Scanned Locations:',
        scanItemCount:  'Total Items:',
        scanClose:      'Close',
        scanAskId:      'Enter Character ID:',
        scanBadId:      'No valid ID entered.',
        scanVehList:    '🔍 Vehicle list:',
        scanVehUnit:    'vehicles.',
        scanHouseList:  '🔍 Housing list:',
        scanHouseUnit:  'properties.',
        scanLogOk:      'types.',
        scanLogEmpty:   'no items.',
        scanStop:       '🛑 STOP',
        panelTitle:     '📦 Depot',
        dragHint:       '⠿ Drag',
        btnSettings:    'Panel Settings',
        btnBackup:      '📤 Backup Script',
        btnRestore:     '📥 Load Backup',
        catLabel:       'Category CSV (Category,Item):',
        catPlaceholder: 'Category,Item Name',
        btnCatSave:     '💾 Update Categories',
        btnClearAll:    '⚠️ Reset All Data',
        btnSavePage:    '📍 Save Page',
        btnDelRecord:   '🗑️ Delete Record',
        btnInventory:   'Inventory Actions',
        btnAutoScan:    '🔄 AUTO SCAN',
        btnListManage:  '📋 LIST / MANAGE INVENTORIES',
        btnDetailed:    '📄 Detailed CSV',
        btnStockCSV:    '📊 Stock & Priced CSV',
        btnImportCSV:   '📥 Import CSV',
        btnForumList:   '💰 Price Check',
        csvEditorTitle: '📊 CSV Viewer / Editor',
        csvFormat:      'CSV Format:',
        csvFmtDetailed: 'Detailed CSV (All Info)',
        csvFmtSummary:  'Summary CSV (Stock & Price)',
        csvFmtForum:    'Forum Format (Price List)',
        csvDataLabel:   'CSV Data (editable):',
        csvCopy:        '📋 Copy',
        csvDownload:    '💾 Download',
        csvApply:       '✓ Apply Changes',
        csvUpdated:     'CSV data updated.',
        csvCopied:      'CSV copied to clipboard!',
        csvNoPrice:     'No prices to update.',
        csvApplyUnsup:  'Applying changes not supported for this format.',
        csvErrPrefix:   'Error: ',
        pcTitle:        '💰 Price Check',
        pcPasteLbl:     'Paste forum list or in-game message here:',
        pcPastePh:      'Snowglobe|1|100m\nor concatenated:\nPhony Bad Teeth x1Snowball x1',
        pcProcess:      'Process',
        pcAIBtn:        '🤖 Clean with AI',
        pcAIHint:       'Prompt copied! Gemini opened.',
        pcClose:        'Close',
        pcFound:        'You have:',
        pcNotFound:     'Not in inventory',
        pcForumPrice:   'Listed price:',
        pcLastPrice:    'Last price:',
        pcAddPrice:     'Add Price',
        pcAdded:        'Added ✓',
        pcHistory:      'History',
        pcNoHistory:    'No price history.',
        pcNoInput:      'Please enter a list.',
        pcResults:      'Results:',
        pcApplyAll:     'Apply All',
        pcApplied:      'prices applied.',
        searchPH:       'Search inventory (e.g. Haiku)...',
        alertNoItems:   'No items found on this page.',
        alertDeleted:   'Deleted.',
        alertNotFound:  'Record not found.',
        alertEnterData: 'Enter data.',
        alertCatFmt:    "Invalid format! 'Category,Item'",
        alertClearConf: 'All data will be reset!',
        alertRestored:  'Restored.',
        alertInvalidFile: 'Invalid file.',
        listEmpty:      'Database empty.',
        btnDelAll:      'Delete All',
        btnDel:         'DEL',
        confirmDel:     'Delete this record?',
        noResults:      'No results found.',
        totalLabel:     'Total',
        lastScanLabel:  'Last scan:',
        btnCopy:        '📋 Copy these results',
        btnCopied:      '✅ Copied',
        noPriceLabel:   'Make an offer',
        clsPersonal:    'Items',
        clsVehicle:     'Personal Vehicle',
        clsHousing:     'Housing',
        clsArtist:      'Artist Vehicle',
        minTooltip:     'Open panel',
        btnMinimize:    'Minimize',
        btnClose:       'Close',
        locVehicleFb:   'Personal Vehicle',
        locLocaleFb:    'Locale',
        locCharFb:      'Character',
        locInvSuffix:   'Inventory',
        locArtistFb:    'Artist Vehicle',
        locUnknown:     'Unknown',
        variantDefault: 'Standard',
    },
    'PT-BR': {
        scanTitle:      '🔄 Varredura Automática',
        scanPersonal:   '👤 Itens Pessoais',
        scanVehicles:   '🚗 Veículos Pessoais',
        scanHousing:    '🏠 Moradias',
        scanArtist:     '🎵 Artista / Veículo de Turnê',
        scanWarn:       '⚠️ As abas mudarão automaticamente durante a varredura.',
        scanCancel:     'Cancelar',
        scanStart:      '▶ Iniciar Varredura',
        scanSelectOne:  'Selecione pelo menos uma categoria.',
        scanDone:       '✅ Varredura Concluída',
        scanLocCount:   'Locais Varridos:',
        scanItemCount:  'Total de Itens:',
        scanClose:      'Fechar',
        scanAskId:      'Digite o ID do Personagem:',
        scanBadId:      'Nenhum ID válido inserido.',
        scanVehList:    '🔍 Lista de veículos:',
        scanVehUnit:    'veículos.',
        scanHouseList:  '🔍 Lista de moradias:',
        scanHouseUnit:  'imóveis.',
        scanLogOk:      'tipos.',
        scanLogEmpty:   'sem itens.',
        scanStop:       '🛑 PARAR',
        panelTitle:     '📦 Depot',
        dragHint:       '⠿ Arrastar',
        btnSettings:    'Configurações do Painel',
        btnBackup:      '📤 Fazer Backup',
        btnRestore:     '📥 Carregar Backup',
        catLabel:       'CSV de Categorias (Categoria,Item):',
        catPlaceholder: 'Categoria,Nome do Item',
        btnCatSave:     '💾 Atualizar Categorias',
        btnClearAll:    '⚠️ Redefinir Todos os Dados',
        btnSavePage:    '📍 Salvar Página',
        btnDelRecord:   '🗑️ Excluir Registro',
        btnInventory:   'Ações de Inventário',
        btnAutoScan:    '🔄 VARREDURA AUTOMÁTICA',
        btnListManage:  '📋 LISTAR / GERENCIAR INVENTÁRIOS',
        btnDetailed:    '📄 CSV Detalhado',
        btnStockCSV:    '📊 CSV de Estoque & Preços',
        btnImportCSV:   '📥 Importar CSV',
        btnForumList:   '💰 Verificação de Preço',
        csvEditorTitle: '📊 Visualizador / Editor CSV',
        csvFormat:      'Formato CSV:',
        csvFmtDetailed: 'CSV Detalhado (Todas as Informações)',
        csvFmtSummary:  'CSV Resumido (Estoque & Preço)',
        csvFmtForum:    'Formato Fórum (Lista de Preços)',
        csvDataLabel:   'Dados CSV (editável):',
        csvCopy:        '📋 Copiar',
        csvDownload:    '💾 Baixar',
        csvApply:       '✓ Aplicar Alterações',
        csvUpdated:     'Dados CSV atualizados.',
        csvCopied:      'CSV copiado para a área de transferência!',
        csvNoPrice:     'Nenhum preço para atualizar.',
        csvApplyUnsup:  'Aplicação não suportada para este formato.',
        csvErrPrefix:   'Erro: ',
        pcTitle:        '💰 Verificação de Preço',
        pcPasteLbl:     'Cole a lista do fórum ou mensagem do jogo aqui:',
        pcPastePh:      'Snowglobe|1|100m\nou concatenado:\nPhony Bad Teeth x1Snowball x1',
        pcProcess:      'Processar',
        pcAIBtn:        '🤖 Limpar com IA',
        pcAIHint:       'Prompt copiado! Gemini aberto.',
        pcClose:        'Fechar',
        pcFound:        'Você tem:',
        pcNotFound:     'Não está no inventário',
        pcForumPrice:   'Preço listado:',
        pcLastPrice:    'Último preço:',
        pcAddPrice:     'Adicionar Preço',
        pcAdded:        'Adicionado ✓',
        pcHistory:      'Histórico',
        pcNoHistory:    'Sem histórico de preços.',
        pcNoInput:      'Por favor, insira uma lista.',
        pcResults:      'Resultados:',
        pcApplyAll:     'Aplicar Todos',
        pcApplied:      'preços aplicados.',
        searchPH:       'Buscar no inventário (ex: Haiku)...',
        alertNoItems:   'Nenhum item encontrado nesta página.',
        alertDeleted:   'Excluído.',
        alertNotFound:  'Registro não encontrado.',
        alertEnterData: 'Insira os dados.',
        alertCatFmt:    "Formato inválido! 'Categoria,Item'",
        alertClearConf: 'Todos os dados serão redefinidos!',
        alertRestored:  'Restaurado.',
        alertInvalidFile: 'Arquivo inválido.',
        listEmpty:      'Banco de dados vazio.',
        btnDelAll:      'Excluir Tudo',
        btnDel:         'EXC',
        confirmDel:     'Excluir este registro?',
        noResults:      'Nenhum resultado encontrado.',
        totalLabel:     'Total',
        lastScanLabel:  'Última varredura:',
        btnCopy:        '📋 Copiar estes resultados',
        btnCopied:      '✅ Copiado',
        noPriceLabel:   'Consultar preço',
        clsPersonal:    'Itens',
        clsVehicle:     'Veículo Pessoal',
        clsHousing:     'Moradia',
        clsArtist:      'Veículo do Artista',
        minTooltip:     'Abrir painel',
        btnMinimize:    'Minimizar',
        btnClose:       'Fechar',
        locVehicleFb:   'Veículo Pessoal',
        locLocaleFb:    'Local',
        locCharFb:      'Personagem',
        locInvSuffix:   'Inventário',
        locArtistFb:    'Veículo do Artista',
        locUnknown:     'Desconhecido',
        variantDefault: 'Padrão',
    },
},

// ─── DİĞER SCRIPTLERr — her refactor sırasında doldurulacak ───────────────────
helper:        { TR: {}, EN: {}, 'PT-BR': {} },
social:        { TR: {}, EN: {}, 'PT-BR': {} },
social_mobile: { TR: {}, EN: {}, 'PT-BR': {} },
guide:         { TR: {}, EN: {}, 'PT-BR': {} },
route49:       { TR: {}, EN: {}, 'PT-BR': {} },

}; // _S sonu

// ─── KISA ERİŞİM: PopLang kendi UI metinleri ───────────────────────────────────
const _ui = k => _S.poplang[_lang]?.[k] ?? _S.poplang.TR?.[k] ?? k;

// ═══════════════════════════════════════════════════════════════════════════════
// PUBLIC API
// ═══════════════════════════════════════════════════════════════════════════════
const API = {

    /**
     * Bir script için çevrilmiş string döner.
     * Öncelik: custom override > aktif dil > TR fallback > key kendisi
     * @param {string} scriptId  — 'depot', 'helper', vb.
     * @param {string} key
     * @returns {string}
     */
    get(scriptId, key) {
        if (_custom[scriptId]?.[key]) return _custom[scriptId][key];
        const dict = _S[scriptId];
        if (!dict) return key;
        return dict[_lang]?.[key] ?? dict.TR?.[key] ?? key;
    },

    /**
     * Bir script için tüm çeviri objesini döner.
     * Scriptler başlangıçta bunu çekip lokal cache'e alabilir:
     *   const S = window.PopLang?.getAll('depot') ?? {};
     *   const _t = k => S[k] ?? k;
     * @param {string} scriptId
     * @returns {object}
     */
    getAll(scriptId) {
        const dict = _S[scriptId];
        if (!dict) return {};
        // TR taban, üstüne aktif dil, üstüne custom
        const merged = Object.assign({}, dict.TR || {}, dict[_lang] || {}, _custom[scriptId] || {});
        return merged;
    },

    /** Aktif dil kodu: 'TR' | 'EN' | 'PT-BR' */
    getLang: () => _lang,

    /** Dili ayarla ve cookie'ye yaz (reload scriptlere kalır) */
    setLang(lang) {
        _lang = _normLang(lang);
        CK.set('ppm_lang', _lang);
    },

    /** Dil seçici + Customize panelini aç */
    openUI: () => _openUI(),

    /**
     * Script kendi TR stringlerini runtime'da ekleyebilir (opsiyonel, forward-compat)
     * Gerekirse custom stringlerini register etmek için kullanılır.
     */
    register(scriptId, tr, en, ptBr) {
        if (!_S[scriptId]) _S[scriptId] = { TR: {}, EN: {}, 'PT-BR': {} };
        if (tr)   Object.assign(_S[scriptId].TR,       tr);
        if (en)   Object.assign(_S[scriptId].EN,       en);
        if (ptBr) Object.assign(_S[scriptId]['PT-BR'], ptBr);
    },

    /** Tüm string registry'sini döner (Customize export için) */
    _getRegistry: () => _S,

    /** Versiyon */
    version: '1.0',
};

// Global erişim
window.PopLang = API;
if (typeof unsafeWindow !== 'undefined') unsafeWindow.PopLang = API;

// ═══════════════════════════════════════════════════════════════════════════════
// UI — DİL SEÇİCİ + CUSTOMIZE PANELİ
// ═══════════════════════════════════════════════════════════════════════════════

function _openUI() {
    document.getElementById('ppl-ov')?.remove();

    const hasCustom = Object.keys(_custom).length > 0;

    const ov = document.createElement('div');
    ov.id = 'ppl-ov';
    ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:100000;display:flex;align-items:flex-end;justify-content:center';

    const box = document.createElement('div');
    box.style.cssText = 'background:#fff;border-radius:16px 16px 0 0;padding:20px;width:100%;max-width:520px;max-height:85vh;overflow-y:auto;box-sizing:border-box;font-family:inherit';

    const mkH = t => {
        const d = document.createElement('div');
        d.textContent = t;
        d.style.cssText = 'font-size:10px;font-weight:700;color:#888;text-transform:uppercase;letter-spacing:.5px;margin:16px 0 6px';
        return d;
    };
    const mkHr = () => { const hr = document.createElement('hr'); hr.style.cssText = 'border:none;border-top:1px solid #eee;margin:12px 0'; return hr; };

    // ── Başlık ───────────────────────────────────────────────────────────────
    const hdr = document.createElement('div');
    hdr.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:16px';
    const title = document.createElement('div');
    title.style.cssText = 'display:flex;align-items:center;gap:8px';
    const titleText = document.createElement('span');
    titleText.textContent = _ui('title');
    titleText.style.cssText = 'font-weight:700;font-size:15px';
    title.appendChild(titleText);
    if (hasCustom) {
        const badge = document.createElement('span');
        badge.textContent = _ui('hasCustomBadge');
        badge.style.cssText = 'font-size:9px;font-weight:700;background:#f39c12;color:#fff;padding:2px 7px;border-radius:10px;letter-spacing:.3px';
        title.appendChild(badge);
    }
    const xBtn = mkB('✕', () => ov.remove());
    xBtn.style.cssText = 'background:none;border:none;font-size:20px;cursor:pointer;color:#aaa;padding:0';
    hdr.appendChild(title);
    hdr.appendChild(xBtn);
    box.appendChild(hdr);

    // ── Dil Seçimi ───────────────────────────────────────────────────────────
    box.appendChild(mkH(_ui('langLabel')));
    const langRow = document.createElement('div');
    langRow.style.cssText = 'display:flex;gap:6px;flex-wrap:wrap';

    const langs = [
        { code: 'TR', label: '🇹🇷 Türkçe' },
        { code: 'EN', label: '🇬🇧 English' },
        { code: 'PT-BR', label: '🇧🇷 Português' },
    ];

    langs.forEach(({ code, label }) => {
        const b = mkB(label, () => {
            API.setLang(code);
            ov.remove();
            location.reload();
        });
        const isActive = _lang === code;
        b.style.cssText = `flex:1;min-width:100px;padding:9px 6px;border-radius:8px;border:2px solid ${isActive ? '#6f42c1' : '#ddd'};background:${isActive ? '#6f42c1' : '#f8f9fa'};color:${isActive ? '#fff' : '#333'};font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;transition:all .15s`;
        langRow.appendChild(b);
    });

    // Customize butonu
    const custBtn = mkB(_ui('customizeBtn'), () => { ov.remove(); _openCustomize(); });
    const isCust = hasCustom;
    custBtn.style.cssText = `flex:1;min-width:100px;padding:9px 6px;border-radius:8px;border:2px solid ${isCust ? '#f39c12' : '#ddd'};background:${isCust ? '#fff8ee' : '#f8f9fa'};color:${isCust ? '#f39c12' : '#555'};font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;transition:all .15s`;
    langRow.appendChild(custBtn);

    box.appendChild(langRow);
    box.appendChild(mkHr());

    // ── Versiyon bilgisi ─────────────────────────────────────────────────────
    const vRow = document.createElement('div');
    vRow.style.cssText = 'text-align:center;font-size:11px;color:#aaa;padding:4px 0';
    vRow.textContent = `PopLang v${API.version}`;
    box.appendChild(vRow);

    ov.onclick = e => { if (e.target === ov) ov.remove(); };
    ov.appendChild(box);
    document.body.appendChild(ov);
}

// ─── CUSTOMIZE PANELİ ──────────────────────────────────────────────────────────
function _openCustomize() {
    document.getElementById('ppl-cust-ov')?.remove();

    const hasCustom = Object.keys(_custom).length > 0;

    const ov = document.createElement('div');
    ov.id = 'ppl-cust-ov';
    ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:100001;display:flex;align-items:center;justify-content:center;padding:16px;box-sizing:border-box';

    const box = document.createElement('div');
    box.style.cssText = 'background:#fff;border-radius:14px;padding:20px;width:100%;max-width:480px;max-height:92vh;overflow-y:auto;box-sizing:border-box;font-family:inherit';

    const mkHr = () => { const hr = document.createElement('hr'); hr.style.cssText = 'border:none;border-top:1px solid #eee;margin:14px 0'; return hr; };

    // Başlık
    const hdr = document.createElement('div');
    hdr.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:14px';
    const titleEl = document.createElement('span');
    titleEl.textContent = _ui('custTitle');
    titleEl.style.cssText = 'font-weight:700;font-size:15px';
    const xBtn = mkB('✕', () => ov.remove());
    xBtn.style.cssText = 'background:none;border:none;font-size:20px;cursor:pointer;color:#aaa;padding:0';
    hdr.append(titleEl, xBtn);
    box.appendChild(hdr);

    // Bilgi kutusu
    const info = document.createElement('div');
    info.style.cssText = 'background:#e8f4fd;border-radius:8px;padding:12px;margin-bottom:14px;font-size:12px;line-height:1.7;color:#1a5276';
    const steps = [
        `1. <strong>${_ui('custStep1')}</strong>`,
        `2. <a href="https://gemini.google.com" target="_blank" style="color:#1a5276;font-weight:600">Google Gemini</a>'ye yapıştır — ${_ui('custStep2').replace('Google Gemini\'ye yapıştır, ','')}`,
        `3. ${_ui('custStep3')}`,
        `4. ${_ui('custStep4')}`,
    ];
    info.innerHTML = `<div style="font-weight:600;margin-bottom:6px">${_ui('custInfo')}</div>` + steps.map(s => `<div style="margin-left:8px;margin-bottom:3px">${s}</div>`).join('');
    box.appendChild(info);

    // Export
    const expLabel = document.createElement('div');
    expLabel.textContent = _ui('exportLabel');
    expLabel.style.cssText = 'font-weight:700;font-size:13px;margin-bottom:6px';
    box.appendChild(expLabel);

    const expBtn = mkB(_ui('exportBtn'), () => {
        const exported = {};
        _SCRIPT_IDS.forEach(id => {
            const tr = _S[id]?.TR;
            if (tr && Object.keys(tr).length) exported[id] = tr;
        });
        if (!Object.keys(exported).length) {
            expBtn.textContent = _ui('exportEmpty');
            setTimeout(() => expBtn.textContent = _ui('exportBtn'), 2500);
            return;
        }
        const prompt = [
            'Translate the JSON values below to the target language.',
            'Rules: keep all emojis, {n}, %s and \\n exactly as-is.',
            'Return ONLY valid JSON, no markdown, no explanation.',
            '',
            'Target language: [ENTER TARGET LANGUAGE HERE]',
            '',
            JSON.stringify(exported, null, 2),
        ].join('\n');
        navigator.clipboard.writeText(prompt);
        expBtn.textContent = _ui('exportCopied');
        setTimeout(() => expBtn.textContent = _ui('exportBtn'), 2500);
    });
    expBtn.style.cssText = 'width:100%;padding:10px;background:#0d6efd;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit';
    box.appendChild(expBtn);
    box.appendChild(mkHr());

    // Import
    const impLabel = document.createElement('div');
    impLabel.textContent = _ui('importLabel');
    impLabel.style.cssText = 'font-weight:700;font-size:13px;margin-bottom:6px';
    box.appendChild(impLabel);

    const ta = document.createElement('textarea');
    ta.placeholder = _ui('importPH');
    ta.style.cssText = 'width:100%;height:150px;font-size:11px;font-family:monospace;padding:8px;border:1px solid #ddd;border-radius:6px;box-sizing:border-box;resize:vertical;margin-bottom:8px';
    box.appendChild(ta);

    const errDiv = document.createElement('div');
    errDiv.style.cssText = 'font-size:12px;min-height:18px;margin-bottom:8px';
    box.appendChild(errDiv);

    const applyBtn = mkB(_ui('importApply'), () => {
        try {
            const raw = ta.value.trim();
            if (!raw) { errDiv.style.color = '#dc3545'; errDiv.textContent = _ui('importPasteFirst'); return; }
            const data = JSON.parse(raw);
            let applied = 0;
            _SCRIPT_IDS.forEach(id => {
                if (data[id] && typeof data[id] === 'object') {
                    localStorage.setItem(_lsKey(id), JSON.stringify(data[id]));
                    Object.assign(_custom, { [id]: data[id] }); // runtime güncelle
                    applied++;
                }
            });
            if (!applied) { errDiv.style.color = '#dc3545'; errDiv.textContent = _ui('importNoMatch'); return; }
            errDiv.style.color = '#198754';
            errDiv.textContent = _ui('importApplied').replace('{n}', applied);
            setTimeout(() => { ov.remove(); location.reload(); }, 1200);
        } catch (err) {
            errDiv.style.color = '#dc3545';
            errDiv.textContent = _ui('importError') + err.message;
        }
    });
    applyBtn.style.cssText = 'width:100%;padding:10px;background:#198754;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit';
    box.appendChild(applyBtn);

    // Reset (sadece custom varsa göster)
    if (hasCustom) {
        box.appendChild(mkHr());
        const resetBtn = mkB(_ui('resetBtn'), () => {
            if (!confirm(_ui('resetConfirm'))) return;
            _SCRIPT_IDS.forEach(id => localStorage.removeItem(_lsKey(id)));
            ov.remove();
            location.reload();
        });
        resetBtn.style.cssText = 'width:100%;padding:8px;background:none;color:#dc3545;border:1px solid #dc3545;border-radius:8px;cursor:pointer;font-size:12px;font-family:inherit';
        box.appendChild(resetBtn);
    }

    ov.onclick = e => { if (e.target === ov) ov.remove(); };
    ov.appendChild(box);
    document.body.appendChild(ov);
}

// ═══════════════════════════════════════════════════════════════════════════════
// TOP VIP INJECTION — karakter sayfasında "🌍 Dil Ayarları" menü öğesi
// ═══════════════════════════════════════════════════════════════════════════════

function _injectTopVip() {
    if (!location.href.includes('/World/Popmundo.aspx/Character')) return;
    if (document.getElementById('mnu-ppl-lang')) return;

    const item = { id: 'mnu-ppl-lang', label: _ui('menuLabel'), fn: () => API.openUI() };

    // PopControl MenuManager varsa ona devret
    const pc = (typeof unsafeWindow !== 'undefined' && unsafeWindow.PopControl) || window.PopControl;
    if (pc?.MenuManager) {
        pc.MenuManager.registerMenu({
            id: 'top-vip',
            title: '⭐ TOP VIP ⭐',
            position: 'above-career',
            items: [item],
            collapsible: true,
        });
        return;
    }

    // Standalone injection (PopControl olmadan)
    let ul = document.querySelector('#top-vip-menu ul');
    if (!ul) {
        const ref = [...document.querySelectorAll('.menu h3')]
            .find(h => /Kariyer|Career|Carreira/i.test(h.textContent))?.closest('.menu');
        if (!ref) return; // menü hazır değil, retry bekleniyor

        const isCol = localStorage.getItem('top-vip-collapsed') === 'true';
        const h3 = document.createElement('h3');
        h3.textContent = '⭐ TOP VIP ⭐';
        h3.style.cssText = 'background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;text-align:center;padding:8px;margin:0;border-radius:6px 6px 0 0;cursor:pointer;box-shadow:0 2px 8px rgba(102,126,234,.3);user-select:none';

        ul = document.createElement('ul');
        ul.style.cssText = `margin:0;padding:8px 0;background:#f8f9fa;border:1px solid #e9ecef;border-top:none;border-radius:0 0 6px 6px;${isCol ? 'display:none;' : ''}`;

        h3.onclick = () => {
            const collapsed = ul.style.display === 'none';
            ul.style.display = collapsed ? '' : 'none';
            localStorage.setItem('top-vip-collapsed', String(!collapsed));
        };

        const menu = document.createElement('div');
        menu.id = 'top-vip-menu';
        menu.className = 'menu';
        menu.append(h3, ul);
        ref.before(menu);
        menu.after(Object.assign(document.createElement('div'), { style: 'height:12px' }));
    }

    if (document.getElementById(item.id)) return;
    const a = document.createElement('a');
    a.href = '#';
    a.id = item.id;
    a.textContent = item.label;
    a.style.cssText = 'color:#667eea;font-weight:600;text-decoration:none;display:block;padding:4px 12px;border-radius:4px;transition:all .2s';
    a.onmouseover = () => { a.style.background = '#667eea'; a.style.color = '#fff'; };
    a.onmouseout  = () => { a.style.background = '';        a.style.color = '#667eea'; };
    a.onclick = e => { e.preventDefault(); item.fn(); };

    const li = document.createElement('li');
    li.style.margin = '2px 0';
    li.appendChild(a);
    ul.appendChild(li);
}

// ═══════════════════════════════════════════════════════════════════════════════
// POPCONTROL ENTEGRASYONU
// PopControl settings panelindeki Dil bölümüne "🌍 Dil Ayarları" butonu enjekte
// eder. PopControl settings paneli her açıldığında yeniden render edildiğinden
// MutationObserver ile izliyoruz.
// ═══════════════════════════════════════════════════════════════════════════════

function _patchPopControl() {
    // PopControl'ün settings overlay'ini izle
    const obs = new MutationObserver(() => {
        const box = document.querySelector('#ppc-ov > div');
        if (!box) return;
        if (box.dataset.pplPatched) return;
        box.dataset.pplPatched = '1';

        // "Dil" başlığını bul (STR.langLabel içeren div)
        const langSection = [...box.querySelectorAll('div')]
            .find(el => el.textContent.trim() === 'Dil' || el.textContent.trim() === 'Language' || el.textContent.trim() === 'Idioma');
        if (!langSection) return;

        // Mevcut dil pill row'unu bul (hemen altındaki sibling)
        const pillRow = langSection.nextElementSibling;
        if (!pillRow) return;

        // Pill row'daki "Customize" butonunu PopLang'e yönlendir
        pillRow.querySelectorAll('button').forEach(btn => {
            const txt = btn.textContent.trim();
            // Customize butonunu PopLang'e yönlendir
            if (txt.includes('Customize') || txt.includes('🌍')) {
                btn.onclick = () => { document.getElementById('ppc-ov')?.remove(); API.openUI(); };
            }
            // Dil butonlarını PopLang üzerinden uygula (cookie + reload zaten yapıyor, dokunmaya gerek yok)
        });
    });
    obs.observe(document.body, { childList: true, subtree: true });
}

// ═══════════════════════════════════════════════════════════════════════════════
// INITIALIZATION
// ═══════════════════════════════════════════════════════════════════════════════

const _domReady = fn => {
    if (document.readyState !== 'loading') fn();
    else document.addEventListener('DOMContentLoaded', fn, { once: true });
};

// Karakter menüsüne inject — retry mekanizması ile
let _injRetry = 0;
function _tryInject() {
    _injectTopVip();
    // Menü henüz hazır değilse kısa süre içinde tekrar dene
    if (!document.getElementById('mnu-ppl-lang') && _injRetry < 20) {
        _injRetry++;
        setTimeout(_tryInject, 300);
    }
}

_domReady(() => {
    // PopControl entegrasyonu
    _patchPopControl();

    // PopControlReady event'i dinle (PopControl yüklüyse MenuManager'a inject)
    document.addEventListener('PopControlReady', () => {
        setTimeout(_tryInject, 100);
    }, { once: true });

    // PopControl yoksa veya daha yavaşsa direkt inject dene
    _tryInject();

    // SPA gezintisi — karakter sayfasına her gidişte tekrar inject et
    let _lastUrl = location.href;
    new MutationObserver(() => {
        if (location.href === _lastUrl) return;
        _lastUrl = location.href;
        _injRetry = 0;
        setTimeout(_tryInject, 400);
    }).observe(document.body, { childList: true, subtree: false });
});

console.log(`[PopLang] v${API.version} hazır — dil: ${_lang}`);

})();