// i18n.jsx — Gridome locale infrastructure
// useLocale() → { locale, setLocale, t }
// t(key, params?) — flat dot-notation keys, {placeholder} interpolation
// Default locale: 'pl'. Toggle persists to localStorage.

const STRINGS = {
  pl: {
    // ── Nav ──────────────────────────────────────────────────────────────
    'nav.home': 'Home',
    'nav.plan': 'Plan',
    'nav.forecast': 'Prognoza',
    'nav.optimizer': 'Optimizer',
    'nav.battery': 'Bateria',
    'nav.ev': 'Auto',
    'nav.sessions': 'Sessions',
    'nav.devices': 'Devices',
    'nav.settings': 'Ustawienia',

    // ── Status bar ────────────────────────────────────────────────────────
    'status.bar': 'Pstryk G14 · Warszawa · Bezpieczne · 7 urządzeń',
    'status.safeMode': 'SAFE MODE',
    'status.live': 'Na żywo',

    // ── Tier names ────────────────────────────────────────────────────────
    'tier.cheap': 'Tanio',
    'tier.standard': 'Średnio',
    'tier.peak': 'Drogo',
    'tier.high': 'Szczyt',
    'tier.priceNow': 'Cena teraz',

    // ── Home ──────────────────────────────────────────────────────────────
    'home.savedToday': 'Zaoszczędzono dziś',
    'home.historyLink': '→ Historia',
    'home.forecastLink': '→ Prognoza 48h',
    'home.batteryLink': '→ Bateria',
    'home.flowTitle': 'Przepływ energii · teraz',
    'home.flowIn': 'Wejście',
    'home.flowOut': 'Wyjście',
    'home.socLabel': 'SOC baterii',
    'home.export': 'Eksport',
    'home.import': 'Import',
    'home.priceStrip': 'Ceny · dziś 24h',
    'home.flow.title': 'Przepływ energii · teraz',
    'home.flow.solar': 'Słońce',
    'home.flow.battery': 'Bateria',
    'home.flow.home': 'Dom',
    'home.flow.grid': 'Sieć',
    'home.flow.ev': 'Auto',
    'home.flow.ev.charging': 'Ładowanie',
    'home.flow.ev.idle': 'Gotowy',
    'home.flow.ev.offline': 'Odłączone',
    'home.flow.solar.night': 'noc',
    'home.flow.solar.generating': 'generowanie',
    'home.flow.grid.import': 'import',
    'home.flow.grid.export': 'eksport',
    'home.flow.home.usage': 'zużycie',
    'home.flow.battery.charging': 'ładowanie',
    'home.flow.battery.discharging': 'rozładowanie',
    'home.flow.car.charging': 'ładowanie',
    'home.flow.car.ready': 'gotowy',
    'home.flow.car.offline': 'odłączone',
    'home.flow.summary.import': 'Import · {kw} kW',
    'home.flow.summary.export': 'Eksport · {kw} kW',
    'home.strip48h': '→ Prognoza 48h',
    'home.laterBtn': 'Później',
    'home.blockedBtn': 'Zablokowane',
    'home.thisMonth': 'Ten miesiąc',
    'home.pvSelfUse': 'Autokonsumpcja PV',
    'home.battArbitrage': 'Arbitraż baterii',
    'home.shiftedLoads': 'Przesunięte ładunki',
    'home.exportRevenue': 'Przychód z eksportu',
    'home.cta.midday': 'Uruchom zmywarkę',
    'home.cta.evening': 'Wstrzymaj suszarkę',
    'home.cta.overnight': 'Zgodnie z planem ✓',
    'home.cta.morning': 'Rozumiem',
    'home.sub.midday': 'Najlepiej teraz — bez różnicy jutro rano',
    'home.sub.evening': 'Po 23:00 będzie o 6.80 zł taniej',
    'home.sub.overnight': 'Škoda ładuje się do 06:30',
    'home.sub.morning': 'Pranie uruchom po 11:00',
    'home.rec.midday': 'Świetny czas na prąd — cena pozostanie niska jeszcze ok. 90 min, bateria ładuje się na wieczór.',
    'home.rec.evening': 'Godzina szczytowa. Ceny spadną gwałtownie po 23:00 — bateria zasila dom, żeby nie kupować.',
    'home.rec.overnight': 'Najtańsze okienko doby. Škoda ładuje się, bateria nabiera energię z sieci na rano.',
    'home.rec.morning': 'Ceny rosną. Śniadanie teraz — duże ładunki poczekają do 11:00, gdy wejdzie PV.',
    'home.croissant.midday': 'Słońce pracuje — {saved} zł zaoszczędzone do teraz, czyli {emoji} {food}',
    'home.croissant.evening': 'Mimo szczytu bateria chroni dom — {saved} zł zaoszczędzone, czyli {emoji} {food}',
    'home.croissant.overnight': 'Tej nocy: ładowanie Škodie za 29 gr — oszczędność już {saved} zł, czyli {emoji} {food}',
    'home.croissant.morning': 'Dobry ranek — {saved} zł oszczędności od rana, czyli {emoji} {food}',

    // ── Plan ──────────────────────────────────────────────────────────────
    'plan.header': 'Plan · dotknij dowolnej godziny',
    'plan.cheapWindow': 'Najtańsze okienko 3h',
    'plan.autoLink': 'Wolisz automatycznie? → Optimizer',
    'plan.scheduleBtn': 'Zaplanuj na {hour}:00',
    'plan.cheaper': '{diff} gr taniej niż teraz',
    'plan.moreExp': '{diff} gr drożej niż teraz',
    'plan.same': 'Tyle samo co teraz',
    'plan.croissant': 'Twoja kolejka zaoszczędzi {total} zł, czyli {emoji} {food}.',
    'plan.queueTitle': 'Kolejka zleceń',
    'plan.historyLink': '→ Historia',
    'plan.emptyQueue': 'Dotknij dowolnej godziny poniżej 50 gr, żeby dodać pierwsze zlecenie.',
    'plan.savingsTitle': 'Ta kolejka zaoszczędzi',
    'plan.totalRow': 'Łącznie',
    'plan.shiftedRow': 'Przesunięte',
    'plan.co2Row': 'CO₂ mniej',
    'plan.totalSub': 'vs. ładowanie o szczycie',
    'plan.shiftedSub': 'z drogich na tanie okienka',
    'plan.co2Sub': 'szacunkowo (220 g/kWh PL)',
    'plan.optimizerNote': 'Plan jest nadpisywany przez Optimizer co 15 min.',
    'plan.load.dishwasher': 'Zmywarka',
    'plan.load.washer': 'Pralka',
    'plan.load.car': 'Ładowanie auta',

    // ── Forecast ──────────────────────────────────────────────────────────
    'forecast.cheapTomorrow': 'Najtańsze okienko jutro:',
    'forecast.pvTitle': 'Prognoza PV · generacja brutto',
    'forecast.pvDisclaimer': 'Nie uwzględnia zużycia domu ani baterii — to surowa moc z paneli.',
    'forecast.pvToday': 'Dziś',
    'forecast.pvTomorrow': 'Jutro',
    'forecast.tariffTitle': 'Ceny energii · 48h',
    'forecast.tariffSub': 'Pstryk G14 dynamiczna · ceny all-in z DSO + podatkami',
    'forecast.consumptionTitle': 'Prognoza zużycia domu',
    'forecast.consumptionBody': 'Prognoza zużycia domu wymaga modelu ML (NILM ExtraTreesClassifier + FHMM). W budowie — Phase 2 roadmapy.',
    'forecast.tomorrowLabel': 'jutro →',
    'forecast.croissantSuffix': 'wystarczy na pranie + zmywarkę, czyli',
    'forecast.pvAvailable': 'PV jutro prognozowane na',
    'forecast.min': 'min',
    'forecast.avg': 'śr',
    'forecast.max': 'max',
    'forecast.tierCheap': 'Tanio',
    'forecast.tierStd': 'Średnio',
    'forecast.tierPeak': 'Drogo',
    'forecast.tierHigh': 'Szczyt',
    'forecast.axisNow': 'Teraz',
    'forecast.axisTomorrow': 'jutro',

    // ── Weather & Solar / Grid Health ─────────────────────────────────────
    'weather.title': 'Pogoda i PV',
    'weather.now': 'Teraz',
    'weather.today': 'Dziś',
    'weather.tomorrow': 'Jutro',
    'weather.irradiance': 'Nasłonecznienie',
    'weather.pvToday': 'PV dziś',
    'weather.pvTomorrow': 'PV jutro',
    'weather.peak': 'Szczyt {w} W',
    'weather.mostlyClear': 'Przeważnie pogodnie · {pct}%',
    'weather.overcast': 'Zachmurzenie',
    'weather.dry': 'Sucho',
    'weather.source': 'Open-Meteo + Forecast.Solar · snapshot 15 min',
    'grid.title': 'Stan sieci PL',
    'grid.weekday': 'Dzień pracujący',
    'grid.weekend': 'Weekend',
    'grid.carbonIntensity': 'Intensywność CO₂',
    'grid.renewable': 'Udział OZE',
    'grid.load': 'Obciążenie sieci',
    'grid.wind': 'Wiatr',
    'grid.solar': 'Fotowoltaika',
    'grid.co2Low': 'Niska',
    'grid.co2Mid': 'Średnia',
    'grid.co2High': 'Wysoka',
    'grid.genMix': 'Mix generacji',
    'grid.mix.solar': 'PV',
    'grid.mix.wind': 'Wiatr',
    'grid.mix.gas': 'Gaz',
    'grid.mix.biomass': 'Biomasa',
    'grid.mix.oil': 'Olej',
    'grid.mix.hydro': 'Woda',
    'grid.mix.other': 'Węgiel i inne',
    'grid.source': 'ENTSO-E · {time}',

    // ── Optimizer ────────────────────────────────────────────────────────
    'optimizer.readOnly': 'Read-only — Zapisy do urządzeń zablokowane. Plan wygenerowany przez optimizer_orchestrator.py',
    'optimizer.activeWhen': 'aktywny gdy',
    'optimizer.statusOptimal': 'Optimal',
    'optimizer.lastPlan': 'ostatni plan',
    'optimizer.heroDesc': 'Tej nocy Gridome przesunie {kwh} kWh do tanich okienek — oszczędność {saved} zł, czyli {emoji} {food}.',
    'optimizer.shifted': 'Przesunięte',
    'optimizer.savings': 'Oszczędność',
    'optimizer.co2': 'CO₂ mniej',
    'optimizer.timeline': 'Harmonogram · następne 24h',
    'optimizer.fromNow': 'od {hour}:00',
    'optimizer.socTitle': 'Projekcja SOC baterii · następne 24h',
    'optimizer.socNote': 'Wyliczone przez battery_orchestrator.py na podstawie decyzji optymalizatora.',
    'optimizer.bufferLabel': 'bufor 80%',
    'optimizer.reserveLabel': 'rezerwa 20%',
    'optimizer.rowPrices': 'Ceny',
    'optimizer.rowPV': 'PV',
    'optimizer.rowBattery': 'Bateria',
    'optimizer.rowEV': 'Auto',
    'optimizer.rowHP': 'Pompa ciepła',
    'optimizer.rowExport': 'Eksport',
    'optimizer.decEV': 'Ładowanie {start}–{end} · {price} gr/kWh średnio',
    'optimizer.decBattery': 'Ładowanie z sieci {start}–{end}, rozładowanie {peak}',
    'optimizer.decHP': 'SG-Ready ON: 10:00–14:00 (surplus PV + tania cena)',
    'optimizer.decExport': 'Priorytet eksportu: 11:00–13:00 (bateria pełna)',
    'optimizer.readOnlyNote': 'Zapisy do urządzeń zablokowane do czasu aktywacji live-write.',
    'forecast.savings': 'oszczędności',
    'opt.readonlyText': 'Zapisy do urządzeń zablokowane. Plan wygenerowany przez',
    'opt.card.ev': 'Auto (Škoda)',
    'opt.card.evDecOvernight': 'Ładowanie 01:00–06:00 · 29 gr/kWh średnio',
    'opt.card.evDecOther': 'Zaplanowane 01:00–05:00 jutro · tanie okienko',
    'opt.card.battery': 'Bateria',
    'opt.card.batteryDec': 'Ładowanie z sieci 02:00–05:00, rozładowanie 17:00–20:00',
    'opt.card.hp': 'Pompa ciepła',
    'opt.card.feedin': 'Eksport',
    'food.donuts': '≈ {count} pączków',
    'food.pierogi': '≈ {count} pierogów',
    'food.croissants': '≈ {count} croissantów',
    'food.coffees': '≈ {count} kaw',

    // ── Battery ───────────────────────────────────────────────────────────
    'battery.readOnly': 'Read-only — Modbus writes locked until',
    'battery.readOnlyNote': 'on the pilot device. Viewing live state from pilot2',
    'battery.status.charging': 'Ładowanie',
    'battery.status.discharging': 'Rozładowanie',
    'battery.status.idle': 'Gotowość',
    'battery.powerLabel': 'Moc',
    'battery.voltageLabel': 'Napięcie',
    'battery.energyLabel': 'Energia',
    'battery.zonesTitle': 'Strefy priorytetów',
    'battery.readOnlyBadge': 'Read-only',
    'battery.bufferZone': 'Bufor EV',
    'battery.bufferDesc': 'Powyżej {pct}% bateria ładuje samochód z pierwszeństwem przed siecią.',
    'battery.mixedZone': 'Strefa mieszana',
    'battery.mixedDesc': 'Między {low}% a {high}% — optymalizacja kosztów.',
    'battery.reserveZone': 'Rezerwa domowa',
    'battery.reserveDesc': 'Poniżej {pct}% bateria zasila tylko dom, nie EV.',
    'battery.strategyNote': 'Limity SOC zostaną odblokowane kiedy Gridome dostanie pozwolenie na zapis Modbus. Obecna strategia:',
    'battery.strategyName': 'self-use + peak-shaving',
    'battery.historyTitle': 'Historia SOC · dziś',
    'battery.historyBadge': 'Wymaga backendu',
    'battery.historyNote': 'Godzinowa historia SOC nie jest jeszcze zapisywana.',
    'battery.gridChargeTitle': 'Ładowanie z sieci',
    'battery.priceTreshold': 'Próg cenowy',
    'battery.targetSoc': 'Docelowy SOC',
    'battery.tariffWindow': 'Okienko G14',
    'battery.gridLockedNote': 'Ładowanie z sieci zablokowane do czasu aktywacji live-write.',
    'battery.pendingPerUnit': 'Per-unit SOC · pending',

    // ── Auto ─────────────────────────────────────────────────────────────
    'auto.status.available': 'Gotowy',
    'auto.status.charging': 'Ładowanie',
    'auto.status.suspended': 'Czeka',
    'auto.status.finishing': 'Ukończono',
    'auto.sessionEnergy': 'Energia',
    'auto.sessionTime': 'Czas',
    'auto.sessionEnd': 'Koniec ~',
    'auto.sessionAvgPrice': 'Śr. cena',
    'auto.croissant': 'Tej nocy: {kwh} kWh, czyli ≈ {km} km zasięgu, koszt {cost} zł',
    'auto.range': 'Zasięg',
    'auto.sessionCost': 'Koszt sesji',
    'auto.chargePlan': 'Plan ładowania na noc',
    'auto.chargeNow': 'Ładuj teraz',
    'auto.editPlan': 'Edytuj plan',
    'auto.smartTitle': 'Smart Charging · profil OCPP',
    'auto.loadBalanceTitle': 'Obciążenie obwodu · 25A / 3-faz',
    'auto.weeklyTitle': 'Plan tygodniowy · charge_plans.py',
    'auto.readOnlyBadge': 'Tylko odczyt',
    'auto.status.done': 'Naładowane ✓',
    'auto.status.waiting': 'Czeka na tanią okienkę',
    'auto.status.unplugged': 'Odłączone',
    'auto.doneBy': 'Gotowe do',
    'auto.headroomNote': 'ev_scheduler.py dusi ładowarkę przy',

    // ── Sessions ──────────────────────────────────────────────────────────
    'sessions.backendNotice': 'Stub — Historii sesji brak w backendzie.',
    'sessions.backendPath': 'ev_sessions.json',
    'sessions.backendPattern': 'energy_accum.json',
    'sessions.backendApi': 'GET /api/sessions',
    'sessions.periodMonth': 'Ten miesiąc',
    'sessions.periodYear': 'Ten rok',
    'sessions.periodAll': 'Wszystko',
    'sessions.heroTitle': 'Łączny koszt',
    'sessions.periodApril': 'kwiecień 2026',
    'sessions.periodAllTime': 'wszystko',
    'sessions.sparklineTitle': 'Koszt dzienny',
    'sessions.emptyState': 'Pierwsza sesja czeka.',
    'sessions.colDate': 'Data',
    'sessions.colEnergy': 'Energia',
    'sessions.colTime': 'Czas',
    'sessions.colAvgPrice': 'Śr. cena',
    'sessions.colSolar': 'Solar',
    'sessions.colCost': 'Koszt',
    'sessions.modalKwCurve': 'Moc ładowania (kW) · sesja',
    'sessions.modalTariffCurve': 'Cena taryfy w trakcie sesji (gr/kWh)',
    'sessions.modalAmpProfile': 'Profil Smart Charging (A)',
    'sessions.peakComparison': 'Ładowanie o szczycie (142 gr) kosztowałoby',
    'sessions.optimizerSaved': 'Optimizer zaoszczędził',
    'sessions.csv': 'CSV',
    'sessions.savingsCompare': '{period}: ładowanie po szczycie kosztowałoby {peakCost} zł — Gridome zaoszczędził {saved} zł, czyli {food}.',
    'sessions.noData': 'Brak danych',
    'sessions.backendNeed': 'Potrzebny',
    'sessions.backendPatternLabel': 'wzór',
    'auto.now': 'Teraz',
    'auto.profileDesc': 'Profil ładowania z OCPP Central System. Prąd (A) zależy od okienka taryfowego i dostępnego headroom.',
    'auto.profile.waiting': 'Smart · czeka na tanią okienkę (23:00)',
    'auto.profile.charging': 'TxDefaultProfile · 32A · okienko 01:00–06:00',
    'auto.profile.done': 'TxDefaultProfile · zakończony',
    'auto.labelEnergy': 'Energia',
    'auto.labelTime': 'Czas',
    'auto.day.0': 'Pn', 'auto.day.1': 'Wt', 'auto.day.2': 'Śr',
    'auto.day.3': 'Cz', 'auto.day.4': 'Pt', 'auto.day.5': 'So', 'auto.day.6': 'Nd',

    // ── Tweaks ────────────────────────────────────────────────────────────
    'tweaks.title': 'Ustawienia podglądu',
    'tweaks.scenario': 'Scenariusz',
    'tweaks.screen': 'Ekran',
    'tweaks.state': 'Stan',
    'tweaks.safeMode': 'Safe mode',
    'tweaks.locale': 'Język',

    // ── Days ──────────────────────────────────────────────────────────────
    'days.0': 'nd', 'days.1': 'pn', 'days.2': 'wt',
    'days.3': 'śr', 'days.4': 'cz', 'days.5': 'pt', 'days.6': 'sb',
    'days.full.0': 'Niedziela', 'days.full.1': 'Poniedziałek', 'days.full.2': 'Wtorek',
    'days.full.3': 'Środa', 'days.full.4': 'Czwartek', 'days.full.5': 'Piątek', 'days.full.6': 'Sobota',
    // ── Settings ──────────────────────────────────────────────────────────────
    'settings.general': 'Ogólne',
    'settings.siteTitle': 'Nazwa instalacji',
    'settings.siteTitleDesc': 'Wyświetlana w nagłówku i powiadomieniach',
    'settings.homeAddress': 'Adres instalacji',
    'settings.homeAddressDesc': 'Używany do prognozy PV i wykrywania operatora',
    'settings.language': 'Język interfejsu',
    'settings.currency': 'Waluta',
    'settings.currencyDesc': 'Niezależna od języka — ceny zawsze w PLN',
    'settings.devices': 'Urządzenia',
    'settings.addDevice': 'Dodaj urządzenie',
    'settings.pickDeviceType': 'Wybierz typ urządzenia',
    'settings.deviceShelly': 'Shelly',
    'settings.deviceOCPP': 'Ładowarka OCPP',
    'settings.deviceInverter': 'Falownik',
    'settings.deviceMeter': 'Licznik',
    'settings.deviceHomewiz': 'HomeWizard',
    'settings.deviceTasmota': 'Tasmota',
    'settings.online': 'online',
    'settings.offline': 'offline',
    'settings.users': 'Użytkownicy',
    'settings.addViewer': 'Dodaj widza',
    'settings.lastSeen': 'Ostatnio widziano',
    'settings.revoke': 'Odwołaj dostęp',
    'settings.confirmRevoke': 'Na pewno odwołać?',
    'settings.username': 'Nazwa użytkownika',
    'settings.viewerNote': 'Widz może tylko odczytywać dane.',
    'settings.createViewer': 'Utwórz konto',
    'settings.tariffs': 'Taryfy',
    'settings.tariffProvider': 'Dostawca cen dynamicznych',
    'settings.dso': 'Operator dystrybucyjny',
    'settings.dsoDesc': 'Wykryto automatycznie na podstawie lokalizacji',
    'settings.formula': 'Wzór ceny all-in',
    'settings.formulaDesc': 'Cena = (TGE spot + opłata dystrybucyjna + podatki) × 1.23',
    'settings.operation': 'Cele pracy',
    'settings.safeMode': 'Safe mode — blokada sprzętowa',
    'settings.safeModeDesc': 'Blokuje zapisy Modbus, OCPP i relay. Odczyty i metryki działają normalnie.',
    'settings.batteryLive': 'Zapis do baterii (GRIDOME_BATTERY_LIVE)',
    'settings.batteryLiveDesc': 'Odblokuj granice SOC i ładowanie z sieci',
    'settings.active': 'Aktywne',
    'settings.locked': 'Zablokowane',
    'settings.telemetry': 'Telemetria',
    'settings.telemetryOff': 'Wyłączona',
    'settings.telemetryAnon': 'Anonimowa',
    'settings.telemetryFull': 'Pełna',
    'settings.system': 'System',
    'settings.version': 'Wersja',
    'settings.backup': 'Kopia zapasowa',
    'settings.backupDesc': 'Eksportuj lub importuj konfigurację',
    'settings.backupBtn': 'Pobierz kopię',
    'settings.restoreBtn': 'Przywróć',
    'settings.logs': 'Logi',
    'settings.viewLogs': 'Pokaż logi',
    'settings.versionFooter': 'Gridome v0.9.12 · uruchomiony od 12 dni · oszczędności tego miesiąca: 221 zł',
    'settings.back': 'Wstecz',
    'settings.deviceName': 'Nazwa urządzenia',
    'settings.ipAddress': 'Adres IP',
    'settings.channel': 'Kanał',
    'settings.addDeviceConfirm': 'Dodaj urządzenie',
    'settings.cancel': 'Anuluj',
    'settings.readOnlyNote': 'Zapis wyłączony w prototypie — aktywny po portowaniu do React.',
  },

  en: {
    // ── Nav ──────────────────────────────────────────────────────────────
    'nav.home': 'Home',
    'nav.plan': 'Plan',
    'nav.forecast': 'Forecast',
    'nav.optimizer': 'Optimizer',
    'nav.battery': 'Battery',
    'nav.ev': 'Car',
    'nav.sessions': 'Sessions',
    'nav.devices': 'Devices',
    'nav.settings': 'Settings',

    // ── Status bar ────────────────────────────────────────────────────────
    'status.bar': 'Pstryk G14 · Warsaw · Safe · 7 devices',
    'status.safeMode': 'SAFE MODE',
    'status.live': 'Live',

    // ── Tier names ────────────────────────────────────────────────────────
    'tier.cheap': 'Cheap',
    'tier.standard': 'Medium',
    'tier.peak': 'Expensive',
    'tier.high': 'Peak',
    'tier.priceNow': 'Price now',

    // ── Home ──────────────────────────────────────────────────────────────
    'home.savedToday': 'Saved today',
    'home.historyLink': '→ History',
    'home.forecastLink': '→ 48h Forecast',
    'home.batteryLink': '→ Battery',
    'home.flowTitle': 'Energy flow · now',
    'home.flowIn': 'In',
    'home.flowOut': 'Out',
    'home.socLabel': 'Battery SOC',
    'home.export': 'Export',
    'home.import': 'Import',
    'home.priceStrip': 'Prices · today 24h',
    'home.flow.title': 'Power flow · now',
    'home.flow.solar': 'Solar',
    'home.flow.battery': 'Battery',
    'home.flow.home': 'Home',
    'home.flow.grid': 'Grid',
    'home.flow.ev': 'Car',
    'home.flow.ev.charging': 'Charging',
    'home.flow.ev.idle': 'Ready',
    'home.flow.ev.offline': 'Disconnected',
    'home.flow.solar.night': 'night',
    'home.flow.solar.generating': 'generating',
    'home.flow.grid.import': 'import',
    'home.flow.grid.export': 'export',
    'home.flow.home.usage': 'usage',
    'home.flow.battery.charging': 'charging',
    'home.flow.battery.discharging': 'discharging',
    'home.flow.car.charging': 'charging',
    'home.flow.car.ready': 'ready',
    'home.flow.car.offline': 'disconnected',
    'home.flow.summary.import': 'Import · {kw} kW',
    'home.flow.summary.export': 'Export · {kw} kW',
    'home.strip48h': '→ 48h Forecast',
    'home.laterBtn': 'Later',
    'home.blockedBtn': 'Blocked',
    'home.thisMonth': 'This month',
    'home.pvSelfUse': 'PV self-use',
    'home.battArbitrage': 'Battery arbitrage',
    'home.shiftedLoads': 'Shifted loads',
    'home.exportRevenue': 'Export revenue',
    'home.cta.midday': 'Run dishwasher',
    'home.cta.evening': 'Pause dryer',
    'home.cta.overnight': 'On plan ✓',
    'home.cta.morning': 'Got it',
    'home.sub.midday': 'Best now — no difference tomorrow morning',
    'home.sub.evening': 'After 23:00 it\'ll be 6.80 zł cheaper',
    'home.sub.overnight': 'Škoda charging until 06:30',
    'home.sub.morning': 'Laundry should wait until 11:00',
    'home.rec.midday': 'Great time for electricity — price stays low for ~90 min, battery is topping up for tonight.',
    'home.rec.evening': 'Peak hour. Prices drop sharply after 23:00 — battery powers the house so you don\'t buy.',
    'home.rec.overnight': 'Cheapest window of the day. Škoda is charging, battery absorbing grid energy for morning.',
    'home.rec.morning': 'Prices climbing. Breakfast now — heavy loads wait until 11:00 when PV kicks in.',
    'home.croissant.midday': 'Sun is working — {saved} zł saved so far today, that\'s {emoji} {food}',
    'home.croissant.evening': 'Despite peak hours: {saved} zł saved today — battery shielding the home',
    'home.croissant.overnight': 'Tonight: charging the Škoda at 29 gr — {saved} zł saved already, that\'s {emoji} {food}',
    'home.croissant.morning': 'Good morning — {saved} zł saved since early, that\'s {emoji} {food}',

    // ── Plan ──────────────────────────────────────────────────────────────
    'plan.header': 'Plan · tap any hour',
    'plan.cheapWindow': 'Cheapest 3h window',
    'plan.autoLink': 'Prefer automatic? → Optimizer',
    'plan.scheduleBtn': 'Schedule at {hour}:00',
    'plan.cheaper': '{diff} gr cheaper than now',
    'plan.moreExp': '{diff} gr more expensive than now',
    'plan.same': 'Same as now',
    'plan.croissant': 'Your queue will save {total} zł, that\'s {emoji} {food}.',
    'plan.queueTitle': 'Queue',
    'plan.historyLink': '→ History',
    'plan.emptyQueue': 'Tap any hour below 50 gr to add your first task.',
    'plan.savingsTitle': 'This queue will save',
    'plan.totalRow': 'Total',
    'plan.shiftedRow': 'Shifted',
    'plan.co2Row': 'Less CO₂',
    'plan.totalSub': 'vs. charging at peak',
    'plan.shiftedSub': 'from peak to cheap windows',
    'plan.co2Sub': 'estimated (220 g/kWh PL grid)',
    'plan.optimizerNote': 'Plan is overwritten by Optimizer every 15 min.',
    'plan.load.dishwasher': 'Dishwasher',
    'plan.load.washer': 'Washing machine',
    'plan.load.car': 'Car charge',

    // ── Forecast ──────────────────────────────────────────────────────────
    'forecast.cheapTomorrow': 'Cheapest window tomorrow:',
    'forecast.pvTitle': 'PV Forecast · gross generation',
    'forecast.pvDisclaimer': 'Doesn\'t include home consumption or battery — raw panel output.',
    'forecast.pvToday': 'Today',
    'forecast.pvTomorrow': 'Tomorrow',
    'forecast.tariffTitle': 'Energy prices · 48h',
    'forecast.tariffSub': 'Pstryk G14 dynamic · all-in prices with DSO + taxes',
    'forecast.consumptionTitle': 'Home consumption forecast',
    'forecast.consumptionBody': 'Home consumption forecast requires an ML model (NILM ExtraTreesClassifier + FHMM). Under construction — Phase 2 roadmap.',
    'forecast.tomorrowLabel': 'tomorrow →',
    'forecast.croissantSuffix': 'enough for laundry + dishwasher, that\'s',
    'forecast.pvAvailable': 'PV tomorrow forecast at',
    'forecast.min': 'min',
    'forecast.avg': 'avg',
    'forecast.max': 'max',
    'forecast.tierCheap': 'Cheap',
    'forecast.tierStd': 'Medium',
    'forecast.tierPeak': 'Expensive',
    'forecast.tierHigh': 'Peak',
    'forecast.axisNow': 'Now',
    'forecast.axisTomorrow': 'tomorrow',

    // ── Weather & Solar / Grid Health ─────────────────────────────────────
    'weather.title': 'Weather & Solar',
    'weather.now': 'Now',
    'weather.today': 'Today',
    'weather.tomorrow': 'Tomorrow',
    'weather.irradiance': 'Irradiance',
    'weather.pvToday': 'PV today',
    'weather.pvTomorrow': 'PV tomorrow',
    'weather.peak': 'Peak {w} W',
    'weather.mostlyClear': 'Mostly clear · {pct}%',
    'weather.overcast': 'Overcast',
    'weather.dry': 'Dry',
    'weather.source': 'Open-Meteo + Forecast.Solar · 15 min snapshot',
    'grid.title': 'Grid Health · PL',
    'grid.weekday': 'Weekday',
    'grid.weekend': 'Weekend',
    'grid.carbonIntensity': 'Carbon intensity',
    'grid.renewable': 'Renewable share',
    'grid.load': 'Grid load',
    'grid.wind': 'Wind',
    'grid.solar': 'Solar',
    'grid.co2Low': 'Low',
    'grid.co2Mid': 'Mid',
    'grid.co2High': 'High',
    'grid.genMix': 'Generation mix',
    'grid.mix.solar': 'Solar',
    'grid.mix.wind': 'Wind',
    'grid.mix.gas': 'Gas',
    'grid.mix.biomass': 'Biomass',
    'grid.mix.oil': 'Oil',
    'grid.mix.hydro': 'Hydro',
    'grid.mix.other': 'Coal & other',
    'grid.source': 'ENTSO-E · {time}',

    // ── Optimizer ────────────────────────────────────────────────────────
    'optimizer.readOnly': 'Read-only — Hardware writes locked. Plan generated by optimizer_orchestrator.py',
    'optimizer.activeWhen': 'active when',
    'optimizer.statusOptimal': 'Optimal',
    'optimizer.lastPlan': 'last plan',
    'optimizer.heroDesc': 'Tonight Gridome will shift {kwh} kWh to cheap windows — saving {saved} zł, that\'s {emoji} {food}.',
    'optimizer.shifted': 'Shifted',
    'optimizer.savings': 'Savings',
    'optimizer.co2': 'Less CO₂',
    'optimizer.timeline': 'Schedule · next 24h',
    'optimizer.fromNow': 'from {hour}:00',
    'optimizer.socTitle': 'Battery SOC projection · next 24h',
    'optimizer.socNote': 'Calculated by battery_orchestrator.py from optimizer decisions.',
    'optimizer.bufferLabel': 'buffer 80%',
    'optimizer.reserveLabel': 'reserve 20%',
    'optimizer.rowPrices': 'Prices',
    'optimizer.rowPV': 'PV',
    'optimizer.rowBattery': 'Battery',
    'optimizer.rowEV': 'Car',
    'optimizer.rowHP': 'Heat pump',
    'optimizer.rowExport': 'Export',
    'optimizer.decEV': 'Charging {start}–{end} · avg {price} gr/kWh',
    'optimizer.decBattery': 'Grid charge {start}–{end}, discharge at {peak}',
    'optimizer.decHP': 'SG-Ready ON: 10:00–14:00 (PV surplus + cheap window)',
    'optimizer.decExport': 'Export priority: 11:00–13:00 (battery full)',
    'optimizer.readOnlyNote': 'Hardware writes locked until live-write is activated.',
    'forecast.savings': 'in savings',
    'opt.readonlyText': 'Hardware writes locked. Plan generated by',
    'opt.card.ev': 'Car (Škoda)',
    'opt.card.evDecOvernight': 'Charging 01:00–06:00 · avg 29 gr/kWh',
    'opt.card.evDecOther': 'Scheduled 01:00–05:00 tomorrow · cheap window',
    'opt.card.battery': 'Battery',
    'opt.card.batteryDec': 'Grid charge 02:00–05:00, discharge 17:00–20:00',
    'opt.card.hp': 'Heat pump',
    'opt.card.feedin': 'Export',
    'food.donuts': '≈ {count} donuts',
    'food.pierogi': '≈ {count} beers',
    'food.croissants': '≈ {count} croissants',
    'food.coffees': '≈ {count} coffees',

    // ── Battery ───────────────────────────────────────────────────────────
    'battery.readOnly': 'Read-only — Modbus writes locked until',
    'battery.readOnlyNote': 'on the pilot device. Viewing live state from pilot2',
    'battery.status.charging': 'Charging',
    'battery.status.discharging': 'Discharging',
    'battery.status.idle': 'Standby',
    'battery.powerLabel': 'Power',
    'battery.voltageLabel': 'Voltage',
    'battery.energyLabel': 'Energy',
    'battery.zonesTitle': 'Priority zones',
    'battery.readOnlyBadge': 'Read-only',
    'battery.bufferZone': 'EV Buffer',
    'battery.bufferDesc': 'Above {pct}% battery charges the car preferentially over grid.',
    'battery.mixedZone': 'Mixed zone',
    'battery.mixedDesc': 'Between {low}% and {high}% — cost optimisation.',
    'battery.reserveZone': 'Home reserve',
    'battery.reserveDesc': 'Below {pct}% battery powers home only, not EV.',
    'battery.strategyNote': 'SOC limits will unlock when Gridome gets Modbus write permission. Current strategy:',
    'battery.strategyName': 'self-use + peak-shaving',
    'battery.historyTitle': 'SOC history · today',
    'battery.historyBadge': 'Needs backend',
    'battery.historyNote': 'Hourly SOC history is not yet persisted.',
    'battery.gridChargeTitle': 'Grid charging',
    'battery.priceTreshold': 'Price threshold',
    'battery.targetSoc': 'Target SOC',
    'battery.tariffWindow': 'G14 window',
    'battery.gridLockedNote': 'Grid charging locked until live-write is activated.',
    'battery.pendingPerUnit': 'Per-unit SOC · pending',

    // ── Auto ─────────────────────────────────────────────────────────────
    'auto.status.available': 'Ready',
    'auto.status.charging': 'Charging',
    'auto.status.suspended': 'Waiting',
    'auto.status.finishing': 'Done',
    'auto.sessionEnergy': 'Energy',
    'auto.sessionTime': 'Duration',
    'auto.sessionEnd': 'End ~',
    'auto.sessionAvgPrice': 'Avg price',
    'auto.croissant': 'Tonight: {kwh} kWh, that\'s ≈ {km} km range, cost {cost} zł',
    'auto.range': 'Range',
    'auto.sessionCost': 'Session cost',
    'auto.chargePlan': 'Tonight\'s charge plan',
    'auto.chargeNow': 'Charge now',
    'auto.editPlan': 'Edit plan',
    'auto.smartTitle': 'Smart Charging · OCPP profile',
    'auto.loadBalanceTitle': 'Circuit load · 25A / 3-phase',
    'auto.weeklyTitle': 'Weekly plan · charge_plans.py',
    'auto.readOnlyBadge': 'Read only',
    'auto.status.done': 'Fully charged ✓',
    'auto.status.waiting': 'Waiting for cheap window',
    'auto.status.unplugged': 'Unplugged',
    'auto.doneBy': 'Done by',
    'auto.headroomNote': 'ev_scheduler.py throttles charger at',

    // ── Sessions ──────────────────────────────────────────────────────────
    'sessions.backendNotice': 'Stub — Session history not in backend yet.',
    'sessions.backendPath': 'ev_sessions.json',
    'sessions.backendPattern': 'energy_accum.json',
    'sessions.backendApi': 'GET /api/sessions',
    'sessions.periodMonth': 'This month',
    'sessions.periodYear': 'This year',
    'sessions.periodAll': 'All time',
    'sessions.heroTitle': 'Total cost',
    'sessions.periodApril': 'April 2026',
    'sessions.periodAllTime': 'all time',
    'sessions.sparklineTitle': 'Daily cost',
    'sessions.emptyState': 'Your first session awaits.',
    'sessions.colDate': 'Date',
    'sessions.colEnergy': 'Energy',
    'sessions.colTime': 'Duration',
    'sessions.colAvgPrice': 'Avg price',
    'sessions.colSolar': 'Solar',
    'sessions.colCost': 'Cost',
    'sessions.modalKwCurve': 'Charge power (kW) · session',
    'sessions.modalTariffCurve': 'Tariff price during session (gr/kWh)',
    'sessions.modalAmpProfile': 'Smart Charging profile (A)',
    'sessions.peakComparison': 'Charging at peak (142 gr) would cost',
    'sessions.optimizerSaved': 'Optimizer saved',
    'sessions.csv': 'CSV',
    'sessions.savingsCompare': '{period}: peak charging would cost {peakCost} zł — Gridome saved {saved} zł, that\'s {food}.',
    'sessions.noData': 'No data',
    'sessions.backendNeed': 'Needed',
    'sessions.backendPatternLabel': 'pattern',
    'auto.now': 'Now',
    'auto.profileDesc': 'Charging profile from OCPP Central System. Current (A) depends on tariff window and available headroom.',
    'auto.profile.waiting': 'Smart · waiting for cheap window (23:00)',
    'auto.profile.charging': 'TxDefaultProfile · 32A · window 01:00–06:00',
    'auto.profile.done': 'TxDefaultProfile · finished',
    'auto.labelEnergy': 'Energy',
    'auto.labelTime': 'Time',
    'auto.day.0': 'Mon', 'auto.day.1': 'Tue', 'auto.day.2': 'Wed',
    'auto.day.3': 'Thu', 'auto.day.4': 'Fri', 'auto.day.5': 'Sat', 'auto.day.6': 'Sun',

    // ── Tweaks ────────────────────────────────────────────────────────────
    'tweaks.title': 'Preview settings',
    'tweaks.scenario': 'Scenario',
    'tweaks.screen': 'Screen',
    'tweaks.state': 'State',
    'tweaks.safeMode': 'Safe mode',
    'tweaks.locale': 'Language',

    // ── Days ──────────────────────────────────────────────────────────────
    'days.0': 'Sun', 'days.1': 'Mon', 'days.2': 'Tue',
    'days.3': 'Wed', 'days.4': 'Thu', 'days.5': 'Fri', 'days.6': 'Sat',
    'days.full.0': 'Sunday', 'days.full.1': 'Monday', 'days.full.2': 'Tuesday',
    'days.full.3': 'Wednesday', 'days.full.4': 'Thursday', 'days.full.5': 'Friday', 'days.full.6': 'Saturday',
    // ── Settings ──────────────────────────────────────────────────────────────
    'settings.general': 'General',
    'settings.siteTitle': 'Installation name',
    'settings.siteTitleDesc': 'Shown in the header and notifications',
    'settings.homeAddress': 'Home address',
    'settings.homeAddressDesc': 'Used for PV forecast and DSO detection',
    'settings.language': 'Interface language',
    'settings.currency': 'Currency',
    'settings.currencyDesc': 'Independent of language — prices always in PLN',
    'settings.devices': 'Devices',
    'settings.addDevice': 'Add device',
    'settings.pickDeviceType': 'Pick a device type',
    'settings.deviceShelly': 'Shelly',
    'settings.deviceOCPP': 'OCPP charger',
    'settings.deviceInverter': 'Inverter',
    'settings.deviceMeter': 'Meter',
    'settings.deviceHomewiz': 'HomeWizard',
    'settings.deviceTasmota': 'Tasmota',
    'settings.online': 'online',
    'settings.offline': 'offline',
    'settings.users': 'Users',
    'settings.addViewer': 'Add viewer',
    'settings.lastSeen': 'Last seen',
    'settings.revoke': 'Revoke access',
    'settings.confirmRevoke': 'Sure? Revoke?',
    'settings.username': 'Username',
    'settings.viewerNote': 'Viewers can only read data. No commands to devices.',
    'settings.createViewer': 'Create account',
    'settings.tariffs': 'Tariffs',
    'settings.tariffProvider': 'Dynamic price provider',
    'settings.dso': 'Distribution operator',
    'settings.dsoDesc': 'Auto-detected from site location',
    'settings.formula': 'All-in price formula',
    'settings.formulaDesc': 'Price = (TGE spot + distribution fee + taxes) × 1.23',
    'settings.operation': 'Operation',
    'settings.safeMode': 'Safe mode — hardware lock',
    'settings.safeModeDesc': 'Blocks all Modbus, OCPP and relay writes. Reads and metrics keep flowing.',
    'settings.batteryLive': 'Battery write (GRIDOME_BATTERY_LIVE)',
    'settings.batteryLiveDesc': 'Unlock SOC limits and grid-charge control',
    'settings.active': 'Active',
    'settings.locked': 'Locked',
    'settings.telemetry': 'Telemetry',
    'settings.telemetryOff': 'Off',
    'settings.telemetryAnon': 'Anonymous',
    'settings.telemetryFull': 'Full',
    'settings.system': 'System',
    'settings.version': 'Version',
    'settings.backup': 'Backup',
    'settings.backupDesc': 'Export or import configuration',
    'settings.backupBtn': 'Download backup',
    'settings.restoreBtn': 'Restore',
    'settings.logs': 'Logs',
    'settings.viewLogs': 'View logs',
    'settings.versionFooter': 'Gridome v0.9.12 · running for 12 days · this month savings: 221 zl',
    'settings.back': 'Back',
    'settings.deviceName': 'Device name',
    'settings.ipAddress': 'IP address',
    'settings.channel': 'Channel',
    'settings.addDeviceConfirm': 'Add device',
    'settings.cancel': 'Cancel',
    'settings.readOnlyNote': 'Writes disabled in prototype — active after porting to React.',
  },
};

