🌍 PopLang

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

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==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}`);

})();