// ── Locale context ─────────────────────────────────────────────────────────
const LocaleContext = React.createContext({ locale: 'pl', setLocale: () => {}, t: () => '' });

function LocaleProvider({ children }) {
  const [locale, setLocaleState] = React.useState(() => {
    try { return localStorage.getItem('gridome.locale') || 'pl'; } catch { return 'pl'; }
  });

  const setLocale = React.useCallback((l) => {
    setLocaleState(l);
    try { localStorage.setItem('gridome.locale', l); } catch {}
  }, []);

  const t = React.useCallback((key, params = {}) => {
    const str = (STRINGS[locale] || STRINGS.pl)[key] || (STRINGS.pl[key]) || key;
    return str.replace(/\{(\w+)\}/g, (_, k) => params[k] ?? `{${k}}`);
  }, [locale]);

  return (
    <LocaleContext.Provider value={{ locale, setLocale, t }}>
      {children}
    </LocaleContext.Provider>
  );
}

function useLocale() {
  return React.useContext(LocaleContext);
}

// ── Locale-aware food helper ───────────────────────────────────────────────
function foodText(zl, t) {
  if (zl >= 18) return t('food.donuts',     { count: Math.floor(zl/4.5) });
  if (zl >= 10) return t('food.pierogi',    { count: Math.floor(zl/2.2) });
  if (zl >= 5)  return t('food.croissants', { count: Math.floor(zl/3.5) });
  return               t('food.coffees',    { count: Math.floor(zl/1.6) });
}

// ── Locale-aware tier label helper ─────────────────────────────────────────
function getTierLabel(tier, t) {
  const map = { cheap: 'tier.cheap', standard: 'tier.standard', peak: 'tier.peak', high: 'tier.high' };
  return t(map[tier] || 'tier.cheap');
}

// ── Locale-aware day helper ────────────────────────────────────────────────
function getDayAbbr(dayIndex, t) { return t(`days.${dayIndex}`); }
function getDayFull(dayIndex, t) { return t(`days.full.${dayIndex}`); }

// ── Locale chip component ──────────────────────────────────────────────────
function LocaleChips() {
  const { locale, setLocale } = useLocale();
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 2 }}>
      {['pl', 'en'].map(l => (
        <button key={l} onClick={() => setLocale(l)} style={{
          background: 'transparent', border: 'none',
          color: locale === l ? T.green : T.textMuted,
          fontFamily: T.fontUI, fontSize: 11, fontWeight: locale === l ? 700 : 400,
          cursor: 'pointer', padding: '2px 5px',
          borderBottom: `1.5px solid ${locale === l ? T.green : 'transparent'}`,
          textTransform: 'uppercase', letterSpacing: 0.5,
          transition: 'all 140ms',
        }}>{l}</button>
      ))}
    </div>
  );
}

Object.assign(window, {
  STRINGS, LocaleContext, LocaleProvider, useLocale, foodText,
  getTierLabel, getDayAbbr, getDayFull, LocaleChips,
});
