diff --git a/api/index.php b/api/index.php index 1793424..e4c5f57 100644 --- a/api/index.php +++ b/api/index.php @@ -1744,7 +1744,8 @@ function getInventoryAnomalies(PDO $db): void { $rows = $db->query(" SELECT p.id AS product_id, p.name, p.brand, p.unit, p.default_quantity, p.package_unit, - i.id AS inventory_id, i.quantity AS inv_qty, i.location, + MIN(i.id) AS inventory_id, + SUM(i.quantity) AS inv_qty, COALESCE(tx_in.tot, 0) AS total_in, COALESCE(tx_out.tot, 0) AS total_out FROM inventory i @@ -1758,6 +1759,8 @@ function getInventoryAnomalies(PDO $db): void { FROM transactions WHERE type IN ('out','waste') AND undone = 0 GROUP BY product_id ) tx_out ON tx_out.product_id = p.id WHERE i.quantity > 0 + GROUP BY p.id, p.name, p.brand, p.unit, p.default_quantity, p.package_unit, + tx_in.tot, tx_out.tot ")->fetchAll(PDO::FETCH_ASSOC); // Anomaly dismissed keys stored in a simple JSON file @@ -1783,14 +1786,13 @@ function getInventoryAnomalies(PDO $db): void { // so it stays dismissed until the user explicitly resets or the direction changes. // An inventory correction (bringing qty closer to expected) will flip the direction // or drop below threshold — naturally clearing the dismissed state. - $key = 'a_' . $r['product_id'] . '_' . $direction; - if (!empty($dismissed[$key])) continue; - $direction = $diff > 0 ? 'phantom' : 'missing'; // Special case: expected is negative — more consumption recorded than entries. // The real qty vs tx comparison is meaningless; what we actually know is that // "initial stock was never formally registered as an 'in' transaction". if ($expected <= 0) $direction = 'untracked'; + $key = 'a_' . $r['product_id'] . '_' . $direction; + if (!empty($dismissed[$key])) continue; $anomalies[] = [ 'inventory_id' => (int)$r['inventory_id'], 'product_id' => (int)$r['product_id'], @@ -3076,8 +3078,8 @@ function generateRecipe(PDO $db): void { if (!empty($item['expiry_date']) && $daysLeft < 0) return 1; if (!empty($item['expiry_date']) && $daysLeft <= 3) return 2; if (!empty($item['expiry_date']) && $daysLeft <= 7) return 3; + if ($isOpen) return 3; // opened items: same priority as expiring this week — must be used soon if (!empty($item['expiry_date'])) return 4; - if ($isOpen) return 5; return 6; }; @@ -3132,13 +3134,12 @@ function generateRecipe(PDO $db): void { $priorityHeaders = [ 1 => 'SCADUTI — usa subito', 2 => 'SCADENZA ≤3gg — priorità alta', - 3 => 'SCADENZA ≤7gg', + 3 => 'SCADENZA ≤7gg / APERTI — usa presto', 4 => 'ALTRI CON SCADENZA', - 5 => 'APERTI', 6 => 'DISPENSA', ]; // Limit groups to keep prompt compact: - // 1-3 (urgent): all items; 4 (has expiry): max 40; 5 (opened): all; 6 (pantry): max 20 + // 1-3 (urgent+opened): all items; 4 (has expiry): max 40; 6 (pantry): max 20 foreach ($priorityHeaders as $g => $header) { if (empty($priorityGroups[$g])) continue; $groupItems = $priorityGroups[$g]; @@ -4014,8 +4015,8 @@ function generateRecipeStream(PDO $db): void { if (!empty($item['expiry_date']) && $daysLeft < 0) return 1; if (!empty($item['expiry_date']) && $daysLeft <= 3) return 2; if (!empty($item['expiry_date']) && $daysLeft <= 7) return 3; + if ($isOpen) return 3; // opened items: same priority as expiring this week — must be used soon if (!empty($item['expiry_date'])) return 4; - if ($isOpen) return 5; return 6; }; @@ -4050,7 +4051,7 @@ function generateRecipeStream(PDO $db): void { // Senza piano pasto: limiti moderati per ridurre token (ora safe grazie a thinkingBudget:0) $hasMealPlan = !empty($mealPlanType); $ingredientSections = []; - $priorityHeaders = [1=>'SCADUTI — usa subito',2=>'SCADENZA ≤3gg — priorità alta',3=>'SCADENZA ≤7gg',4=>'ALTRI CON SCADENZA',5=>'APERTI',6=>'DISPENSA']; + $priorityHeaders = [1=>'SCADUTI — usa subito',2=>'SCADENZA ≤3gg — priorità alta',3=>'SCADENZA ≤7gg / APERTI — usa presto',4=>'ALTRI CON SCADENZA',6=>'DISPENSA']; $totalIngredientsSent = 0; foreach ($priorityHeaders as $g => $header) { if (empty($priorityGroups[$g])) continue; @@ -4854,7 +4855,12 @@ function italianToBring(string $italianName): string { $genericQualifiers = [ 'dolce','salato','light','bio','classico','original','naturale','fresco','fresca', - 'intero','intera','magro','magra','piccolo','piccola','grande','rosso','bianco' + 'intero','intera','magro','magra','piccolo','piccola','grande','rosso','bianco', + // Generic descriptors that appear inside multi-word product names (e.g. "succo e polpa frutta", + // "muesli frutta secca") but do NOT represent the item category on their own. + // Pass 1 (exact match on shopping_name) still works correctly for truly generic items + // like shopping_name='Frutta' → it2de['frutta'] = 'Früchte'. + 'frutta','verdura','frutti', ]; $candidates = []; foreach ($catalog['it2de'] as $itLower => $deKey) { @@ -5292,14 +5298,16 @@ function bringCleanupObsolete(PDO $db): array { return array_values(array_unique(array_filter($toks, fn($t) => mb_strlen($t) > 2 && !in_array($t, $stopwords)))); }; - // Build smart map: ONLY shopping_name tokens → item. - // Deliberately NOT indexing by product name tokens — product names like - // "Pera Italiana Succo e polpa frutta" contain words ("succo", "frutta") that - // would wrongly keep unrelated Bring! items ("Succo", "Frutta") on the list. - // The shopping_name (e.g. "Pere") is the canonical generic name used in Bring!. + // Build smart map by shopping_name tokens AND by exact name. + // Exact match is tried first to prevent loose token collisions like + // 'Panna' (Bring! item, in stock) matching 'Panna da cucina' (depleted, critical) + // because they share the 'panna' token. $smartByTok = []; + $smartByExactName = []; foreach ($smartItems as $si) { $sName = !empty($si['shopping_name']) ? $si['shopping_name'] : $si['name']; + $sNameNorm = strtolower(trim($sName)); + if ($sNameNorm !== '') $smartByExactName[$sNameNorm] = $si; foreach ($ntFn($sName) as $tok) { if (!isset($smartByTok[$tok])) $smartByTok[$tok] = $si; } @@ -5321,10 +5329,15 @@ function bringCleanupObsolete(PDO $db): array { } if (!$isAppAdded) continue; - // Match against smart items using shopping_name-priority tokens + // Match against smart items: exact shopping_name first, then first-token fallback. + // Exact match prevents e.g. 'Panna' → 'Panna da cucina' via shared token 'panna'. $nameToks = $ntFn($name); - $firstTok = $nameToks[0] ?? ''; - $smartSi = $firstTok ? ($smartByTok[$firstTok] ?? null) : null; + $exactKey = strtolower(trim($name)); + $smartSi = $smartByExactName[$exactKey] ?? null; + if ($smartSi === null) { + $firstTok = $nameToks[0] ?? ''; + $smartSi = $firstTok ? ($smartByTok[$firstTok] ?? null) : null; + } if ($smartSi !== null) { // Still in smart_shopping with critical or high urgency → keep @@ -5985,8 +5998,12 @@ function smartShopping(PDO $db): void { // first purchase → now, so idle periods after last use don't deflate the rate. // Example: Aglio bought 60 days ago but last used 34 days ago → use 34-day window. $lastActivity = max($lastIn ?? 0, $lastOut ?? 0); - $effectiveDays = ($firstIn && $lastActivity > $firstIn) - ? max(1, ($lastActivity - $firstIn) / 86400) + $activitySpan = ($firstIn && $lastActivity > $firstIn) ? ($lastActivity - $firstIn) : 0; + // Guard: if all activity fits within 24h (e.g. bought & consumed same day / seconds apart), + // effectiveDays would collapse to 1 → wildly inflated daily rate (e.g. Pizza: in+out 9s apart). + // Fall back to daysSinceFirst (first purchase → now) for a conservative estimate. + $effectiveDays = ($activitySpan >= 86400) + ? max(1, $activitySpan / 86400) : $daysSinceFirst; $dailyRate = $effectiveDays < 999 && $totalUsed > 0 ? $totalUsed / $effectiveDays : 0; diff --git a/assets/js/app.js b/assets/js/app.js index 2754bc6..b66f872 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -103,10 +103,10 @@ function _updateGeminiButtonState() { if (_geminiAvailable) { btn.classList.remove('header-btn-no-ai'); btn.removeAttribute('title'); - btn.setAttribute('title', 'Chat con Gemini'); + btn.setAttribute('title', t('gemini.chat_title')); } else { btn.classList.add('header-btn-no-ai'); - btn.setAttribute('title', '🤖 Gemini non configurato — imposta GEMINI_API_KEY nelle impostazioni'); + btn.setAttribute('title', t('gemini.not_configured')); } } @@ -168,8 +168,8 @@ function _checkWebappUpdate() { if (!badge) return; const versionLabel = deployChanged - ? (serverVer ? `v${serverVer}` : 'Nuova versione') - : (latestTag ? `v${latestTag}` : 'Nuova versione'); + ? (serverVer ? `v${serverVer}` : t('update.new_version')) + : (latestTag ? `v${latestTag}` : t('update.new_version')); const hideBadge = () => { badge.style.display = 'none'; @@ -179,7 +179,7 @@ function _checkWebappUpdate() { badge.innerHTML = `⬆️ ${versionLabel}` + - `` + + `` + ``; badge.style.display = 'inline-flex'; @@ -2152,7 +2152,7 @@ window._kioskUpdateResult = function(result) { const verLabel = document.getElementById('kiosk-update-version-label'); if (!status) return; - if (btn) { btn.disabled = false; btn.textContent = '🔍 Cerca aggiornamenti'; } + if (btn) { btn.disabled = false; btn.textContent = t('kiosk.check_btn'); } if (result.error && !result.has_update) { status.style.display = ''; @@ -2165,7 +2165,7 @@ window._kioskUpdateResult = function(result) { const current = result.current || '?'; const latest = result.latest || '?'; - if (verLabel) verLabel.textContent = `Installata: ${current}`; + if (verLabel) verLabel.textContent = t('kiosk.version_installed').replace('{v}', current); if (result.has_update) { _kioskPendingApkUrl = result.apk_url || ''; @@ -2173,7 +2173,7 @@ window._kioskUpdateResult = function(result) { status.style.background = 'rgba(245,158,11,0.1)'; status.style.border = '1px solid rgba(245,158,11,0.35)'; status.style.color = ''; - status.innerHTML = `⬆️ Nuova versione disponibile: ${latest} (installata: ${current})`; + status.innerHTML = t('kiosk.update_available').replace('{latest}', latest).replace('{current}', current); if (installBtn) installBtn.style.display = ''; } else { _kioskPendingApkUrl = ''; @@ -2181,7 +2181,7 @@ window._kioskUpdateResult = function(result) { status.style.background = 'rgba(52,211,153,0.1)'; status.style.border = '1px solid rgba(52,211,153,0.3)'; status.style.color = ''; - status.innerHTML = `✅ Sei già aggiornato — versione ${current}`; + status.innerHTML = t('kiosk.up_to_date').replace('{v}', current); if (installBtn) installBtn.style.display = 'none'; } }; @@ -2195,8 +2195,7 @@ function _kioskCheckForUpdates() { status.style.display = ''; status.style.background = 'rgba(245,158,11,0.1)'; status.style.border = '1px solid rgba(245,158,11,0.35)'; - status.innerHTML = `⚠️ Il kiosk installato è troppo vecchio per il controllo automatico.
- Premi il pulsante qui sotto per scaricare e installare la v1.7.0 direttamente.`; + status.innerHTML = t('kiosk.too_old'); } // Pre-set the pending URL and show the install button (installUpdate works in old APKs too) _kioskPendingApkUrl = 'https://github.com/dadaloop82/EverShelf/releases/download/kiosk-latest/evershelf-kiosk.apk'; @@ -2206,13 +2205,13 @@ function _kioskCheckForUpdates() { const btn = document.getElementById('btn-kiosk-check-update'); const status = document.getElementById('kiosk-update-status'); const installBtn = document.getElementById('btn-kiosk-install-update'); - if (btn) { btn.disabled = true; btn.textContent = '⏳ Controllo…'; } + if (btn) { btn.disabled = true; btn.textContent = t('kiosk.checking'); } if (status) { status.style.display = 'none'; } if (installBtn) { installBtn.style.display = 'none'; } _kioskPendingApkUrl = ''; try { _kioskBridge.checkForUpdates(); } catch(e) { - if (btn) { btn.disabled = false; btn.textContent = '🔍 Cerca aggiornamenti'; } - showToast('❌ Errore durante il controllo', 'error'); + if (btn) { btn.disabled = false; btn.textContent = t('kiosk.check_btn'); } + showToast('❌ ' + t('kiosk.error_check'), 'error'); } } @@ -2226,22 +2225,18 @@ function _kioskInstallUpdate() { status.style.display = ''; status.style.background = 'rgba(239,68,68,0.1)'; status.style.border = '1px solid rgba(239,68,68,0.3)'; - status.innerHTML = `⚠️ Questo kiosk non supporta l'installazione automatica.
- Procedura manuale:
- 1. Esci dal kiosk (tasto ✕ in alto a sinistra)
- 2. Disinstalla l'app EverShelf Kiosk
- 3. Scarica e installa la nuova APK da GitHub:
- - https://github.com/dadaloop82/EverShelf/releases/download/kiosk-latest/evershelf-kiosk.apk + status.innerHTML = t('kiosk.manual_install') + + `
+https://github.com/dadaloop82/EverShelf/releases/download/kiosk-latest/evershelf-kiosk.apk `; } return; } const installBtn = document.getElementById('btn-kiosk-install-update'); - if (installBtn) { installBtn.disabled = true; installBtn.textContent = '⏳ Avvio download…'; } + if (installBtn) { installBtn.disabled = true; installBtn.textContent = t('kiosk.starting_download'); } try { _kioskBridge.installUpdate(_kioskPendingApkUrl); } catch(e) { - if (installBtn) { installBtn.disabled = false; installBtn.textContent = '⬇️ Installa aggiornamento'; } - showToast('❌ Errore avvio installazione', 'error'); + if (installBtn) { installBtn.disabled = false; installBtn.textContent = t('kiosk.install_btn'); } + showToast('❌ ' + t('kiosk.error_start_install'), 'error'); } } @@ -2271,7 +2266,7 @@ function _injectKioskOverlay() { const exitBtn = document.createElement('button'); exitBtn.id = '_kiosk_exit_btn'; exitBtn.textContent = '\u2715'; - exitBtn.title = 'Esci dal kiosk'; + exitBtn.title = t('kiosk.exit_title'); exitBtn.style.cssText = btnStyle; exitBtn.addEventListener('click', (e) => { e.stopPropagation(); @@ -2282,7 +2277,7 @@ function _injectKioskOverlay() { const refBtn = document.createElement('button'); refBtn.id = '_kiosk_refresh_btn'; refBtn.textContent = '\u21bb'; - refBtn.title = 'Aggiorna pagina'; + refBtn.title = t('kiosk.refresh_title'); refBtn.style.cssText = btnStyle.replace('font-size:15px', 'font-size:18px'); refBtn.addEventListener('click', (e) => { e.stopPropagation(); @@ -2297,7 +2292,7 @@ function _injectKioskOverlay() { function renderAppliances(appliances) { const container = document.getElementById('appliances-list'); if (!appliances || appliances.length === 0) { - container.innerHTML = '

Nessun elettrodomestico aggiunto

'; + container.innerHTML = `

${t('appliances.empty')}

`; return; } container.innerHTML = appliances.map((a, i) => ` @@ -3101,14 +3096,14 @@ function _renderNutritionSection(inventory) { // Score colour const scoreColor = healthScore >= 70 ? '#4ade80' : healthScore >= 45 ? '#fbbf24' : '#f87171'; - const scoreLabel = healthScore >= 70 ? '😄 Ottimo' : healthScore >= 45 ? '🙂 Discreto' : '😬 Migliorabile'; + const scoreLabel = healthScore >= 70 ? t('nutrition.score_excellent') : healthScore >= 45 ? t('nutrition.score_good') : t('nutrition.score_improve'); section.innerHTML = `
-

🥗 Analisi Alimentare

+

${t('nutrition.title')}

${scoreLabel}
@@ -3119,7 +3114,7 @@ function _renderNutritionSection(inventory) {
${total} - prodotti + ${t('nutrition.products_count')}
@@ -3137,12 +3132,12 @@ function _renderNutritionSection(inventory) {
- ${_nutrScoreBar('🌿 Salute', healthScore, '#4ade80')} - ${_nutrScoreBar('🎨 Varietà', varietyScore, '#60a5fa')} - ${_nutrScoreBar('❄️ Freschi', fresh_pct, '#22d3ee')} + ${_nutrScoreBar(t('nutrition.label_health'), healthScore, '#4ade80')} + ${_nutrScoreBar(t('nutrition.label_variety'), varietyScore, '#60a5fa')} + ${_nutrScoreBar(t('nutrition.label_fresh'), fresh_pct, '#22d3ee')}
-
Basato su ${total} prodotti in dispensa · EverShelf
+
${t('nutrition.source').replace('{n}', total)}
`; // Trigger pie animation after render @@ -3889,7 +3884,7 @@ function renderBannerItem() { let btns = ``; btns += ``; if (_geminiAvailable) { - btns += ``; + btns += ``; } actionsEl.innerHTML = btns; @@ -3981,7 +3976,7 @@ async function explainBannerAnomaly() { const detailEl = document.getElementById('alert-banner-detail'); if (!detailEl) return; const originalHtml = detailEl.innerHTML; - detailEl.innerHTML = '\ud83e\udd16 Analizzo\u2026'; + detailEl.innerHTML = `${t('dashboard.banner_analyzing')}`; // Disable the Spiega button to prevent double calls const explainBtn = document.querySelector('#alert-banner .btn-banner-ai'); @@ -5569,7 +5564,7 @@ function startManualEntry(barcode = '') { document.getElementById('pf-barcode').value = barcode || ''; document.getElementById('pf-image').value = ''; document.getElementById('pf-image-preview').style.display = 'none'; - document.getElementById('product-form-title').textContent = 'Nuovo Prodotto'; + document.getElementById('product-form-title').textContent = t('product.title_new'); const pfAiRow = document.getElementById('pf-ai-fill-row'); if (pfAiRow) pfAiRow.style.display = 'block'; @@ -6775,7 +6770,7 @@ async function _fetchExpiryHistoryAndUpdate(productId) { let days = isVacuum ? getVacuumExpiryDays(data.avg_days) : data.avg_days; const newDate = addDays(days); const newLabel = formatEstimatedExpiry(days); - const suffix = ` 📊 storico`; + const suffix = ` 📊 storico`; const expiryInput = document.getElementById('add-expiry'); const estimateEl = document.querySelector('.expiry-estimate-label'); const dateEl = document.querySelector('.expiry-estimate-date'); @@ -6976,7 +6971,7 @@ function selectPurchaseType(btn, type) { const estimatedDate = addDays(days); const estimateLabel = formatEstimatedExpiry(days); let suffix = ''; - if (window._historyExpiryDays) suffix = ` 📊 storico`; + if (window._historyExpiryDays) suffix = ` 📊 storico`; else if (loc === 'freezer' && isVacuum) suffix = ' ' + t('add.suffix_freezer_vacuum'); else if (loc === 'freezer') suffix = ' ' + t('add.suffix_freezer'); else if (isVacuum) suffix = ' ' + t('add.suffix_vacuum'); @@ -7572,7 +7567,7 @@ function setPzFraction(frac) { function isLowStock(totalRemaining, unit, defaultQty) { if (totalRemaining <= 0) return true; // fully depleted → definitely needs restocking if (unit === 'pz') return totalRemaining <= 1; // only 1 piece left - if (unit === 'conf') return totalRemaining < 1; // only warn when less than 1 full pack remains (opened/partial) + if (unit === 'conf') return totalRemaining < 0.25; // warn when less than 25% of a package remains // Weight/volume: use percentage of default_qty or fixed threshold if (defaultQty > 0) return totalRemaining <= defaultQty * 0.25; // Fallback fixed thresholds @@ -8320,7 +8315,7 @@ async function analyzeWithAI() { // Option to save as-is without barcode html += `
`; - html += ``; + html += ``; html += `
`; resultDiv.innerHTML = html; @@ -9488,7 +9483,7 @@ function _renderSmartLastUpdate() { const el = document.getElementById('smart-last-update'); if (!el || !_smartShoppingLastFetch) return; const d = new Date(_smartShoppingLastFetch); - el.textContent = `Aggiornato ${d.getHours().toString().padStart(2,'0')}:${d.getMinutes().toString().padStart(2,'0')}`; + el.textContent = t('shopping.smart_last_update').replace('{time}', `${d.getHours().toString().padStart(2,'0')}:${d.getMinutes().toString().padStart(2,'0')}`); } function startBgShoppingRefresh() { @@ -9708,7 +9703,7 @@ async function migrateBringNames(btn) { showToast(`🔄 ${data.migrated} nomi generalizzati in Bring!`, 'success'); loadShoppingList(); // refresh the shopping list view } else { - showToast('Tutti i nomi sono già aggiornati', 'info'); + showToast(t('shopping.names_already_updated'), 'info'); } } else { if (statusEl) statusEl.textContent = '❌ ' + (data.error || 'Errore'); @@ -12931,21 +12926,21 @@ function _renderScreensaverNutrition() { el.innerHTML = `
-
🥗 La tua dispensa oggi
+
${t('nutrition.today_title')}
-
${total} prodotti
+
${t('nutrition.products_n').replace('{n}', total)}
${top4.map(s => `
${s.icon} ${s.cat}${s.pct}%
`).join('')}
- ${_ssDonut('❤️ Salute', healthScore, healthColor)} - ${_ssDonut('🎨 Varietà', varietyScore, varColor)} - ${_ssDonut('❄️ Freschi', fresh_pct, freshColor)} + ${_ssDonut(t('nutrition.label_health'), healthScore, healthColor)} + ${_ssDonut(t('nutrition.label_variety'), varietyScore, varColor)} + ${_ssDonut(t('nutrition.label_fresh'), fresh_pct, freshColor)}
`; @@ -13050,7 +13045,7 @@ function generateScreensaverFact() { } // Greeting based on time - const greeting = hour < 12 ? 'Buongiorno' : hour < 18 ? 'Buon pomeriggio' : 'Buonasera'; + const greeting = hour < 12 ? t('facts.greeting_morning') : hour < 18 ? t('facts.greeting_afternoon') : t('facts.greeting_evening'); // Random item picker const rItem = (arr) => arr.length ? arr[Math.floor(Math.random() * arr.length)] : null; @@ -13060,10 +13055,11 @@ function generateScreensaverFact() { // --- Expired items facts --- if (expired.length > 0) { - facts.push(() => `Hai ${expired.length} ${expired.length === 1 ? 'prodotto scaduto' : 'prodotti scaduti'} in dispensa. Controlla!`); + facts.push(() => expired.length === 1 ? t('facts.expired_one') : t('facts.expired_many').replace('{n}', expired.length)); facts.push(() => { const names = expired.slice(0, 3).map(i => i.name); - return `Prodotti scaduti: ${names.join(', ')}${expired.length > 3 ? ` e altri ${expired.length - 3}` : ''}`; + const extra = expired.length > 3 ? ` ${t('facts.expired_list_more').replace('{n}', expired.length - 3)}` : ''; + return t('facts.expired_list').replace('{names}', names.join(', ') + extra); }); const freezerExpired = expired.filter(i => i.location === 'freezer'); if (freezerExpired.length > 0) { @@ -13071,14 +13067,14 @@ function generateScreensaverFact() { const item = rItem(freezerExpired); const safety = getExpiredSafety(item, Math.abs(daysUntilExpiry(item.expiry_date))); if (safety.level === 'ok' || safety.level === 'warning') { - return `${item.name} è scaduto, ma essendo in freezer potrebbe essere ancora buono! Controlla.`; + return t('facts.freezer_expired_ok').replace('{name}', item.name); } - return `${item.name} in freezer è scaduto da troppo tempo. Meglio buttarlo.`; + return t('facts.freezer_expired_old').replace('{name}', item.name); }); } const frigoExpired = expired.filter(i => i.location === 'frigo'); if (frigoExpired.length > 0) { - facts.push(() => `Hai ${frigoExpired.length} ${frigoExpired.length === 1 ? 'prodotto scaduto' : 'prodotti scaduti'} in frigo!`); + facts.push(() => frigoExpired.length === 1 ? t('facts.fridge_expired_one') : t('facts.fridge_expired_many').replace('{n}', frigoExpired.length)); } } @@ -13087,48 +13083,48 @@ function generateScreensaverFact() { facts.push(() => { const item = expiringSoon[0]; const days = daysUntilExpiry(item.expiry_date); - if (days === 0) return `${item.name} scade oggi! Usalo subito.`; - if (days === 1) return `${item.name} scade domani. Pensaci!`; - return `${item.name} scade tra ${days} giorni.`; + if (days === 0) return t('facts.expiring_today').replace('{name}', item.name); + if (days === 1) return t('facts.expiring_tomorrow').replace('{name}', item.name); + return t('facts.expiring_days').replace('{name}', item.name).replace('{days}', days); }); if (expiringSoon.length > 1) { - facts.push(() => `Hai ${expiringSoon.length} prodotti in scadenza ravvicinata.`); + facts.push(() => t('facts.expiring_many').replace('{n}', expiringSoon.length)); } } if (expiringThisWeek.length > 0) { - facts.push(() => `Questa settimana scadono ${expiringThisWeek.length} prodotti. Pianifica i pasti di conseguenza!`); + facts.push(() => t('facts.expiring_this_week').replace('{n}', expiringThisWeek.length)); facts.push(() => { const item = rItem(expiringThisWeek); const days = daysUntilExpiry(item.expiry_date); const locLabel = LOCATIONS[item.location]?.label || item.location; - return `${item.name} (${locLabel}) scade tra ${days} ${days === 1 ? 'giorno' : 'giorni'}.`; + return t('facts.expiring_item_loc').replace('{name}', item.name).replace('{loc}', locLabel).replace('{days}', days).replace('{dayslabel}', days === 1 ? t('facts.day') : t('facts.days')); }); } if (expiringThisMonth.length > 0) { - facts.push(() => `In questo mese scadranno ${expiringThisMonth.length} prodotti.`); + facts.push(() => t('facts.expiring_this_month').replace('{n}', expiringThisMonth.length)); } // --- Shopping list facts (skip count/names — already shown in the shopping panel) --- if (shop.length > 0) { const names = shop.slice(0, 3).map(i => i.name).join(', '); - const extra = shop.length > 3 ? ` e altri ${shop.length - 3}` : ''; - facts.push(() => `Metti in lista: ${names}${extra} 🛒`); + const extra = shop.length > 3 ? ` ${t('facts.shopping_more').replace('{n}', shop.length - 3)}` : ''; + facts.push(() => t('facts.shopping_add').replace('{names}', names + extra)); } if (shop.length === 0) { - facts.push(() => `Lista della spesa vuota. Tutto rifornito! ✅`); + facts.push(() => t('facts.shopping_empty')); } // --- Location-based facts --- if (inFrigo.length > 0) { facts.push(() => { const item = rItem(inFrigo); - return `In frigo c'è: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}.`; + return t('facts.in_fridge').replace('{name}', item.name + (item.brand ? ' (' + item.brand + ')' : '')); }); } if (inFreezer.length > 0) { facts.push(() => { const item = rItem(inFreezer); - return `Nel freezer c'è: ${item.name}. Non dimenticartelo!`; + return t('facts.in_freezer').replace('{name}', item.name); }); } @@ -13140,37 +13136,37 @@ function generateScreensaverFact() { const top = sorted[0]; const catLabel = top[0]; const icon = CATEGORY_ICONS[catLabel] || '📦'; - return `La categoria più presente è ${icon} ${catLabel} con ${top[1].length} prodotti.`; + return t('facts.top_category').replace('{icon}', icon).replace('{cat}', t('categories.' + catLabel) || catLabel).replace('{n}', top[1].length); }); if (byCategory['carne'] && byCategory['carne'].length > 0) { - facts.push(() => `Hai ${byCategory['carne'].length} prodotti di carne. 🥩`); + facts.push(() => t('facts.cat_meat').replace('{n}', byCategory['carne'].length)); } if (byCategory['latticini'] && byCategory['latticini'].length > 0) { - facts.push(() => `Hai ${byCategory['latticini'].length} latticini in casa. 🥛`); + facts.push(() => t('facts.cat_dairy').replace('{n}', byCategory['latticini'].length)); } if (byCategory['verdura'] && byCategory['verdura'].length > 0) { - facts.push(() => `Hai ${byCategory['verdura'].length} tipi di verdura. Ottimo per la salute! 🥬`); + facts.push(() => t('facts.cat_veggies').replace('{n}', byCategory['verdura'].length)); } if (byCategory['frutta'] && byCategory['frutta'].length > 0) { - facts.push(() => `Hai ${byCategory['frutta'].length} tipi di frutta. 🍎`); + facts.push(() => t('facts.cat_fruit').replace('{n}', byCategory['frutta'].length)); } if (byCategory['bevande'] && byCategory['bevande'].length > 0) { - facts.push(() => `Hai ${byCategory['bevande'].length} bevande disponibili. 🥤`); + facts.push(() => t('facts.cat_drinks').replace('{n}', byCategory['bevande'].length)); } if (byCategory['surgelati'] && byCategory['surgelati'].length > 0) { - facts.push(() => `Hai ${byCategory['surgelati'].length} surgelati nel freezer. ❄️`); + facts.push(() => t('facts.cat_frozen').replace('{n}', byCategory['surgelati'].length)); } if (byCategory['pasta'] && byCategory['pasta'].length > 0) { - facts.push(() => `Hai ${byCategory['pasta'].length} tipi di pasta. 🍝 Che ne dici di una carbonara?`); + facts.push(() => t('facts.cat_pasta').replace('{n}', byCategory['pasta'].length)); } if (byCategory['conserve'] && byCategory['conserve'].length > 0) { - facts.push(() => `Hai ${byCategory['conserve'].length} conserve in dispensa. 🥫`); + facts.push(() => t('facts.cat_canned').replace('{n}', byCategory['conserve'].length)); } if (byCategory['snack'] && byCategory['snack'].length > 0) { - facts.push(() => `Hai ${byCategory['snack'].length} snack. Resisti alla tentazione! 🍪`); + facts.push(() => t('facts.cat_snacks').replace('{n}', byCategory['snack'].length)); } if (byCategory['condimenti'] && byCategory['condimenti'].length > 0) { - facts.push(() => `Hai ${byCategory['condimenti'].length} condimenti a disposizione. 🧂`); + facts.push(() => t('facts.cat_condiments').replace('{n}', byCategory['condimenti'].length)); } } @@ -13178,16 +13174,16 @@ function generateScreensaverFact() { if (inv.length > 0) { facts.push(() => { const item = rItem(inv); - return `Lo sapevi? Hai ${item.name} in ${LOCATIONS[item.location]?.label || item.location}.`; + return t('facts.item_random').replace('{name}', item.name).replace('{loc}', LOCATIONS[item.location]?.label || item.location); }); facts.push(() => { const item = rItem(inv); const qty = formatQuantity(item.quantity, item.unit, item.default_quantity, item.package_unit); - return `${item.name}: ne hai ${qty}.`; + return t('facts.item_qty').replace('{name}', item.name).replace('{qty}', qty); }); } if (noExpiry.length > 0) { - facts.push(() => `${noExpiry.length} prodotti non hanno una data di scadenza impostata.`); + facts.push(() => t('facts.no_expiry_count').replace('{n}', noExpiry.length)); } if (withExpiry.length > 0) { // Find the one expiring furthest away @@ -13196,7 +13192,7 @@ function generateScreensaverFact() { return d > (best.d || 0) ? { item, d } : best; }, { d: 0 }); if (furthest.item && furthest.d > 30) { - facts.push(() => `Il prodotto con scadenza più lontana è ${furthest.item.name}: ${Math.round(furthest.d / 30)} mesi.`); + facts.push(() => t('facts.furthest_expiry').replace('{name}', furthest.item.name).replace('{months}', Math.round(furthest.d / 30))); } } @@ -13206,56 +13202,56 @@ function generateScreensaverFact() { facts.push(() => { const item = rItem(highQtyItems); const qty = formatQuantity(item.quantity, item.unit, item.default_quantity, item.package_unit); - return `Hai una bella scorta di ${item.name}: ${qty}!`; + return t('facts.high_qty').replace('{name}', item.name).replace('{qty}', qty); }); } const lowQtyItems = inv.filter(i => parseFloat(i.quantity) <= 1 && parseFloat(i.quantity) > 0); if (lowQtyItems.length > 0) { facts.push(() => { const item = rItem(lowQtyItems); - return `${item.name} sta per finire. Aggiungilo alla spesa?`; + return t('facts.low_qty_item').replace('{name}', item.name); }); - facts.push(() => `Ci sono ${lowQtyItems.length} prodotti quasi finiti.`); + facts.push(() => t('facts.low_qty_count').replace('{n}', lowQtyItems.length)); } // --- Time-of-day greetings & suggestions --- if (hour >= 6 && hour < 10) { - if (byCategory['pane']) facts.push(() => `Buongiorno! Hai del pane per la colazione. 🍞`); - if (byCategory['latticini']) facts.push(() => `C'è del latte in frigo per il cappuccino? ☕🥛`); - if (byCategory['frutta']) facts.push(() => `Buongiorno! Una bella frutta fresca per iniziare bene. 🍎`); + if (byCategory['pane']) facts.push(() => t('facts.morning_bread')); + if (byCategory['latticini']) facts.push(() => t('facts.morning_milk')); + if (byCategory['frutta']) facts.push(() => t('facts.morning_fruit')); } if (hour >= 11 && hour < 14) { - if (byCategory['pasta']) facts.push(() => `Ora di pranzo… Un bel piatto di pasta? 🍝`); - if (byCategory['verdura']) facts.push(() => `Un'insalata fresca per pranzo? Hai ${byCategory['verdura'].length} verdure! 🥗`); + if (byCategory['pasta']) facts.push(() => t('facts.noon_pasta')); + if (byCategory['verdura']) facts.push(() => t('facts.noon_salad').replace('{n}', byCategory['verdura'].length)); } if (hour >= 17 && hour < 21) { - if (byCategory['carne']) facts.push(() => `Per cena potresti usare la carne che hai. 🥩`); - if (byCategory['pesce']) facts.push(() => `Che ne dici di pesce per cena? 🐟`); - if (expiringThisWeek.length > 0) facts.push(() => `Hai ${expiringThisWeek.length} prodotti in scadenza questa settimana — usali stasera!`); + if (byCategory['carne']) facts.push(() => t('facts.evening_meat')); + if (byCategory['pesce']) facts.push(() => t('facts.evening_fish')); + if (expiringThisWeek.length > 0) facts.push(() => t('facts.evening_expiring').replace('{n}', expiringThisWeek.length)); } if (hour >= 21 || hour < 6) { - if (expiringSoon.length > 0) facts.push(() => `Buonanotte! Domani ricordati di usare: ${expiringSoon.slice(0,2).map(i=>i.name).join(', ')}.`); + if (expiringSoon.length > 0) facts.push(() => t('facts.night_reminder').replace('{names}', expiringSoon.slice(0,2).map(i=>i.name).join(', '))); } // --- Weekly stats --- const recentIn = stats.recent_in || 0; const recentOut = stats.recent_out || 0; if (recentIn > 0 && recentOut > 0) { - facts.push(() => `Bilancio settimana: +${recentIn} aggiunti, −${recentOut} consumati.`); + facts.push(() => t('facts.weekly_balance').replace('{in}', recentIn).replace('{out}', recentOut)); } else if (recentIn > 0) { - facts.push(() => `Questa settimana hai aggiunto ${recentIn} prodotti.`); + facts.push(() => t('facts.weekly_added').replace('{n}', recentIn)); } else if (recentOut > 0) { - facts.push(() => `Questa settimana hai consumato ${recentOut} prodotti. Ottimo!`); + facts.push(() => t('facts.weekly_consumed').replace('{n}', recentOut)); } // --- Tips & curiosità (statici ma ruotano) --- - facts.push(() => `💡 I prodotti in freezer durano molto più a lungo della data di scadenza.`); - facts.push(() => `💡 Il pane congelato mantiene la fragranza per settimane.`); - facts.push(() => `💡 Per evitare sprechi, usa prima i prodotti con scadenza più vicina (FIFO).`); - facts.push(() => `💡 La carne in freezer può durare fino a 6 mesi senza problemi.`); - facts.push(() => `💡 Non ricongelare mai un alimento già scongelato. Cucinalo subito!`); - facts.push(() => `💡 Un frigo ordinato ti fa risparmiare tempo e denaro.`); - facts.push(() => `💡 Le conserve aperte vanno in frigo e consumate in pochi giorni.`); + facts.push(() => t('facts.tip_freezer')); + facts.push(() => t('facts.tip_bread')); + facts.push(() => t('facts.tip_fifo')); + facts.push(() => t('facts.tip_meat')); + facts.push(() => t('facts.tip_no_refreeze')); + facts.push(() => t('facts.tip_fridge')); + facts.push(() => t('facts.tip_canned')); // --- Brand-based facts --- const brands = inv.filter(i => i.brand).map(i => i.brand); @@ -13263,24 +13259,24 @@ function generateScreensaverFact() { const brandCount = {}; brands.forEach(b => { brandCount[b] = (brandCount[b] || 0) + 1; }); const topBrand = Object.entries(brandCount).sort((a, b) => b[1] - a[1])[0]; - facts.push(() => `Il marca più presente nella tua dispensa è ${topBrand[0]} con ${topBrand[1]} prodotti.`); + facts.push(() => t('facts.top_brand').replace('{brand}', topBrand[0]).replace('{n}', topBrand[1])); } // --- Specific food combo facts --- if (byCategory['pasta'] && byCategory['condimenti']) { - facts.push(() => `Hai pasta e condimenti: sei pronto per un primo piatto! 🍝`); + facts.push(() => t('facts.combo_pasta')); } if (byCategory['pane'] && byCategory['carne']) { - facts.push(() => `Pane e carne: un panino veloce è sempre una buona idea! 🥪`); + facts.push(() => t('facts.combo_sandwich')); } if (byCategory['verdura'] && byCategory['carne']) { - facts.push(() => `Verdura e carne: hai tutto per un piatto equilibrato! 🥗🥩`); + facts.push(() => t('facts.combo_balanced')); } // --- Empty states --- if (inv.length === 0) { - facts.push(() => `La dispensa è vuota! Fai una bella spesa. 🛒`); - facts.push(() => `Nessun prodotto registrato. Scansiona qualcosa per iniziare!`); + facts.push(() => t('facts.pantry_empty')); + facts.push(() => t('facts.pantry_empty_scan')); } // --- Location distribution --- @@ -13290,7 +13286,7 @@ function generateScreensaverFact() { const parts = Object.entries(byLocation).map(([loc, items]) => `${LOCATIONS[loc]?.icon || '📦'} ${items.length}` ); - return `Distribuzione: ${parts.join(' · ')}`; + return t('facts.location_distribution').replace('{parts}', parts.join(' · ')); }); } @@ -13300,7 +13296,7 @@ function generateScreensaverFact() { // Pick a random fact if (facts.length === 0) { - return `${greeting}! La tua Dispensa ti aspetta.`; + return t('facts.pantry_waiting').replace('{greeting}', greeting); } return facts[Math.floor(Math.random() * facts.length)](); } diff --git a/index.html b/index.html index ba5a1ef..890d227 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@ EverShelf - + @@ -1469,6 +1469,6 @@ - + diff --git a/translations/de.json b/translations/de.json index d3d3573..9a14f74 100644 --- a/translations/de.json +++ b/translations/de.json @@ -1,946 +1,1064 @@ { - "app": { - "name": "EverShelf", - "loading": "Laden..." + "app": { + "name": "EverShelf", + "loading": "Laden..." + }, + "nav": { + "title": "EverShelf", + "home": "Home", + "inventory": "Vorrat", + "recipes": "Rezepte", + "shopping": "Einkauf", + "log": "Verlauf" + }, + "btn": { + "back": "← Zurück", + "save": "💾 Speichern", + "cancel": "✕ Abbrechen", + "close": "Schließen", + "add": "✅ Hinzufügen", + "delete": "Löschen", + "edit": "✏️ Bearbeiten", + "search": "🔍 Suchen", + "go": "✅ Los", + "toggle_password": "👁️ Anzeigen/Ausblenden", + "load_more": "Mehr laden...", + "save_config": "💾 Konfiguration speichern", + "save_product": "💾 Produkt speichern", + "restart": "↺ Neustart", + "reset_default": "↺ Standard wiederherstellen", + "save_info": "💾 Info speichern", + "retry": "🔄 Erneut versuchen" + }, + "locations": { + "dispensa": "Vorratskammer", + "frigo": "Kühlschrank", + "freezer": "Gefrierschrank", + "altro": "Sonstiges" + }, + "categories": { + "latticini": "Milchprodukte", + "carne": "Fleisch", + "pesce": "Fisch", + "frutta": "Obst", + "verdura": "Gemüse", + "pasta": "Pasta & Reis", + "pane": "Brot & Backwaren", + "surgelati": "Tiefkühl", + "bevande": "Getränke", + "condimenti": "Gewürze", + "snack": "Snacks & Süßes", + "conserve": "Konserven", + "cereali": "Getreide & Hülsenfrüchte", + "igiene": "Hygiene", + "pulizia": "Reinigung", + "altro": "Sonstiges", + "select": "-- Auswählen --" + }, + "units": { + "pz": "Stk", + "conf": "Pkg", + "g": "g", + "ml": "ml", + "pieces": "Stück", + "grams": "Gramm", + "box": "Packung", + "boxes": "Packungen" + }, + "shopping_sections": { + "frutta_verdura": "Obst & Gemüse", + "carne_pesce": "Fleisch & Fisch", + "latticini": "Milchprodukte & Frisches", + "pane_dolci": "Brot & Süßes", + "pasta": "Pasta & Getreide", + "conserve": "Konserven & Soßen", + "surgelati": "Tiefkühl", + "bevande": "Getränke", + "pulizia_igiene": "Reinigung & Hygiene", + "altro": "Sonstiges" + }, + "dashboard": { + "expired_title": "🚫 Abgelaufen", + "expiring_title": "⏰ Bald ablaufend", + "stats_period": "📊 Letzte 30 Tage", + "opened_title": "📦 Geöffnete Produkte", + "review_title": "🔍 Zu prüfen", + "review_hint": "Mengen, die ungewöhnlich erscheinen. Bestätigen oder ändern.", + "quick_recipe": "Schnelles Rezept mit ablaufenden Produkten", + "banner_review_title": "Ungewöhnliche Menge", + "banner_review_action_ok": "Ist korrekt", + "banner_review_action_finish": "🗑️ Alles aufgebraucht", + "banner_review_action_edit": "Korrigieren", + "banner_review_action_weigh": "Wiegen", + "banner_review_dismiss": "Ignorieren", + "banner_prediction_title": "Ungewöhnlicher Verbrauch", + "banner_prediction_hint": "Laut Vorhersage stimmt diese Menge nicht mit dem erwarteten Verbrauch überein.", + "banner_prediction_action_confirm": "{qty} {unit} bestätigen", + "banner_prediction_action_weigh": "Jetzt wiegen", + "banner_prediction_action_edit": "Menge aktualisieren", + "banner_expired_title": "Abgelaufenes Produkt", + "banner_expired_today": "Heute abgelaufen", + "banner_expired_days": "Seit {days} Tagen abgelaufen", + "banner_expired_action_use": "Trotzdem verwenden", + "banner_expired_action_throw": "Habe ich weggeworfen", + "banner_expired_action_edit": "Datum korrigieren", + "banner_anomaly_action_edit": "Bestand korrigieren", + "banner_anomaly_action_dismiss": "Menge ist korrekt", + "banner_no_expiry_title": "Ablaufdatum fehlt: {name}", + "banner_no_expiry_detail": "Dieses Produkt hat kein Ablaufdatum. Möchten Sie eines hinzufügen oder bestätigen, dass es nicht verfällt?", + "banner_no_expiry_action_set": "Ablaufdatum setzen", + "banner_no_expiry_action_dismiss": "Läuft nicht ab ✓", + "banner_no_expiry_toast_dismissed": "Als 'läuft nicht ab' markiert", + "banner_expiring_title": "Bald ablaufend", + "banner_expiring_today": "Läuft heute ab!", + "banner_expiring_tomorrow": "Läuft morgen ab", + "banner_expiring_days": "Läuft in {days} Tagen ab", + "banner_expiring_action_use": "Jetzt verwenden", + "banner_finished_title": "aufgebraucht?", + "banner_finished_detail": "Ich habe vermerkt, dass {name} auf null gesunken ist. Ist es wirklich leer, oder hast du noch welches?", + "banner_finished_action_yes": "Ja, aufgebraucht", + "banner_finished_action_no": "Nein, ich habe noch welches", + "banner_review_unusual_pkg_title": "Ungewöhnliche Packungsgröße", + "banner_review_unusual_pkg_detail": "Du hast eine Packung von {qty} {unit} eingestellt — die Größe scheint sehr groß. Überprüfe ob es korrekt ist.", + "banner_review_low_qty_title": "Sehr geringe Menge", + "banner_review_low_qty_detail": "Du hast nur {qty} im Bestand — das scheint sehr wenig, möglicherweise ein Eingabefehler. Bestätige wenn korrekt.", + "banner_review_high_qty_title": "Ungewöhnlich hohe Menge", + "banner_review_high_qty_detail": "Du hast {qty} im Bestand — die Zahl scheint sehr hoch. Bestätige wenn korrekt oder korrigiere.", + "banner_prediction_rate_day": "Durchschnitt ~{n} {unit}/Tag", + "banner_prediction_rate_week": "Durchschnitt ~{n} {unit}/Woche", + "banner_prediction_days_ago": "Vor {n} Tagen aufgefüllt", + "banner_prediction_more": "Ich erwartete {expected} {unit}{time}, du hast aber {actual} {unit}. Hast du Bestand ohne Buchung hinzugefügt?", + "banner_prediction_less": "Ich erwartete {expected} {unit}{time}, du hast aber nur {actual} {unit}. Hast du mehr als üblich verbraucht?", + "banner_finished_zero": "Bestand zeigt null, aber gespeicherte Buchungen deuten an, dass es nicht leer sein sollte.", + "banner_finished_expected": "Laut Aufzeichnungen solltest du noch {qty} {unit} haben.", + "banner_finished_check": "Kannst du nachschauen?", + "banner_anomaly_phantom_title": "mehr Bestand als erwartet", + "banner_anomaly_phantom_detail": "Bestand zeigt {inv_qty} {unit}, aber laut Buchungen solltest du nur {expected_qty} {unit} haben. Hast du Bestand ohne Buchung hinzugefügt?", + "banner_anomaly_untracked_title": "Anfangsbestand nicht als Eingang gebucht", + "banner_anomaly_untracked_detail": "Du hast {inv_qty} {unit} im Bestand, aber die gebuchten Abgänge übersteigen die Eingänge — der Anfangsbestand wurde wahrscheinlich nie als \"Eingang\" erfasst. Bitte korrigiere die Menge oder trage die fehlenden Eingänge nach.", + "banner_anomaly_ghost_title": "weniger Bestand als erwartet", + "banner_anomaly_ghost_detail": "Laut Buchungen solltest du {expected_qty} {unit} von {name} haben, aber der Bestand zeigt nur {inv_qty} {unit}. Hast du etwas ohne Buchung entnommen?", + "consumed": "Verbraucht: {n} ({pct}%)", + "wasted": "Weggeworfen: {n} ({pct}%)", + "more_opened": "und {n} weitere geöffnet...", + "banner_expired_detail": "{when} · du hast noch {qty}.", + "banner_explain_title": "Gemini um eine Erklärung bitten", + "banner_explain_btn": "Erklären", + "banner_analyzing": "🤖 Analysiere…" + }, + "inventory": { + "title": "Vorrat", + "filter_all": "Alle", + "search_placeholder": "🔍 Produkt suchen...", + "recent_title": "🕐 Zuletzt verwendet", + "popular_title": "⭐ Meistverwendet", + "empty": "Keine Produkte hier.\nScanne ein Produkt, um es hinzuzufügen!", + "no_items_found": "Keine Bestandseinträge gefunden", + "qty_remainder_suffix": "übrig", + "vacuum_badge": "🫙 Vakuumiert", + "opened_badge": "📭 Geöffnet", + "label_expiry": "📅 Ablaufdatum", + "label_storage": "🫙 Aufbewahrung", + "label_status": "📭 Status", + "opened_since": "Geöffnet seit {date}", + "label_position": "📍 Standort", + "label_quantity": "📦 Menge", + "label_added": "📅 Hinzugefügt", + "empty_text": "Keine Produkte hier.
Scanne ein Produkt, um es hinzuzufügen!", + "empty_db": "Keine Produkte in der Datenbank.
Scanne ein Produkt, um loszulegen!", + "qty_trace": "< 1" + }, + "scan": { + "title": "Produkt scannen", + "mode_shopping": "🛒 Einkaufsmodus", + "mode_shopping_end": "✅ Einkauf beenden", + "zoom": "Zoom", + "barcode_placeholder": "Barcode eingeben...", + "quick_name_divider": "oder Name eingeben", + "quick_name_placeholder": "z.B.: Äpfel, Zucchini, Brot...", + "manual_entry": "✏️ Manuelle Eingabe", + "ai_identify": "🤖 Mit KI identifizieren", + "hint": "Barcode scannen, Produktname eingeben oder KI zur Identifikation nutzen", + "debug_toggle": "🐛 Debug Log", + "barcode_acquired": "🔖 Barcode gescannt: {code}", + "scan_barcode": "🔖 Barcode scannen", + "create_named": "{name} erstellen", + "new_without_barcode": "Neues Produkt ohne Barcode" + }, + "action": { + "title": "Was möchtest du tun?", + "add_btn": "📥 HINZUFÜGEN", + "add_sub": "in Vorrat/Kühlschrank", + "use_btn": "📤 VERWENDEN / VERBRAUCHEN", + "use_sub": "aus Vorrat/Kühlschrank", + "have_title": "📦 Schon auf Lager!", + "add_more_sub": "weitere Menge", + "use_qty_sub": "wie viel verwendet", + "throw_btn": "🗑️ ENTSORGEN", + "throw_sub": "wegwerfen", + "edit_sub": "Ablauf, Ort…", + "create_recipe_btn": "Rezept damit erstellen" + }, + "add": { + "title": "Zum Vorrat hinzufügen", + "location_label": "📍 Wohin?", + "quantity_label": "📦 Menge", + "conf_size_label": "📦 Jede Packung enthält:", + "conf_size_placeholder": "z.B. 300", + "vacuum_label": "🫙 Vakuumiert", + "vacuum_hint": "Ablaufdatum wird automatisch verlängert", + "submit": "✅ Hinzufügen", + "purchase_type_label": "🛒 Dieses Produkt ist...", + "new_btn": "🆕 Gerade gekauft", + "existing_btn": "📦 Hatte ich schon", + "remaining_label": "📦 Verbleibende Menge", + "remaining_hint": "Ungefähr wie viel ist noch übrig?", + "remaining_full": "🟢 Voll", + "remaining_half": "🟠 Halb", + "estimated_expiry": "Geschätzte Haltbarkeit:", + "suffix_freezer": "(Tiefkühler)", + "suffix_vacuum": "(vakuumversiegelt)", + "hint_modify": "📝 Du kannst das Datum ändern oder mit der Kamera scannen", + "scan_expiry_title": "📷 Ablaufdatum scannen", + "product_added": "✅ {name} hinzugefügt!{qty}", + "suffix_freezer_vacuum": "(Tiefkühler + vakuumversiegelt)", + "history_badge_tip": "Durchschnitt aus {n} früheren Einträgen" + }, + "use": { + "title": "Verwenden / Verbrauchen", + "location_label": "📍 Woher?", + "quantity_label": "Wie viel hast du benutzt?", + "change": "ändern", + "partial_hint": "Oder genaue Menge angeben:", + "partial_piece_hint": "Hast du nur einen Teil verwendet?", + "piece": "Stück", + "one_whole": "1 ganzes", + "use_all": "🗑️ ALLES verwendet / Aufgebraucht", + "submit": "📤 Diese Menge verwenden", + "available": "📦 Verfügbar:", + "opened_badge": "GEOEFFNET", + "not_in_inventory": "⚠️ Produkt nicht im Bestand.", + "expiry_warning": "⚠️ Verwende zuerst die{loc}, die am {date} abläuft — {when}!", + "throw_title": "🗑️ Produkt entsorgen", + "throw_all": "🗑️ ALLES entsorgen ({qty})", + "throw_qty_label": "Wie viel wegwerfen?", + "throw_qty_hint": "oder Menge angeben:", + "throw_partial_btn": "🗑️ Diese Menge entsorgen", + "when_expired": "seit {n} Tagen abgelaufen", + "when_today": "läuft heute ab", + "when_tomorrow": "läuft morgen ab", + "when_days": "läuft in {n} Tagen ab", + "toast_used": "📤 {qty} von {name} verwendet", + "toast_bring": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt", + "toast_opened_finished": "🔓 Geöffnete Packung von {name} aufgebraucht!", + "disambiguation_hint": "Was meinst du mit \"alles aufgebraucht\"?", + "disambiguation_all": "🗑️ ALLES verbraucht ({qty})", + "error_exceeds_stock": "⚠️ Du kannst nicht mehr verwenden als du verfügbar hast!", + "use_all_confirm_title": "✅ Alles aufbrauchen", + "use_all_confirm_msg": "Bestätige, dass du das Produkt vollständig aufgebraucht hast:", + "use_all_confirm_btn": "✅ Ja, aufgebraucht", + "throw_all_confirm_title": "🗑️ Alles entsorgen", + "throw_all_confirm_msg": "Möchtest du wirklich das gesamte Produkt entsorgen?", + "throw_all_confirm_btn": "🗑️ Ja, entsorgen" + }, + "product": { + "title_new": "Neues Produkt", + "title_edit": "Produkt bearbeiten", + "ai_fill": "📷 Foto machen und mit KI identifizieren", + "ai_fill_hint": "KI füllt die Produktfelder automatisch aus", + "name_label": "🏷️ Produktname *", + "name_placeholder": "z.B.: Vollmilch, Penne Nudeln...", + "brand_label": "🏢 Marke", + "brand_placeholder": "z.B.: Barilla, Müller, Knorr...", + "category_label": "📂 Kategorie", + "unit_label": "📏 Maßeinheit", + "default_qty_label": "🔢 Standardmenge", + "conf_size_label": "📦 Jede Packung enthält:", + "conf_size_placeholder": "z.B. 300", + "notes_label": "📝 Notizen", + "notes_placeholder": "z.B.: laktosefrei, bio, nach dem Öffnen im Kühlschrank aufbewahren...", + "barcode_label": "🔖 Barcode", + "barcode_placeholder": "Barcode (falls vorhanden)", + "barcode_hint": "⚠️ Barcode hinzufügen, damit du beim nächsten Einkauf nur scannen musst!", + "submit": "💾 Produkt speichern", + "name_required": "Produktname eingeben", + "conf_size_required": "Packungsinhalt angeben", + "expiry_estimated": "Geschätztes Ablaufdatum:", + "scan_expiry": "Ablaufdatum scannen", + "expiry_hint": "📝 Du kannst das Datum ändern oder mit der Kamera scannen", + "add_batch": "📦 + Charge mit anderem Ablaufdatum", + "package_info": "📦 Packung: {info}", + "edit_catalog": "⚙️ Produktinfo bearbeiten (Name, Marke, Kategorie…)", + "not_recognized": "⚠️ Produkt nicht erkannt", + "edit_info": "✏️ Informationen bearbeiten", + "modify_details": "BEARBEITEN\nAblauf, Ort…", + "already_in_pantry": "📋 Bereits im Vorratsschrank", + "no_barcode": "Kein Barcode" + }, + "products": { + "title": "📦 Alle Produkte", + "search_placeholder": "🔍 Produkt suchen...", + "empty": "Keine Produkte in der Datenbank.\nScanne ein Produkt zum Starten!", + "no_category": "Keine Produkte in dieser Kategorie" + }, + "recipes": { + "title": "🍳 Rezepte", + "generate": "✨ Neues Rezept generieren", + "archive_empty": "Keine Rezepte gespeichert. Erstelle dein erstes Rezept!", + "dialog_title": "🍳 Rezept", + "dialog_desc": "Ich erstelle ein gesundes Rezept mit Zutaten aus dem Vorrat und priorisiere Produkte mit nahendem Ablaufdatum.", + "meal_label": "🕐 Für welche Mahlzeit?", + "persons_label": "👥 Für wie viele Personen?", + "meal_type_label": "🎯 Art der Mahlzeit", + "opt_fast": "⚡ Schnelle Mahlzeit", + "opt_light": "🥗 Kleiner Hunger", + "opt_expiry": "⏰ Ablaufdaten priorisieren", + "opt_healthy": "💚 Extra gesund", + "opt_opened": "📦 Geöffnete Produkte priorisieren", + "opt_zero_waste": "♻️ Zero Waste", + "generate_btn": "✨ Rezept generieren", + "loading_msg": "Rezept wird vorbereitet...", + "start_cooking": "👨‍🍳 Kochmodus", + "regenerate": "🔄 Noch eins generieren", + "close_btn": "✅ Schließen", + "ingredients_title": "🧾 Zutaten", + "steps_title": "👨‍🍳 Zubereitung", + "no_steps": "Keine Zubereitungsschritte verfügbar", + "generate_error": "Fehler bei der Generierung", + "persons_short": "Pers.", + "use_ingredient_title": "Zutat verwenden", + "recipe_qty_label": "Rezept", + "from_where_label": "Von wo?", + "amount_label": "Wie viel", + "use_amount_btn": "Diese Menge verwenden", + "use_all_btn": "ALLES verwenden / Aufgebraucht", + "packs_label": "Packungen", + "quantity_in_total": "Menge in {unit} (gesamt: {total})", + "packs_of_have": "Packungen à {size} (du hast {count} Pack.)", + "scale_wait_stable": "10s stabiles Gewicht für Auto-Ausfüllen abwarten…", + "ingredient_scaled_toast": "📦 Zutat vom Vorrat abgezogen!", + "finished_added_bring_toast": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt!", + "load_error": "Fehler beim Laden" + }, + "shopping": { + "title": "🛒 Einkaufsliste", + "bring_loading": "Verbindung zu Bring!...", + "bring_not_configured": "Bring! ist nicht konfiguriert. Füge E-Mail und Passwort in den Einstellungen hinzu.", + "tab_to_buy": "🛍️ Zu kaufen", + "tab_forecast": "🧠 Vorhersage", + "total_label": "💰 Geschätzter Gesamtbetrag", + "section_to_buy": "🛍️ Zu kaufen", + "suggestions_title": "💡 KI-Vorschläge", + "suggestions_add": "✅ Ausgewählte zu Bring! hinzufügen", + "search_prices": "🔍 Alle Preise suchen", + "suggest_btn": "Einkaufsvorschläge", + "smart_title": "🧠 Intelligente Vorhersagen", + "smart_empty": "Keine Vorhersagen verfügbar.
Füge Produkte zur Vorratskammer hinzu, um intelligente Vorhersagen zu erhalten.", + "smart_filter_all": "Alle", + "smart_filter_critical": "🔴 Dringend", + "smart_filter_high": "🟠 Bald", + "smart_filter_medium": "🟡 Planen", + "smart_filter_low": "🟢 Vorhersage", + "smart_add": "🛒 Ausgewählte zu Bring! hinzufügen", + "empty": "Einkaufsliste leer!\nNutze den Button unten, um Vorschläge zu generieren.", + "already_in_list": "🛒 \"{name}\" ist bereits in der Einkaufsliste", + "already_in_list_short": "ℹ️ Bereits in der Einkaufsliste", + "add_prompt": "Möchtest du es zur Einkaufsliste hinzufügen?", + "smart_already": "📊 Intelligenter Einkauf sagt bereits {name} voraus", + "all_searched": "Alle Produkte wurden bereits gesucht. Nutze 🔄 für einzelne Suchen.", + "search_complete": "Suche abgeschlossen: {count} Produkte", + "suggest_buy": "🛒 Kaufen: {qty} {unit}", + "suggest_buy_approx": "🛒 Mindestens: {qty} {unit}", + "suggest_buy_tip": "Empfohlene Menge basierend auf dem Verbrauch der letzten 14 Tage", + "suggest_buy_approx_tip": "Mindestschätzung basierend auf Verbrauch (nächste Packungsgröße kaufen)", + "removed_sufficient": "🧹 {removed} Produkt(e) mit ausreichendem Bestand von der Liste entfernt", + "bring_badge": "🛒 Schon auf Bring!", + "add_urgent_toast": "🔴 {n} dringende(s) Produkt(e) automatisch zu Bring! hinzugefügt", + "migration_done": "✅ {migrated} aktualisiert, {skipped} bereits ok", + "added_to_bring": "🛒 {n} Produkte zu Bring! hinzugefügt", + "added_to_bring_skip": "{n} bereits vorhanden", + "all_on_bring": "Alle Produkte waren bereits auf Bring!", + "freq_high": "📈 Häufig", + "freq_regular": "📊 Regelmäßig", + "freq_occasional": "📉 Gelegentlich", + "out_of_stock": "Ausverkauft", + "scan_toast": "📷 Scannen: {name}", + "empty_category": "Keine Produkte in dieser Kategorie", + "session_empty": "🛒 Noch keine Produkte", + "urgency_critical": "Dringend", + "urgency_high": "Bald", + "urgency_medium": "Planen", + "urgency_low": "Vorschau", + "urgency_medium_short": "Mittel", + "urgency_low_short": "Ok", + "tag_urgent": "🔴 Dringend", + "tag_priority": "⭐ Priorität", + "tag_check": "✅ Prüfen", + "smart_already_predicted": "📊 Einkauf wird bereits vorhergesagt: {name}{urgency}.", + "item_removed": "✅ {name} von der Liste entfernt!", + "urgency_spec_critical": "⚡ Dringend", + "urgency_spec_high": "🟠 Bald", + "bring_add_n": "{n} zu Bring! hinzufügen", + "bring_add_selected": "Ausgewählte zu Bring! hinzufügen", + "bring_adding": "Wird hinzugefügt...", + "bring_added_one": "1 Produkt zu Bring! hinzugefügt", + "bring_added_many": "{n} Produkte zu Bring! hinzugefügt", + "bring_skipped": "({n} bereits in Liste)", + "force_sync": "Bring!-Synchronisierung erzwingen", + "scan_target_label": "Du suchst", + "scan_target_found": "Gefunden! Aus Liste entfernen", + "bring_add_one": "1 Produkt zu Bring! hinzufügen", + "bring_add_many": "{n} Produkte zu Bring! hinzufügen", + "syncing": "Synchronisiere…", + "sync_done": "Synchronisierung abgeschlossen", + "price_searching": "Suche...", + "search_action": "Suchen", + "open_action": "Öffnen", + "not_found": "Nicht gefunden", + "search_price": "Preis suchen", + "tap_to_scan": "Zum Scannen tippen", + "tag_title": "Tag", + "remove_title": "Entfernen", + "found_count": "{found}/{total} Produkte gefunden", + "savings_offers": "· 🏷️ Du sparst €{amount} mit Angeboten", + "searching_progress": "Suche {current}/{total}...", + "remove_error": "Fehler beim Entfernen", + "btn_fetch_prices": "Preise suchen", + "price_total_label": "💰 Geschätzter Gesamtpreis:", + "price_loading": "Preise werden gesucht…", + "price_not_found": "Preis n/v", + "suggest_loading": "Analyse läuft...", + "suggest_error": "Fehler bei der Vorschlagserstellung", + "priority_high": "Hoch", + "priority_medium": "Mittel", + "priority_low": "Niedrig", + "smart_last_update": "Aktualisiert {time}", + "names_already_updated": "Alle Namen sind bereits aktuell" + }, + "ai": { + "title": "🤖 KI-Identifikation", + "capture": "📸 Foto aufnehmen", + "retake": "🔄 Neu aufnehmen", + "hint": "Mache ein Foto des Produkts und die KI versucht es zu identifizieren", + "identifying": "🤖 Identifiziere Produkt...", + "no_api_key": "⚠️ Gemini API-Schlüssel nicht konfiguriert.\nFüge GEMINI_API_KEY in der .env Datei auf dem Server hinzu.", + "fields_filled": "✅ Felder von KI ausgefüllt", + "use_data": "✅ KI-Daten verwenden", + "use_data_no_barcode": "✅ KI-Daten verwenden (ohne Barcode)" + }, + "log": { + "title": "📒 Verlauf", + "type_added": "Hinzugefügt", + "type_waste": "Entsorgt", + "type_used": "Verwendet", + "type_bring": "Zu Bring! hinzugefügt", + "undone_badge": "Rückgängig", + "undo_title": "Diese Operation rückgängig machen", + "load_error": "Fehler beim Laden des Verlaufs", + "empty": "Keine Operationen aufgezeichnet.", + "undo_action_remove": "Entfernen von", + "undo_action_restore": "Wiederherstellen von", + "undo_confirm": "Vorgang rückgängig machen?\n→ {action} {name}", + "undo_success": "↩ Vorgang rückgängig gemacht für {name}", + "already_undone": "Vorgang bereits rückgängig gemacht", + "too_old": "Vorgänge älter als 24 Stunden können nicht rückgängig gemacht werden", + "undo_error": "Fehler beim Rückgängigmachen" + }, + "chat": { + "title": "Gemini Chef", + "welcome": "Hallo! Ich bin dein Küchenassistent", + "welcome_desc": "Frag mich, dir einen Saft, einen Snack, ein schnelles Gericht zu machen... Ich kenne deinen Vorrat, deine Geräte und deine Vorlieben!", + "suggestion_snack": "🍿 Schneller Snack", + "suggestion_juice": "🥤 Saft/Smoothie", + "suggestion_light": "🥗 Etwas Leichtes", + "suggestion_expiry": "⏰ Ablaufende nutzen", + "clear": "Neues Gespräch", + "placeholder": "Frag etwas...", + "cleared": "Chat geleert", + "suggestion_snack_text": "Was kann ich als schnellen Snack machen?", + "suggestion_juice_text": "Mach mir einen Saft oder Smoothie mit dem was ich habe", + "suggestion_light_text": "Ich habe Hunger, möchte aber etwas Leichtes", + "suggestion_expiry_text": "Was läuft bald ab und wie kann ich es verwenden?", + "transfer_to_recipes": "Zu Rezepten hinzufügen", + "transferring": "Übertrage...", + "transferred": "Zu Rezepten hinzugefügt!", + "open_recipe": "Rezept öffnen" + }, + "cooking": { + "close": "Schließen", + "tts_btn": "Vorlesen", + "restart": "↺ Neustart", + "replay": "🔊 Nochmal", + "timer": "⏱️ {time} · Timer", + "prev": "◀ Zurück", + "next": "Weiter ▶", + "ingredient_used": "✔️ Abgezogen", + "ingredient_use_btn": "📦 Verwenden", + "ingredient_deduct_title": "Von Vorrat abziehen", + "timer_expired_tts": "Timer {label} abgelaufen!", + "timer_warning_tts": "Achtung! {label}: noch 10 Sekunden!", + "recipe_done_tts": "Rezept abgeschlossen! Guten Appetit!", + "expires_chip": "läuft ab {date}", + "finish": "✅ Fertig" + }, + "settings": { + "title": "⚙️ Einstellungen", + "tab_api": "API Keys", + "tab_bring": "Bring!", + "tab_recipe": "Rezepte", + "tab_mealplan": "Wochenplan", + "tab_appliances": "Geräte", + "tab_spesa": "Online-Einkauf", + "tab_camera": "Kamera", + "tab_security": "Sicherheit", + "tab_tts": "Sprache (TTS)", + "tab_language": "Sprache", + "tab_scale": "Smart-Waage", + "gemini": { + "title": "🤖 Google Gemini AI", + "hint": "API-Schlüssel für Produkterkennung, Ablaufdaten und Rezepte.", + "key_label": "Gemini API Key" }, - "nav": { - "title": "EverShelf", - "home": "Home", - "inventory": "Vorrat", - "recipes": "Rezepte", - "shopping": "Einkauf", - "log": "Verlauf" + "bring": { + "title": "🛒 Bring! Einkaufsliste", + "hint": "Zugangsdaten für die Bring! Einkaufslisten-Integration.", + "email_label": "📧 Bring! E-Mail", + "password_label": "🔒 Bring! Passwort" }, - "btn": { - "back": "← Zurück", - "save": "💾 Speichern", - "cancel": "✕ Abbrechen", - "close": "Schließen", - "add": "✅ Hinzufügen", - "delete": "Löschen", - "edit": "✏️ Bearbeiten", - "search": "🔍 Suchen", - "go": "✅ Los", - "toggle_password": "👁️ Anzeigen/Ausblenden", - "load_more": "Mehr laden...", - "save_config": "💾 Konfiguration speichern", - "save_product": "💾 Produkt speichern", - "restart": "↺ Neustart", - "reset_default": "↺ Standard wiederherstellen", - "save_info": "💾 Info speichern", - "retry": "🔄 Erneut versuchen" + "price": { + "title": "💰 Preisschätzung (KI)", + "hint": "Zeigt geschätzte Kosten pro Produkt in der Einkaufsliste mithilfe von KI an.", + "enabled_label": "Preisschätzung aktivieren", + "country_label": "🌍 Referenzland", + "currency_label": "💱 Währung", + "update_label": "🔄 Preise aktualisieren alle", + "update_suffix": "Monate" }, - "locations": { - "dispensa": "Vorratskammer", - "frigo": "Kühlschrank", - "freezer": "Gefrierschrank", - "altro": "Sonstiges" + "recipe": { + "title": "🍳 Rezept-Einstellungen", + "hint": "Konfiguriere die Standardoptionen für die Rezeptgenerierung.", + "persons_label": "👥 Standard-Portionen", + "options_label": "🎯 Standard-Rezeptoptionen", + "fast": "⚡ Schnelles Gericht", + "light": "🥗 Leichte Mahlzeit", + "expiry": "⏰ Ablauf-Priorität", + "healthy": "💚 Extra Gesund", + "opened": "📦 Offene Produkte zuerst", + "zerowaste": "♻️ Keine Verschwendung", + "dietary_label": "🚫 Unverträglichkeiten / Einschränkungen", + "dietary_placeholder": "z.B.: glutenfrei, laktosefrei, vegetarisch..." }, - "categories": { - "latticini": "Milchprodukte", - "carne": "Fleisch", - "pesce": "Fisch", - "frutta": "Obst", - "verdura": "Gemüse", - "pasta": "Pasta & Reis", - "pane": "Brot & Backwaren", - "surgelati": "Tiefkühl", - "bevande": "Getränke", - "condimenti": "Gewürze", - "snack": "Snacks & Süßes", - "conserve": "Konserven", - "cereali": "Getreide & Hülsenfrüchte", - "igiene": "Hygiene", - "pulizia": "Reinigung", - "altro": "Sonstiges", - "select": "-- Auswählen --" + "mealplan": { + "title": "📅 Wöchentlicher Essensplan", + "hint": "Lege die Mahlzeitenart für jeden Tag fest. Wird als Leitfaden bei der Rezeptgenerierung verwendet.", + "enabled": "✅ Wöchentlichen Essensplan aktivieren", + "legend": "🌤️ = Mittagessen · 🌙 = Abendessen · Tippe auf ein Badge, um es zu ändern.", + "types_title": "📋 Verfügbare Typen" }, - "units": { - "pz": "Stk", - "conf": "Pkg", - "g": "g", - "ml": "ml", - "pieces": "Stück", - "grams": "Gramm", - "box": "Packung", - "boxes": "Packungen" + "appliances": { + "title": "🔌 Verfügbare Geräte", + "hint": "Gib an, welche Geräte du hast. Sie werden bei der Rezeptgenerierung berücksichtigt.", + "new_placeholder": "z.B.: Brotbackmaschine, Thermomix, Heißluftfritteuse...", + "quick_title": "Schnell hinzufügen:", + "oven": "🔥 Backofen", + "microwave": "📡 Mikrowelle", + "air_fryer": "🍟 Heißluftfritteuse", + "bread_maker": "🍞 Brotbackmaschine", + "bimby": "🤖 Thermomix/Cookeo", + "mixer": "🌀 Küchenmaschine", + "steamer": "♨️ Dampfgarer", + "pressure_cooker": "🫕 Schnellkochtopf", + "toaster": "🍞 Toaster", + "blender": "🍹 Mixer", + "empty": "Keine Geräte hinzugefügt" }, - "shopping_sections": { - "frutta_verdura": "Obst & Gemüse", - "carne_pesce": "Fleisch & Fisch", - "latticini": "Milchprodukte & Frisches", - "pane_dolci": "Brot & Süßes", - "pasta": "Pasta & Getreide", - "conserve": "Konserven & Soßen", - "surgelati": "Tiefkühl", - "bevande": "Getränke", - "pulizia_igiene": "Reinigung & Hygiene", - "altro": "Sonstiges" + "spesa": { + "title": "🛍️ Online-Einkauf", + "hint": "Online-Einkaufsanbieter konfigurieren.", + "provider_label": "🏪 Anbieter", + "email_label": "📧 E-Mail", + "password_label": "🔒 Passwort", + "login_btn": "🔐 Anmelden", + "ai_prompt_label": "🤖 KI-Produktauswahl Prompt", + "ai_prompt_placeholder": "Anweisungen für die KI bei der Auswahl zwischen mehreren Produkten...", + "ai_prompt_hint": "Die KI verwendet diesen Prompt zur Auswahl des passendsten Produkts. Leer lassen für Standardverhalten.", + "configure_first": "Konfiguriere zuerst den Online-Einkauf in den Einstellungen", + "missing_credentials": "E-Mail und Passwort eingeben", + "login_in_progress": "Anmeldung läuft...", + "login_error_prefix": "Fehler:", + "login_network_error_prefix": "Netzwerkfehler:", + "login_success_default": "Anmeldung erfolgreich!", + "result_name_label": "Name", + "result_card_label": "Karte", + "result_pickup_label": "Abholpunkt", + "result_points_label": "Treuepunkte", + "connected_relogin": "✅ Verbunden — Erneut anmelden", + "connected_as": "Verbunden als {name}" }, - "dashboard": { - "expired_title": "🚫 Abgelaufen", - "expiring_title": "⏰ Bald ablaufend", - "stats_period": "📊 Letzte 30 Tage", - "opened_title": "📦 Geöffnete Produkte", - "review_title": "🔍 Zu prüfen", - "review_hint": "Mengen, die ungewöhnlich erscheinen. Bestätigen oder ändern.", - "quick_recipe": "Schnelles Rezept mit ablaufenden Produkten", - "banner_review_title": "Ungewöhnliche Menge", - "banner_review_action_ok": "Ist korrekt", - "banner_review_action_finish": "🗑️ Alles aufgebraucht", - "banner_review_action_edit": "Korrigieren", - "banner_review_action_weigh": "Wiegen", - "banner_review_dismiss": "Ignorieren", - "banner_prediction_title": "Ungewöhnlicher Verbrauch", - "banner_prediction_hint": "Laut Vorhersage stimmt diese Menge nicht mit dem erwarteten Verbrauch überein.", - "banner_prediction_action_confirm": "{qty} {unit} bestätigen", - "banner_prediction_action_weigh": "Jetzt wiegen", - "banner_prediction_action_edit": "Menge aktualisieren", - "banner_expired_title": "Abgelaufenes Produkt", - "banner_expired_today": "Heute abgelaufen", - "banner_expired_days": "Seit {days} Tagen abgelaufen", - "banner_expired_action_use": "Trotzdem verwenden", - "banner_expired_action_throw": "Habe ich weggeworfen", - "banner_expired_action_edit": "Datum korrigieren", - "banner_anomaly_action_edit": "Bestand korrigieren", - "banner_anomaly_action_dismiss": "Menge ist korrekt", - "banner_no_expiry_title": "Ablaufdatum fehlt: {name}", - "banner_no_expiry_detail": "Dieses Produkt hat kein Ablaufdatum. Möchten Sie eines hinzufügen oder bestätigen, dass es nicht verfällt?", - "banner_no_expiry_action_set": "Ablaufdatum setzen", - "banner_no_expiry_action_dismiss": "Läuft nicht ab ✓", - "banner_no_expiry_toast_dismissed": "Als 'läuft nicht ab' markiert", - "banner_expiring_title": "Bald ablaufend", - "banner_expiring_today": "Läuft heute ab!", - "banner_expiring_tomorrow": "Läuft morgen ab", - "banner_expiring_days": "Läuft in {days} Tagen ab", - "banner_expiring_action_use": "Jetzt verwenden", - "banner_finished_title": "aufgebraucht?", - "banner_finished_detail": "Ich habe vermerkt, dass {name} auf null gesunken ist. Ist es wirklich leer, oder hast du noch welches?", - "banner_finished_action_yes": "Ja, aufgebraucht", - "banner_finished_action_no": "Nein, ich habe noch welches", - "banner_review_unusual_pkg_title": "Ungewöhnliche Packungsgröße", - "banner_review_unusual_pkg_detail": "Du hast eine Packung von {qty} {unit} eingestellt — die Größe scheint sehr groß. Überprüfe ob es korrekt ist.", - "banner_review_low_qty_title": "Sehr geringe Menge", - "banner_review_low_qty_detail": "Du hast nur {qty} im Bestand — das scheint sehr wenig, möglicherweise ein Eingabefehler. Bestätige wenn korrekt.", - "banner_review_high_qty_title": "Ungewöhnlich hohe Menge", - "banner_review_high_qty_detail": "Du hast {qty} im Bestand — die Zahl scheint sehr hoch. Bestätige wenn korrekt oder korrigiere.", - "banner_prediction_rate_day": "Durchschnitt ~{n} {unit}/Tag", - "banner_prediction_rate_week": "Durchschnitt ~{n} {unit}/Woche", - "banner_prediction_days_ago": "Vor {n} Tagen aufgefüllt", - "banner_prediction_more": "Ich erwartete {expected} {unit}{time}, du hast aber {actual} {unit}. Hast du Bestand ohne Buchung hinzugefügt?", - "banner_prediction_less": "Ich erwartete {expected} {unit}{time}, du hast aber nur {actual} {unit}. Hast du mehr als üblich verbraucht?", - "banner_finished_zero": "Bestand zeigt null, aber gespeicherte Buchungen deuten an, dass es nicht leer sein sollte.", - "banner_finished_expected": "Laut Aufzeichnungen solltest du noch {qty} {unit} haben.", - "banner_finished_check": "Kannst du nachschauen?", - "banner_anomaly_phantom_title": "mehr Bestand als erwartet", - "banner_anomaly_phantom_detail": "Bestand zeigt {inv_qty} {unit}, aber laut Buchungen solltest du nur {expected_qty} {unit} haben. Hast du Bestand ohne Buchung hinzugefügt?", - "banner_anomaly_untracked_title": "Anfangsbestand nicht als Eingang gebucht", - "banner_anomaly_untracked_detail": "Du hast {inv_qty} {unit} im Bestand, aber die gebuchten Abgänge übersteigen die Eingänge — der Anfangsbestand wurde wahrscheinlich nie als \"Eingang\" erfasst. Bitte korrigiere die Menge oder trage die fehlenden Eingänge nach.", - "banner_anomaly_ghost_title": "weniger Bestand als erwartet", - "banner_anomaly_ghost_detail": "Laut Buchungen solltest du {expected_qty} {unit} von {name} haben, aber der Bestand zeigt nur {inv_qty} {unit}. Hast du etwas ohne Buchung entnommen?", - "consumed": "Verbraucht: {n} ({pct}%)", - "wasted": "Weggeworfen: {n} ({pct}%)", - "more_opened": "und {n} weitere geöffnet...", - "banner_expired_detail": "{when} · du hast noch {qty}." + "camera": { + "title": "📷 Kamera", + "hint": "Wähle die Kamera für Barcode-Scanning und KI-Identifikation.", + "device_label": "📸 Standardkamera", + "back": "📱 Rückkamera (Standard)", + "front": "🤳 Frontkamera", + "devices_hint": "Bei mehreren Kameras kannst du nach Freigabe der Berechtigungen eine bestimmte aus der Liste oben wählen.", + "detect_btn": "🔄 Kameras erkennen" }, - "inventory": { - "title": "Vorrat", - "filter_all": "Alle", - "search_placeholder": "🔍 Produkt suchen...", - "recent_title": "🕐 Zuletzt verwendet", - "popular_title": "⭐ Meistverwendet", - "empty": "Keine Produkte hier.\nScanne ein Produkt, um es hinzuzufügen!", - "no_items_found": "Keine Bestandseinträge gefunden", - "qty_remainder_suffix": "übrig", - "vacuum_badge": "🫙 Vakuumiert", - "opened_badge": "📭 Geöffnet", - "label_expiry": "📅 Ablaufdatum", - "label_storage": "🫙 Aufbewahrung", - "label_status": "📭 Status", - "opened_since": "Geöffnet seit {date}", - "label_position": "📍 Standort", - "label_quantity": "📦 Menge", - "label_added": "📅 Hinzugefügt", - "empty_text": "Keine Produkte hier.
Scanne ein Produkt, um es hinzuzufügen!", - "empty_db": "Keine Produkte in der Datenbank.
Scanne ein Produkt, um loszulegen!", - "qty_trace": "< 1" + "security": { + "title": "🔒 HTTPS-Zertifikat", + "hint": "Wenn der Browser den Fehler \"Verbindung nicht sicher\" (ERR_CERT_AUTHORITY_INVALID) zeigt, installiere das CA-Zertifikat auf dem Gerät.", + "download_btn": "📥 CA-Zertifikat herunterladen" }, - "scan": { - "title": "Produkt scannen", - "mode_shopping": "🛒 Einkaufsmodus", - "mode_shopping_end": "✅ Einkauf beenden", - "zoom": "Zoom", - "barcode_placeholder": "Barcode eingeben...", - "quick_name_divider": "oder Name eingeben", - "quick_name_placeholder": "z.B.: Äpfel, Zucchini, Brot...", - "manual_entry": "✏️ Manuelle Eingabe", - "ai_identify": "🤖 Mit KI identifizieren", - "hint": "Barcode scannen, Produktname eingeben oder KI zur Identifikation nutzen", - "debug_toggle": "🐛 Debug Log", - "barcode_acquired": "🔖 Barcode gescannt: {code}", - "scan_barcode": "🔖 Barcode scannen", - "create_named": "{name} erstellen", - "new_without_barcode": "Neues Produkt ohne Barcode" + "tts": { + "title": "🔊 Sprache & TTS", + "hint": "Sprachsynthese über externe REST-API konfigurieren. Rezeptschritte und abgelaufene Timer werden an den Endpunkt gesendet.", + "enabled": "✅ TTS aktivieren", + "url_label": "🌐 Endpunkt-URL", + "method_label": "📡 HTTP-Methode", + "auth_label": "🔐 Authentifizierung", + "auth_bearer": "Bearer Token", + "auth_custom": "Benutzerdefinierter Header", + "auth_none": "Keine", + "token_label": "🔑 Bearer Token", + "custom_header_name": "📋 Header-Name", + "custom_header_value": "📋 Header-Wert", + "content_type_label": "📄 Content-Type", + "payload_key_label": "🗝️ Textfeld im Payload", + "payload_key_hint": "Name des JSON-Feldes für den zu lesenden Text (z.B.: message, text).", + "extra_fields_label": "➕ Zusätzliche Felder (JSON)", + "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}", + "extra_fields_hint": "Zusätzliche Felder im Payload, im JSON-Format. Leer lassen wenn nicht benötigt.", + "test_btn": "🔊 Testansage senden" }, - "action": { - "title": "Was möchtest du tun?", - "add_btn": "📥 HINZUFÜGEN", - "add_sub": "in Vorrat/Kühlschrank", - "use_btn": "📤 VERWENDEN / VERBRAUCHEN", - "use_sub": "aus Vorrat/Kühlschrank", - "have_title": "📦 Schon auf Lager!", - "add_more_sub": "weitere Menge", - "use_qty_sub": "wie viel verwendet", - "throw_btn": "🗑️ ENTSORGEN", - "throw_sub": "wegwerfen", - "edit_sub": "Ablauf, Ort…", - "create_recipe_btn": "Rezept damit erstellen" - }, - "add": { - "title": "Zum Vorrat hinzufügen", - "location_label": "📍 Wohin?", - "quantity_label": "📦 Menge", - "conf_size_label": "📦 Jede Packung enthält:", - "conf_size_placeholder": "z.B. 300", - "vacuum_label": "🫙 Vakuumiert", - "vacuum_hint": "Ablaufdatum wird automatisch verlängert", - "submit": "✅ Hinzufügen", - "purchase_type_label": "🛒 Dieses Produkt ist...", - "new_btn": "🆕 Gerade gekauft", - "existing_btn": "📦 Hatte ich schon", - "remaining_label": "📦 Verbleibende Menge", - "remaining_hint": "Ungefähr wie viel ist noch übrig?", - "remaining_full": "🟢 Voll", - "remaining_half": "🟠 Halb", - "estimated_expiry": "Geschätzte Haltbarkeit:", - "suffix_freezer": "(Tiefkühler)", - "suffix_vacuum": "(vakuumversiegelt)", - "hint_modify": "📝 Du kannst das Datum ändern oder mit der Kamera scannen", - "scan_expiry_title": "📷 Ablaufdatum scannen", - "product_added": "✅ {name} hinzugefügt!{qty}", - "suffix_freezer_vacuum": "(Tiefkühler + vakuumversiegelt)" - }, - "use": { - "title": "Verwenden / Verbrauchen", - "location_label": "📍 Woher?", - "quantity_label": "Wie viel hast du benutzt?", - "change": "ändern", - "partial_hint": "Oder genaue Menge angeben:", - "partial_piece_hint": "Hast du nur einen Teil verwendet?", - "piece": "Stück", - "one_whole": "1 ganzes", - "use_all": "🗑️ ALLES verwendet / Aufgebraucht", - "submit": "📤 Diese Menge verwenden", - "available": "📦 Verfügbar:", - "opened_badge": "GEOEFFNET", - "not_in_inventory": "⚠️ Produkt nicht im Bestand.", - "expiry_warning": "⚠️ Verwende zuerst die{loc}, die am {date} abläuft — {when}!", - "throw_title": "🗑️ Produkt entsorgen", - "throw_all": "🗑️ ALLES entsorgen ({qty})", - "throw_qty_label": "Wie viel wegwerfen?", - "throw_qty_hint": "oder Menge angeben:", - "throw_partial_btn": "🗑️ Diese Menge entsorgen", - "when_expired": "seit {n} Tagen abgelaufen", - "when_today": "läuft heute ab", - "when_tomorrow": "läuft morgen ab", - "when_days": "läuft in {n} Tagen ab", - "toast_used": "📤 {qty} von {name} verwendet", - "toast_bring": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt", - "toast_opened_finished": "🔓 Geöffnete Packung von {name} aufgebraucht!", - "disambiguation_hint": "Was meinst du mit \"alles aufgebraucht\"?", - "disambiguation_all": "🗑️ ALLES verbraucht ({qty})", - "error_exceeds_stock": "⚠️ Du kannst nicht mehr verwenden als du verfügbar hast!", - "use_all_confirm_title": "✅ Alles aufbrauchen", - "use_all_confirm_msg": "Bestätige, dass du das Produkt vollständig aufgebraucht hast:", - "use_all_confirm_btn": "✅ Ja, aufgebraucht", - "throw_all_confirm_title": "🗑️ Alles entsorgen", - "throw_all_confirm_msg": "Möchtest du wirklich das gesamte Produkt entsorgen?", - "throw_all_confirm_btn": "🗑️ Ja, entsorgen" - }, - "product": { - "title_new": "Neues Produkt", - "title_edit": "Produkt bearbeiten", - "ai_fill": "📷 Foto machen und mit KI identifizieren", - "ai_fill_hint": "KI füllt die Produktfelder automatisch aus", - "name_label": "🏷️ Produktname *", - "name_placeholder": "z.B.: Vollmilch, Penne Nudeln...", - "brand_label": "🏢 Marke", - "brand_placeholder": "z.B.: Barilla, Müller, Knorr...", - "category_label": "📂 Kategorie", - "unit_label": "📏 Maßeinheit", - "default_qty_label": "🔢 Standardmenge", - "conf_size_label": "📦 Jede Packung enthält:", - "conf_size_placeholder": "z.B. 300", - "notes_label": "📝 Notizen", - "notes_placeholder": "z.B.: laktosefrei, bio, nach dem Öffnen im Kühlschrank aufbewahren...", - "barcode_label": "🔖 Barcode", - "barcode_placeholder": "Barcode (falls vorhanden)", - "barcode_hint": "⚠️ Barcode hinzufügen, damit du beim nächsten Einkauf nur scannen musst!", - "submit": "💾 Produkt speichern", - "name_required": "Produktname eingeben", - "conf_size_required": "Packungsinhalt angeben", - "expiry_estimated": "Geschätztes Ablaufdatum:", - "scan_expiry": "Ablaufdatum scannen", - "expiry_hint": "📝 Du kannst das Datum ändern oder mit der Kamera scannen", - "add_batch": "📦 + Charge mit anderem Ablaufdatum", - "package_info": "📦 Packung: {info}", - "edit_catalog": "⚙️ Produktinfo bearbeiten (Name, Marke, Kategorie…)", - "not_recognized": "⚠️ Produkt nicht erkannt", - "edit_info": "✏️ Informationen bearbeiten", - "modify_details": "BEARBEITEN\nAblauf, Ort…", - "already_in_pantry": "📋 Bereits im Vorratsschrank", - "no_barcode": "Kein Barcode" - }, - "products": { - "title": "📦 Alle Produkte", - "search_placeholder": "🔍 Produkt suchen...", - "empty": "Keine Produkte in der Datenbank.\nScanne ein Produkt zum Starten!", - "no_category": "Keine Produkte in dieser Kategorie" - }, - "recipes": { - "title": "🍳 Rezepte", - "generate": "✨ Neues Rezept generieren", - "archive_empty": "Keine Rezepte gespeichert. Erstelle dein erstes Rezept!", - "dialog_title": "🍳 Rezept", - "dialog_desc": "Ich erstelle ein gesundes Rezept mit Zutaten aus dem Vorrat und priorisiere Produkte mit nahendem Ablaufdatum.", - "meal_label": "🕐 Für welche Mahlzeit?", - "persons_label": "👥 Für wie viele Personen?", - "meal_type_label": "🎯 Art der Mahlzeit", - "opt_fast": "⚡ Schnelle Mahlzeit", - "opt_light": "🥗 Kleiner Hunger", - "opt_expiry": "⏰ Ablaufdaten priorisieren", - "opt_healthy": "💚 Extra gesund", - "opt_opened": "📦 Geöffnete Produkte priorisieren", - "opt_zero_waste": "♻️ Zero Waste", - "generate_btn": "✨ Rezept generieren", - "loading_msg": "Rezept wird vorbereitet...", - "start_cooking": "👨‍🍳 Kochmodus", - "regenerate": "🔄 Noch eins generieren", - "close_btn": "✅ Schließen", - "ingredients_title": "🧾 Zutaten", - "steps_title": "👨‍🍳 Zubereitung", - "no_steps": "Keine Zubereitungsschritte verfügbar", - "generate_error": "Fehler bei der Generierung", - "persons_short": "Pers.", - "use_ingredient_title": "Zutat verwenden", - "recipe_qty_label": "Rezept", - "from_where_label": "Von wo?", - "amount_label": "Wie viel", - "use_amount_btn": "Diese Menge verwenden", - "use_all_btn": "ALLES verwenden / Aufgebraucht", - "packs_label": "Packungen", - "quantity_in_total": "Menge in {unit} (gesamt: {total})", - "packs_of_have": "Packungen à {size} (du hast {count} Pack.)", - "scale_wait_stable": "10s stabiles Gewicht für Auto-Ausfüllen abwarten…", - "ingredient_scaled_toast": "📦 Zutat vom Vorrat abgezogen!", - "finished_added_bring_toast": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt!", - "load_error": "Fehler beim Laden" - }, - "shopping": { - "title": "🛒 Einkaufsliste", - "bring_loading": "Verbindung zu Bring!...", - "bring_not_configured": "Bring! ist nicht konfiguriert. Füge E-Mail und Passwort in den Einstellungen hinzu.", - "tab_to_buy": "🛍️ Zu kaufen", - "tab_forecast": "🧠 Vorhersage", - "total_label": "💰 Geschätzter Gesamtbetrag", - "section_to_buy": "🛍️ Zu kaufen", - "suggestions_title": "💡 KI-Vorschläge", - "suggestions_add": "✅ Ausgewählte zu Bring! hinzufügen", - "search_prices": "🔍 Alle Preise suchen", - "suggest_btn": "Einkaufsvorschläge", - "smart_title": "🧠 Intelligente Vorhersagen", - "smart_empty": "Keine Vorhersagen verfügbar.
Füge Produkte zur Vorratskammer hinzu, um intelligente Vorhersagen zu erhalten.", - "smart_filter_all": "Alle", - "smart_filter_critical": "🔴 Dringend", - "smart_filter_high": "🟠 Bald", - "smart_filter_medium": "🟡 Planen", - "smart_filter_low": "🟢 Vorhersage", - "smart_add": "🛒 Ausgewählte zu Bring! hinzufügen", - "empty": "Einkaufsliste leer!\nNutze den Button unten, um Vorschläge zu generieren.", - "already_in_list": "🛒 \"{name}\" ist bereits in der Einkaufsliste", - "already_in_list_short": "ℹ️ Bereits in der Einkaufsliste", - "add_prompt": "Möchtest du es zur Einkaufsliste hinzufügen?", - "smart_already": "📊 Intelligenter Einkauf sagt bereits {name} voraus", - "all_searched": "Alle Produkte wurden bereits gesucht. Nutze 🔄 für einzelne Suchen.", - "search_complete": "Suche abgeschlossen: {count} Produkte", - "suggest_buy": "🛒 Kaufen: {qty} {unit}", - "suggest_buy_approx": "🛒 Mindestens: {qty} {unit}", - "suggest_buy_tip": "Empfohlene Menge basierend auf dem Verbrauch der letzten 14 Tage", - "suggest_buy_approx_tip": "Mindestschätzung basierend auf Verbrauch (nächste Packungsgröße kaufen)", - "removed_sufficient": "🧹 {removed} Produkt(e) mit ausreichendem Bestand von der Liste entfernt", - "bring_badge": "🛒 Schon auf Bring!", - "add_urgent_toast": "🔴 {n} dringende(s) Produkt(e) automatisch zu Bring! hinzugefügt", - "migration_done": "✅ {migrated} aktualisiert, {skipped} bereits ok", - "added_to_bring": "🛒 {n} Produkte zu Bring! hinzugefügt", - "added_to_bring_skip": "{n} bereits vorhanden", - "all_on_bring": "Alle Produkte waren bereits auf Bring!", - "freq_high": "📈 Häufig", - "freq_regular": "📊 Regelmäßig", - "freq_occasional": "📉 Gelegentlich", - "out_of_stock": "Ausverkauft", - "scan_toast": "📷 Scannen: {name}", - "empty_category": "Keine Produkte in dieser Kategorie", - "session_empty": "🛒 Noch keine Produkte", - "urgency_critical": "Dringend", - "urgency_high": "Bald", - "urgency_medium": "Planen", - "urgency_low": "Vorschau", - "urgency_medium_short": "Mittel", - "urgency_low_short": "Ok", - "tag_urgent": "🔴 Dringend", - "tag_priority": "⭐ Priorität", - "tag_check": "✅ Prüfen", - "smart_already_predicted": "📊 Einkauf wird bereits vorhergesagt: {name}{urgency}.", - "item_removed": "✅ {name} von der Liste entfernt!", - "urgency_spec_critical": "⚡ Dringend", - "urgency_spec_high": "🟠 Bald", - "bring_add_n": "{n} zu Bring! hinzufügen", - "bring_add_selected": "Ausgewählte zu Bring! hinzufügen", - "bring_adding": "Wird hinzugefügt...", - "bring_added_one": "1 Produkt zu Bring! hinzugefügt", - "bring_added_many": "{n} Produkte zu Bring! hinzugefügt", - "bring_skipped": "({n} bereits in Liste)", - "force_sync": "Bring!-Synchronisierung erzwingen", - "scan_target_label": "Du suchst", - "scan_target_found": "Gefunden! Aus Liste entfernen", - "bring_add_one": "1 Produkt zu Bring! hinzufügen", - "bring_add_many": "{n} Produkte zu Bring! hinzufügen", - "syncing": "Synchronisiere…", - "sync_done": "Synchronisierung abgeschlossen", - "price_searching": "Suche...", - "search_action": "Suchen", - "open_action": "Öffnen", - "not_found": "Nicht gefunden", - "search_price": "Preis suchen", - "tap_to_scan": "Zum Scannen tippen", - "tag_title": "Tag", - "remove_title": "Entfernen", - "found_count": "{found}/{total} Produkte gefunden", - "savings_offers": "· 🏷️ Du sparst €{amount} mit Angeboten", - "searching_progress": "Suche {current}/{total}...", - "remove_error": "Fehler beim Entfernen", - "btn_fetch_prices": "Preise suchen", - "price_total_label": "💰 Geschätzter Gesamtpreis:", - "price_loading": "Preise werden gesucht…", - "price_not_found": "Preis n/v", - "suggest_loading": "Analyse läuft...", - "suggest_error": "Fehler bei der Vorschlagserstellung", - "priority_high": "Hoch", - "priority_medium": "Mittel", - "priority_low": "Niedrig" - }, - "ai": { - "title": "🤖 KI-Identifikation", - "capture": "📸 Foto aufnehmen", - "retake": "🔄 Neu aufnehmen", - "hint": "Mache ein Foto des Produkts und die KI versucht es zu identifizieren", - "identifying": "🤖 Identifiziere Produkt...", - "no_api_key": "⚠️ Gemini API-Schlüssel nicht konfiguriert.\nFüge GEMINI_API_KEY in der .env Datei auf dem Server hinzu.", - "fields_filled": "✅ Felder von KI ausgefüllt", - "use_data": "✅ KI-Daten verwenden", - "use_data_no_barcode": "✅ KI-Daten verwenden (ohne Barcode)" - }, - "log": { - "title": "📒 Verlauf", - "type_added": "Hinzugefügt", - "type_waste": "Entsorgt", - "type_used": "Verwendet", - "type_bring": "Zu Bring! hinzugefügt", - "undone_badge": "Rückgängig", - "undo_title": "Diese Operation rückgängig machen", - "load_error": "Fehler beim Laden des Verlaufs", - "empty": "Keine Operationen aufgezeichnet.", - "undo_action_remove": "Entfernen von", - "undo_action_restore": "Wiederherstellen von", - "undo_confirm": "Vorgang rückgängig machen?\n→ {action} {name}", - "undo_success": "↩ Vorgang rückgängig gemacht für {name}", - "already_undone": "Vorgang bereits rückgängig gemacht", - "too_old": "Vorgänge älter als 24 Stunden können nicht rückgängig gemacht werden", - "undo_error": "Fehler beim Rückgängigmachen" - }, - "chat": { - "title": "Gemini Chef", - "welcome": "Hallo! Ich bin dein Küchenassistent", - "welcome_desc": "Frag mich, dir einen Saft, einen Snack, ein schnelles Gericht zu machen... Ich kenne deinen Vorrat, deine Geräte und deine Vorlieben!", - "suggestion_snack": "🍿 Schneller Snack", - "suggestion_juice": "🥤 Saft/Smoothie", - "suggestion_light": "🥗 Etwas Leichtes", - "suggestion_expiry": "⏰ Ablaufende nutzen", - "clear": "Neues Gespräch", - "placeholder": "Frag etwas...", - "cleared": "Chat geleert", - "suggestion_snack_text": "Was kann ich als schnellen Snack machen?", - "suggestion_juice_text": "Mach mir einen Saft oder Smoothie mit dem was ich habe", - "suggestion_light_text": "Ich habe Hunger, möchte aber etwas Leichtes", - "suggestion_expiry_text": "Was läuft bald ab und wie kann ich es verwenden?", - "transfer_to_recipes": "Zu Rezepten hinzufügen", - "transferring": "Übertrage...", - "transferred": "Zu Rezepten hinzugefügt!", - "open_recipe": "Rezept öffnen" - }, - "cooking": { - "close": "Schließen", - "tts_btn": "Vorlesen", - "restart": "↺ Neustart", - "replay": "🔊 Nochmal", - "timer": "⏱️ {time} · Timer", - "prev": "◀ Zurück", - "next": "Weiter ▶", - "ingredient_used": "✔️ Abgezogen", - "ingredient_use_btn": "📦 Verwenden", - "ingredient_deduct_title": "Von Vorrat abziehen", - "timer_expired_tts": "Timer {label} abgelaufen!", - "timer_warning_tts": "Achtung! {label}: noch 10 Sekunden!", - "recipe_done_tts": "Rezept abgeschlossen! Guten Appetit!", - "expires_chip": "läuft ab {date}", - "finish": "✅ Fertig" - }, - "settings": { - "title": "⚙️ Einstellungen", - "tab_api": "API Keys", - "tab_bring": "Bring!", - "tab_recipe": "Rezepte", - "tab_mealplan": "Wochenplan", - "tab_appliances": "Geräte", - "tab_spesa": "Online-Einkauf", - "tab_camera": "Kamera", - "tab_security": "Sicherheit", - "tab_tts": "Sprache (TTS)", - "tab_language": "Sprache", - "tab_scale": "Smart-Waage", - "gemini": { - "title": "🤖 Google Gemini AI", - "hint": "API-Schlüssel für Produkterkennung, Ablaufdaten und Rezepte.", - "key_label": "Gemini API Key" - }, - "bring": { - "title": "🛒 Bring! Einkaufsliste", - "hint": "Zugangsdaten für die Bring! Einkaufslisten-Integration.", - "email_label": "📧 Bring! E-Mail", - "password_label": "🔒 Bring! Passwort" - }, - "price": { - "title": "💰 Preisschätzung (KI)", - "hint": "Zeigt geschätzte Kosten pro Produkt in der Einkaufsliste mithilfe von KI an.", - "enabled_label": "Preisschätzung aktivieren", - "country_label": "🌍 Referenzland", - "currency_label": "💱 Währung", - "update_label": "🔄 Preise aktualisieren alle", - "update_suffix": "Monate" - }, - "recipe": { - "title": "🍳 Rezept-Einstellungen", - "hint": "Konfiguriere die Standardoptionen für die Rezeptgenerierung.", - "persons_label": "👥 Standard-Portionen", - "options_label": "🎯 Standard-Rezeptoptionen", - "fast": "⚡ Schnelles Gericht", - "light": "🥗 Leichte Mahlzeit", - "expiry": "⏰ Ablauf-Priorität", - "healthy": "💚 Extra Gesund", - "opened": "📦 Offene Produkte zuerst", - "zerowaste": "♻️ Keine Verschwendung", - "dietary_label": "🚫 Unverträglichkeiten / Einschränkungen", - "dietary_placeholder": "z.B.: glutenfrei, laktosefrei, vegetarisch..." - }, - "mealplan": { - "title": "📅 Wöchentlicher Essensplan", - "hint": "Lege die Mahlzeitenart für jeden Tag fest. Wird als Leitfaden bei der Rezeptgenerierung verwendet.", - "enabled": "✅ Wöchentlichen Essensplan aktivieren", - "legend": "🌤️ = Mittagessen · 🌙 = Abendessen · Tippe auf ein Badge, um es zu ändern.", - "types_title": "📋 Verfügbare Typen" - }, - "appliances": { - "title": "🔌 Verfügbare Geräte", - "hint": "Gib an, welche Geräte du hast. Sie werden bei der Rezeptgenerierung berücksichtigt.", - "new_placeholder": "z.B.: Brotbackmaschine, Thermomix, Heißluftfritteuse...", - "quick_title": "Schnell hinzufügen:", - "oven": "🔥 Backofen", - "microwave": "📡 Mikrowelle", - "air_fryer": "🍟 Heißluftfritteuse", - "bread_maker": "🍞 Brotbackmaschine", - "bimby": "🤖 Thermomix/Cookeo", - "mixer": "🌀 Küchenmaschine", - "steamer": "♨️ Dampfgarer", - "pressure_cooker": "🫕 Schnellkochtopf", - "toaster": "🍞 Toaster", - "blender": "🍹 Mixer", - "empty": "Keine Geräte hinzugefügt" - }, - "spesa": { - "title": "🛍️ Online-Einkauf", - "hint": "Online-Einkaufsanbieter konfigurieren.", - "provider_label": "🏪 Anbieter", - "email_label": "📧 E-Mail", - "password_label": "🔒 Passwort", - "login_btn": "🔐 Anmelden", - "ai_prompt_label": "🤖 KI-Produktauswahl Prompt", - "ai_prompt_placeholder": "Anweisungen für die KI bei der Auswahl zwischen mehreren Produkten...", - "ai_prompt_hint": "Die KI verwendet diesen Prompt zur Auswahl des passendsten Produkts. Leer lassen für Standardverhalten.", - "configure_first": "Konfiguriere zuerst den Online-Einkauf in den Einstellungen", - "missing_credentials": "E-Mail und Passwort eingeben", - "login_in_progress": "Anmeldung läuft...", - "login_error_prefix": "Fehler:", - "login_network_error_prefix": "Netzwerkfehler:", - "login_success_default": "Anmeldung erfolgreich!", - "result_name_label": "Name", - "result_card_label": "Karte", - "result_pickup_label": "Abholpunkt", - "result_points_label": "Treuepunkte", - "connected_relogin": "✅ Verbunden — Erneut anmelden", - "connected_as": "Verbunden als {name}" - }, - "camera": { - "title": "📷 Kamera", - "hint": "Wähle die Kamera für Barcode-Scanning und KI-Identifikation.", - "device_label": "📸 Standardkamera", - "back": "📱 Rückkamera (Standard)", - "front": "🤳 Frontkamera", - "devices_hint": "Bei mehreren Kameras kannst du nach Freigabe der Berechtigungen eine bestimmte aus der Liste oben wählen.", - "detect_btn": "🔄 Kameras erkennen" - }, - "security": { - "title": "🔒 HTTPS-Zertifikat", - "hint": "Wenn der Browser den Fehler \"Verbindung nicht sicher\" (ERR_CERT_AUTHORITY_INVALID) zeigt, installiere das CA-Zertifikat auf dem Gerät.", - "download_btn": "📥 CA-Zertifikat herunterladen" - }, - "tts": { - "title": "🔊 Sprache & TTS", - "hint": "Sprachsynthese über externe REST-API konfigurieren. Rezeptschritte und abgelaufene Timer werden an den Endpunkt gesendet.", - "enabled": "✅ TTS aktivieren", - "url_label": "🌐 Endpunkt-URL", - "method_label": "📡 HTTP-Methode", - "auth_label": "🔐 Authentifizierung", - "auth_bearer": "Bearer Token", - "auth_custom": "Benutzerdefinierter Header", - "auth_none": "Keine", - "token_label": "🔑 Bearer Token", - "custom_header_name": "📋 Header-Name", - "custom_header_value": "📋 Header-Wert", - "content_type_label": "📄 Content-Type", - "payload_key_label": "🗝️ Textfeld im Payload", - "payload_key_hint": "Name des JSON-Feldes für den zu lesenden Text (z.B.: message, text).", - "extra_fields_label": "➕ Zusätzliche Felder (JSON)", - "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}", - "extra_fields_hint": "Zusätzliche Felder im Payload, im JSON-Format. Leer lassen wenn nicht benötigt.", - "test_btn": "🔊 Testansage senden" - }, - "language": { - "title": "🌐 Sprache", - "hint": "Wähle die Sprache der Benutzeroberfläche.", - "label": "🌐 Sprache", - "restart_notice": "Die Seite wird neu geladen, um die neue Sprache anzuwenden." - }, - "screensaver": { - "label": "Bildschirmschoner aktivieren", - "card_title": "🌙 Bildschirmschoner", - "card_hint": "Zeigt nach 5 Minuten Inaktivität eine Uhr mit nützlichen Fakten. Standardmäßig deaktiviert." - }, - "scale": { - "title": "⚖️ Smart-Waage", - "hint": "Verbinde eine Bluetooth-Waage über das Android-Gateway, um das Gewicht automatisch auszulesen.", - "tab": "Smart-Waage", - "enabled": "✅ Smart-Waage aktivieren", - "url_label": "🌐 WebSocket-Gateway-URL", - "url_placeholder": "ws://192.168.1.x:8765", - "url_hint": "URL der Android-App (gleiches WLAN). z.B.:", - "test_btn": "🔗 Verbindung testen", - "download_btn": "📥 Android-Gateway herunterladen (APK)", - "download_hint": "Android-App als Brücke zwischen BLE-Waage und EverShelf.", - "download_sub": "Quellcode: evershelf-scale-gateway/ im Projektstamm" - }, - "kiosk": { - "hint": "Verwandeln Sie ein Android-Tablet in ein EverShelf-Panel mit integriertem BLE-Waagen-Gateway.", - "download_btn": "📥 EverShelf Kiosk herunterladen (APK)", - "download_sub": "Vollbild-Kioskmodus + integriertes Waagen-Gateway. Quellcode: evershelf-kiosk/" - }, - "saved": "✅ Konfiguration gespeichert!", - "saved_local": "✅ Konfiguration lokal gespeichert", - "saved_local_error": "⚠️ Lokal gespeichert, Serverfehler: {error}" - }, - "expiry": { - "today": "HEUTE", - "tomorrow": "Morgen", - "days": "{days} Tage", - "expired_days": "Seit {days}T", - "expired_yesterday": "Seit gestern", - "expired_today": "Heute", - "badge_today": "⚠️ Läuft heute ab!", - "badge_tomorrow": "⏰ Morgen", - "badge_tomorrow_long": "⏰ Läuft morgen ab", - "badge_days": "⏰ {n} Tage", - "badge_expired_ago": "⚠️ Seit {n}T abgel.", - "badge_expired": "⛔ Abgelaufen!", - "badge_stable": "✅ Stabil", - "badge_expiring_short": "⏰ Läuft in {n}T ab", - "badge_ok_still": "✅ Noch {n}T", - "badge_expires_red": "🔴 In {n}T", - "badge_expires_yellow": "🟡 In {n}T", - "badge_expired_bare": "⚠️ Abgelaufen", - "badge_expires_warn": "⚠️ In {n}T", - "badge_days_left": "⏳ ~{n}T übrig", - "days_approx": "~{n} Tage", - "weeks_approx": "~{n} Wochen", - "months_approx": "~{n} Monate", - "years_approx": "~{n} Jahre", - "expired_today_long": "Heute abgelaufen", - "expired_ago_long": "Seit {n} Tagen abgelaufen", - "expired_suffix": "— Abgelaufen!", - "expired_suffix_ok": "— Abgelaufen (noch ok)", - "expired_suffix_warning": "— Abgelaufen (erst prüfen)", - "days_compact": "{n}T" - }, - "status": { - "ok": "OK", - "check": "Prüfen", - "discard": "Entsorgen", - "tip_freezer_ok": "Im Gefrierschrank: noch sicher (~{n}T Puffer)", - "tip_freezer_check": "Seit langem im Gefrierschrank, könnte an Qualität verloren haben. Bald verbrauchen", - "tip_freezer_danger": "Zu lange im Gefrierschrank, Gefrierbrand- und Qualitätsverlust-Risiko", - "tip_highRisk_check": "Kürzlich abgelaufen, Geruch und Aussehen vor dem Verzehr prüfen", - "tip_highRisk_danger": "Verderbliches Produkt abgelaufen: aus Sicherheitsgründen entsorgen", - "tip_medRisk_check1": "Aussehen und Geruch vor dem Verzehr prüfen", - "tip_medRisk_check2": "Schon eine Weile abgelaufen, vor dem Verzehr gut prüfen", - "tip_medRisk_danger": "Zu lange seit dem Ablaufdatum, lieber entsorgen", - "tip_lowRisk_ok": "Haltbares Produkt, noch sicher zu verzehren", - "tip_lowRisk_check": "Seit über einem Monat abgelaufen, Verpackungsintegrität prüfen", - "tip_lowRisk_danger": "Zu lange abgelaufen, besser kein Risiko eingehen" - }, - "toast": { - "product_saved": "Produkt gespeichert!", - "product_created": "Produkt erstellt!", - "product_updated": "✅ Produkt aktualisiert!", - "product_removed": "Produkt entfernt", - "updated": "Aktualisiert!", - "quantity_confirmed": "✓ Menge bestätigt", - "added_to_inventory": "✅ {name} hinzugefügt!", - "removed_from_list": "✅ {name} von der Liste entfernt!", - "removed_from_list_short": "Von der Liste entfernt", - "added_to_shopping": "🛒 Zur Einkaufsliste hinzugefügt!", - "removed_from_shopping": "🛒 Von der Einkaufsliste entfernt", - "finished_to_bring": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt", - "thrown_away": "🗑️ {name} weggeworfen!", - "thrown_away_partial": "🗑️ {qty} {unit} von {name} weggeworfen", - "product_finished_confirmed": "✅ Entfernt — wieder hinzufügen, wenn du nachkaufst", - "appliance_added": "Gerät hinzugefügt", - "item_added": "{name} hinzugefügt" - }, - "antiwaste": { - "title": "🌱 Anti-Verschwendungs-Bericht", - "grade_label": "Note", - "you": "Du", - "avg_label": "Ø", - "better": "🎉 Du verlierst {diff}% weniger als der {country}!", - "worse": "⚠️ Du verlierst mehr als der {country}. Verbesserungspotenzial!", - "on_par": "→ Du liegst beim {country}. Du kannst noch besser werden!", - "saved_money": "~{amount}/Monat gespart", - "saved_meals": "~{n} Mahlzeiten gerettet", - "saved_co2": "{n} kg CO₂ eingespart", - "trend_title": "Trend (letzte 3 Monate)", - "months_ago_2": "-60 Tage", - "months_ago_1": "-30 Tage", - "this_month": "Jetzt", - "country_it": "ital. Durchschnitt", - "country_de": "dt. Durchschnitt", - "country_en": "US-Durchschnitt", - "source": "Quellen: REDUCE, Eurostat, USDA 2021", - "live_on": "Live-Daten", - "live_off": "Offline", - "meals": "Mahlzeiten", - "annual_info": "📅 Du ~{you} kg/Jahr · Ø ~{avg} kg/Jahr", - "badge_rate": "Verlustquote", - "badge_saved_money": "gespart vs Ø", - "badge_wasted": "verloren", - "badge_better": "weniger als Ø" - }, - "error": { - "generic": "Fehler", - "network": "Netzwerkfehler", - "no_api_key": "API-Schluessel in den Einstellungen konfigurieren", - "loading": "Fehler beim Laden des Produkts", - "not_found": "Produkt nicht gefunden", - "not_found_manual": "Produkt nicht gefunden. Manuell eingeben.", - "search": "Suchfehler. Nochmal versuchen.", - "search_short": "Suchfehler", - "save": "Fehler beim Speichern", - "connection": "Verbindungsfehler", - "camera": "Kamera nicht verfügbar", - "bring_add": "Fehler beim Hinzufügen zu Bring!", - "bring_connection": "Bring! Verbindungsfehler", - "identification": "Identifikationsfehler", - "ai_quota": "KI-Kontingent erschöpft. Bitte in ein paar Minuten erneut versuchen.", - "barcode_empty": "Barcode eingeben", - "barcode_format": "Barcode darf nur Zahlen enthalten (4-14 Ziffern)", - "min_chars": "Mindestens 2 Zeichen eingeben", - "not_in_inventory": "Produkt nicht im Bestand", - "appliance_exists": "Gerät bereits vorhanden", - "already_exists": "Bereits vorhanden", - "network_retry": "Verbindungsfehler. Erneut versuchen.", - "select_items": "Wähle mindestens ein Produkt aus", - "server_offline": "Serververbindung unterbrochen", - "server_restored": "Serververbindung wiederhergestellt", - "server_retry": "Erneut versuchen" - }, - "confirm": { - "remove_item": "Möchtest du dieses Produkt wirklich aus dem Bestand entfernen?", - "kiosk_exit": "Kioskmodus verlassen?", - "cancel": "Abbrechen", - "proceed": "Bestätigen" - }, - "location": { - "dispensa": "Vorratskammer", - "frigo": "Kühlschrank", - "freezer": "Gefrierschrank" - }, - "edit": { - "title": "{name} bearbeiten", - "unknown_hint": "Produktname und Informationen eingeben", - "label_name": "🏷️ Produktname" + "language": { + "title": "🌐 Sprache", + "hint": "Wähle die Sprache der Benutzeroberfläche.", + "label": "🌐 Sprache", + "restart_notice": "Die Seite wird neu geladen, um die neue Sprache anzuwenden." }, "screensaver": { - "recipe_btn": "Rezepte", - "scan_btn": "Produkt scannen" - }, - "days": { - "mon": "Montag", - "tue": "Dienstag", - "wed": "Mittwoch", - "thu": "Donnerstag", - "fri": "Freitag", - "sat": "Samstag", - "sun": "Sonntag", - "mon_short": "Mo", - "tue_short": "Di", - "wed_short": "Mi", - "thu_short": "Do", - "fri_short": "Fr", - "sat_short": "Sa", - "sun_short": "So" - }, - "meal_types": { - "lunch": "Mittagessen", - "dinner": "Abendessen", - "colazione": "Frühstück", - "merenda": "Nachmittagssnack", - "dolce": "Dessert", - "succo": "Fruchtsaft", - "pranzo": "Mittagessen", - "cena": "Abendessen" + "label": "Bildschirmschoner aktivieren", + "card_title": "🌙 Bildschirmschoner", + "card_hint": "Zeigt nach 5 Minuten Inaktivität eine Uhr mit nützlichen Fakten. Standardmäßig deaktiviert." }, "scale": { - "status_connected": "Waage verbunden", - "status_searching": "Gateway verbunden, warte auf Waage…", - "status_disconnected": "Waagen-Gateway nicht erreichbar", - "status_error": "Verbindungsfehler zum Gateway", - "not_connected": "Waagen-Gateway nicht verbunden", - "read_btn": "⚖️ Von Waage lesen", - "reading_title": "Waage lesen", - "place_on_scale": "Produkt auf die Waage legen…", - "waiting_stable": "Das Gewicht wird automatisch erfasst, wenn die Messung stabil ist.", - "no_url": "Gateway-URL eingeben", - "testing": "⏳ Verbindung wird getestet…", - "connected_ok": "Gateway-Verbindung erfolgreich!", - "timeout": "Timeout: keine Antwort vom Gateway", - "error_connect": "Verbindung zum Gateway nicht möglich", - "tab": "Smart-Waage", - "low_weight": "Gewicht < 10 g · manuell eingeben\n(Auto-Erkennung erfordert mind. 10 g)", - "density_hint": "(Dichte {density} g/ml)", - "ml_hint": "(wird in ml umgerechnet)", - "weight_detected": "Gewicht erkannt — 10s Stabilität abwarten…", - "weight_too_low": "Gewicht zu niedrig — warten…", - "stable": "✓ Stabil", - "auto_confirm": "✅ {val} {unit} — Auto-Bestätigung in 5s (tippen zum Abbrechen)", - "cancelled_replace": "Abgebrochen — lege die Zutat wieder auf die Waage, um fortzufahren" + "title": "⚖️ Smart-Waage", + "hint": "Verbinde eine Bluetooth-Waage über das Android-Gateway, um das Gewicht automatisch auszulesen.", + "tab": "Smart-Waage", + "enabled": "✅ Smart-Waage aktivieren", + "url_label": "🌐 WebSocket-Gateway-URL", + "url_placeholder": "ws://192.168.1.x:8765", + "url_hint": "URL der Android-App (gleiches WLAN). z.B.:", + "test_btn": "🔗 Verbindung testen", + "download_btn": "📥 Android-Gateway herunterladen (APK)", + "download_hint": "Android-App als Brücke zwischen BLE-Waage und EverShelf.", + "download_sub": "Quellcode: evershelf-scale-gateway/ im Projektstamm" }, - "prediction": { - "expected_qty": "Erwartet: {expected} {unit}", - "actual_qty": "Aktuell: {actual} {unit}", - "check_suggestion": "Überprüfe oder wiege die Restmenge" + "kiosk": { + "hint": "Verwandeln Sie ein Android-Tablet in ein EverShelf-Panel mit integriertem BLE-Waagen-Gateway.", + "download_btn": "📥 EverShelf Kiosk herunterladen (APK)", + "download_sub": "Vollbild-Kioskmodus + integriertes Waagen-Gateway. Quellcode: evershelf-kiosk/" }, - "date": { - "today": "📅 Heute", - "yesterday": "📅 Gestern" - }, - "scanner": { - "title_barcode": "🔖 Barcode scannen", - "barcode_hint": "Produktbarcode einrahmen", - "barcode_manual_placeholder": "Oder manuell eingeben...", - "barcode_use_btn": "✅ Diesen Code verwenden", - "ai_identifying": "🤖 Produkt wird erkannt...", - "ai_analyzing": "🤖 KI-Analyse läuft...", - "product_label_hint": "Produktetikett einrahmen", - "expiry_label_hint": "Ablaufdatum auf dem Produkt einrahmen", - "capture_btn": "📸 Aufnehmen", - "capture_photo_btn": "📸 Foto aufnehmen", - "retake_btn": "🔄 Erneut aufnehmen", - "camera_error_hint": "Stelle sicher, dass du HTTPS verwendest und Kameraberechtigungen erteilt hast.
Du kannst den Barcode manuell eingeben oder die KI-Identifikation verwenden.", - "no_barcode": "Kein Barcode" - }, - "lowstock": { - "title": "⚠️ Wird knapp!", - "message": "{name} wird knapp — nur noch {qty} übrig.", - "question": "Möchtest du es zur Einkaufsliste hinzufügen?", - "yes": "🛒 Ja, zu Bring! hinzufügen", - "no": "Nein, passt für jetzt" - }, - "move": { - "title": "📦 Den Rest bewegen?", - "question": "Möchtest du {thing} von {name} an einen anderen Ort bewegen?", - "question_short": "Möchtest du {thing} an einen anderen Ort bewegen?", - "thing_opened": "die offene Packung", - "thing_rest": "den Rest", - "stay_btn": "Nein, bleibt in {location}", - "moved_toast": "📦 Offene Packung bewegt nach {location}", - "vacuum_restore": "🫙 Vakuum wiederherstellen" - }, - "nova": { - "1": "Unverarbeitet", - "2": "Kulinarische Zutat", - "3": "Verarbeitet", - "4": "Hochverarbeitet" - }, - "meal_plan_types": { - "pasta": "Pasta", - "riso": "Reis", - "carne": "Fleisch", - "pesce": "Fisch", - "legumi": "Hülsenfrüchte", - "uova": "Eier", - "formaggio": "Käse", - "pizza": "Pizza", - "affettati": "Aufschnitt", - "verdure": "Gemüse", - "zuppa": "Suppe", - "insalata": "Salat", - "pane": "Brot/Sandwich", - "dolce": "Dessert", - "libero": "Frei" - }, - "meal_sub": { - "dolce_torta": "Kuchen", - "dolce_crema": "Creme / Pudding", - "dolce_crumble": "Crumble / Tarte", - "dolce_biscotti": "Kekse / Gebäck", - "dolce_frutta": "Fruchtdessert", - "succo_dolce": "Süß / Fruchtig", - "succo_energizzante": "Energetisierend", - "succo_detox": "Detox / Grün", - "succo_rinfrescante": "Erfrischend", - "succo_vitaminico": "Vitamin / Zitrus" - }, - "meal_plan": { - "reset_success": "Wochenplan zurückgesetzt", - "not_available": "nicht im Vorrat verfügbar", - "suggested_by": "vom Wochenplan vorgeschlagen" - }, - "kiosk_session": { - "first_item": "Erstes Produkt: {name}!", - "items_two_four": "{n} Artikel — Trägheit überwinden 🚀", - "items_five_nine": "{n} Artikel — super Tempo! 💪", - "items_ten_twenty": "{n} Artikel — fast Rekord 🏆", - "items_twenty_plus": "{n} Artikel — epischer Einkauf! 🛒🔥", - "duplicates_one": "1 Duplikat (gleiches Produkt zweimal)", - "duplicates_many": "{n} Duplikate (mehrfach genommen)", - "top_category": "Top-Kategorie: {cat} ({count}×)", - "items_fallback": "{n} Artikel hinzugefügt" - } + "saved": "✅ Konfiguration gespeichert!", + "saved_local": "✅ Konfiguration lokal gespeichert", + "saved_local_error": "⚠️ Lokal gespeichert, Serverfehler: {error}" + }, + "expiry": { + "today": "HEUTE", + "tomorrow": "Morgen", + "days": "{days} Tage", + "expired_days": "Seit {days}T", + "expired_yesterday": "Seit gestern", + "expired_today": "Heute", + "badge_today": "⚠️ Läuft heute ab!", + "badge_tomorrow": "⏰ Morgen", + "badge_tomorrow_long": "⏰ Läuft morgen ab", + "badge_days": "⏰ {n} Tage", + "badge_expired_ago": "⚠️ Seit {n}T abgel.", + "badge_expired": "⛔ Abgelaufen!", + "badge_stable": "✅ Stabil", + "badge_expiring_short": "⏰ Läuft in {n}T ab", + "badge_ok_still": "✅ Noch {n}T", + "badge_expires_red": "🔴 In {n}T", + "badge_expires_yellow": "🟡 In {n}T", + "badge_expired_bare": "⚠️ Abgelaufen", + "badge_expires_warn": "⚠️ In {n}T", + "badge_days_left": "⏳ ~{n}T übrig", + "days_approx": "~{n} Tage", + "weeks_approx": "~{n} Wochen", + "months_approx": "~{n} Monate", + "years_approx": "~{n} Jahre", + "expired_today_long": "Heute abgelaufen", + "expired_ago_long": "Seit {n} Tagen abgelaufen", + "expired_suffix": "— Abgelaufen!", + "expired_suffix_ok": "— Abgelaufen (noch ok)", + "expired_suffix_warning": "— Abgelaufen (erst prüfen)", + "days_compact": "{n}T" + }, + "status": { + "ok": "OK", + "check": "Prüfen", + "discard": "Entsorgen", + "tip_freezer_ok": "Im Gefrierschrank: noch sicher (~{n}T Puffer)", + "tip_freezer_check": "Seit langem im Gefrierschrank, könnte an Qualität verloren haben. Bald verbrauchen", + "tip_freezer_danger": "Zu lange im Gefrierschrank, Gefrierbrand- und Qualitätsverlust-Risiko", + "tip_highRisk_check": "Kürzlich abgelaufen, Geruch und Aussehen vor dem Verzehr prüfen", + "tip_highRisk_danger": "Verderbliches Produkt abgelaufen: aus Sicherheitsgründen entsorgen", + "tip_medRisk_check1": "Aussehen und Geruch vor dem Verzehr prüfen", + "tip_medRisk_check2": "Schon eine Weile abgelaufen, vor dem Verzehr gut prüfen", + "tip_medRisk_danger": "Zu lange seit dem Ablaufdatum, lieber entsorgen", + "tip_lowRisk_ok": "Haltbares Produkt, noch sicher zu verzehren", + "tip_lowRisk_check": "Seit über einem Monat abgelaufen, Verpackungsintegrität prüfen", + "tip_lowRisk_danger": "Zu lange abgelaufen, besser kein Risiko eingehen" + }, + "toast": { + "product_saved": "Produkt gespeichert!", + "product_created": "Produkt erstellt!", + "product_updated": "✅ Produkt aktualisiert!", + "product_removed": "Produkt entfernt", + "updated": "Aktualisiert!", + "quantity_confirmed": "✓ Menge bestätigt", + "added_to_inventory": "✅ {name} hinzugefügt!", + "removed_from_list": "✅ {name} von der Liste entfernt!", + "removed_from_list_short": "Von der Liste entfernt", + "added_to_shopping": "🛒 Zur Einkaufsliste hinzugefügt!", + "removed_from_shopping": "🛒 Von der Einkaufsliste entfernt", + "finished_to_bring": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt", + "thrown_away": "🗑️ {name} weggeworfen!", + "thrown_away_partial": "🗑️ {qty} {unit} von {name} weggeworfen", + "product_finished_confirmed": "✅ Entfernt — wieder hinzufügen, wenn du nachkaufst", + "appliance_added": "Gerät hinzugefügt", + "item_added": "{name} hinzugefügt" + }, + "antiwaste": { + "title": "🌱 Anti-Verschwendungs-Bericht", + "grade_label": "Note", + "you": "Du", + "avg_label": "Ø", + "better": "🎉 Du verlierst {diff}% weniger als der {country}!", + "worse": "⚠️ Du verlierst mehr als der {country}. Verbesserungspotenzial!", + "on_par": "→ Du liegst beim {country}. Du kannst noch besser werden!", + "saved_money": "~{amount}/Monat gespart", + "saved_meals": "~{n} Mahlzeiten gerettet", + "saved_co2": "{n} kg CO₂ eingespart", + "trend_title": "Trend (letzte 3 Monate)", + "months_ago_2": "-60 Tage", + "months_ago_1": "-30 Tage", + "this_month": "Jetzt", + "country_it": "ital. Durchschnitt", + "country_de": "dt. Durchschnitt", + "country_en": "US-Durchschnitt", + "source": "Quellen: REDUCE, Eurostat, USDA 2021", + "live_on": "Live-Daten", + "live_off": "Offline", + "meals": "Mahlzeiten", + "annual_info": "📅 Du ~{you} kg/Jahr · Ø ~{avg} kg/Jahr", + "badge_rate": "Verlustquote", + "badge_saved_money": "gespart vs Ø", + "badge_wasted": "verloren", + "badge_better": "weniger als Ø" + }, + "error": { + "generic": "Fehler", + "network": "Netzwerkfehler", + "no_api_key": "API-Schluessel in den Einstellungen konfigurieren", + "loading": "Fehler beim Laden des Produkts", + "not_found": "Produkt nicht gefunden", + "not_found_manual": "Produkt nicht gefunden. Manuell eingeben.", + "search": "Suchfehler. Nochmal versuchen.", + "search_short": "Suchfehler", + "save": "Fehler beim Speichern", + "connection": "Verbindungsfehler", + "camera": "Kamera nicht verfügbar", + "bring_add": "Fehler beim Hinzufügen zu Bring!", + "bring_connection": "Bring! Verbindungsfehler", + "identification": "Identifikationsfehler", + "ai_quota": "KI-Kontingent erschöpft. Bitte in ein paar Minuten erneut versuchen.", + "barcode_empty": "Barcode eingeben", + "barcode_format": "Barcode darf nur Zahlen enthalten (4-14 Ziffern)", + "min_chars": "Mindestens 2 Zeichen eingeben", + "not_in_inventory": "Produkt nicht im Bestand", + "appliance_exists": "Gerät bereits vorhanden", + "already_exists": "Bereits vorhanden", + "network_retry": "Verbindungsfehler. Erneut versuchen.", + "select_items": "Wähle mindestens ein Produkt aus", + "server_offline": "Serververbindung unterbrochen", + "server_restored": "Serververbindung wiederhergestellt", + "server_retry": "Erneut versuchen" + }, + "confirm": { + "remove_item": "Möchtest du dieses Produkt wirklich aus dem Bestand entfernen?", + "kiosk_exit": "Kioskmodus verlassen?", + "cancel": "Abbrechen", + "proceed": "Bestätigen" + }, + "location": { + "dispensa": "Vorratskammer", + "frigo": "Kühlschrank", + "freezer": "Gefrierschrank" + }, + "edit": { + "title": "{name} bearbeiten", + "unknown_hint": "Produktname und Informationen eingeben", + "label_name": "🏷️ Produktname" + }, + "screensaver": { + "recipe_btn": "Rezepte", + "scan_btn": "Produkt scannen" + }, + "days": { + "mon": "Montag", + "tue": "Dienstag", + "wed": "Mittwoch", + "thu": "Donnerstag", + "fri": "Freitag", + "sat": "Samstag", + "sun": "Sonntag", + "mon_short": "Mo", + "tue_short": "Di", + "wed_short": "Mi", + "thu_short": "Do", + "fri_short": "Fr", + "sat_short": "Sa", + "sun_short": "So" + }, + "meal_types": { + "lunch": "Mittagessen", + "dinner": "Abendessen", + "colazione": "Frühstück", + "merenda": "Nachmittagssnack", + "dolce": "Dessert", + "succo": "Fruchtsaft", + "pranzo": "Mittagessen", + "cena": "Abendessen" + }, + "scale": { + "status_connected": "Waage verbunden", + "status_searching": "Gateway verbunden, warte auf Waage…", + "status_disconnected": "Waagen-Gateway nicht erreichbar", + "status_error": "Verbindungsfehler zum Gateway", + "not_connected": "Waagen-Gateway nicht verbunden", + "read_btn": "⚖️ Von Waage lesen", + "reading_title": "Waage lesen", + "place_on_scale": "Produkt auf die Waage legen…", + "waiting_stable": "Das Gewicht wird automatisch erfasst, wenn die Messung stabil ist.", + "no_url": "Gateway-URL eingeben", + "testing": "⏳ Verbindung wird getestet…", + "connected_ok": "Gateway-Verbindung erfolgreich!", + "timeout": "Timeout: keine Antwort vom Gateway", + "error_connect": "Verbindung zum Gateway nicht möglich", + "tab": "Smart-Waage", + "low_weight": "Gewicht < 10 g · manuell eingeben\n(Auto-Erkennung erfordert mind. 10 g)", + "density_hint": "(Dichte {density} g/ml)", + "ml_hint": "(wird in ml umgerechnet)", + "weight_detected": "Gewicht erkannt — 10s Stabilität abwarten…", + "weight_too_low": "Gewicht zu niedrig — warten…", + "stable": "✓ Stabil", + "auto_confirm": "✅ {val} {unit} — Auto-Bestätigung in 5s (tippen zum Abbrechen)", + "cancelled_replace": "Abgebrochen — lege die Zutat wieder auf die Waage, um fortzufahren" + }, + "prediction": { + "expected_qty": "Erwartet: {expected} {unit}", + "actual_qty": "Aktuell: {actual} {unit}", + "check_suggestion": "Überprüfe oder wiege die Restmenge" + }, + "date": { + "today": "📅 Heute", + "yesterday": "📅 Gestern" + }, + "scanner": { + "title_barcode": "🔖 Barcode scannen", + "barcode_hint": "Produktbarcode einrahmen", + "barcode_manual_placeholder": "Oder manuell eingeben...", + "barcode_use_btn": "✅ Diesen Code verwenden", + "ai_identifying": "🤖 Produkt wird erkannt...", + "ai_analyzing": "🤖 KI-Analyse läuft...", + "product_label_hint": "Produktetikett einrahmen", + "expiry_label_hint": "Ablaufdatum auf dem Produkt einrahmen", + "capture_btn": "📸 Aufnehmen", + "capture_photo_btn": "📸 Foto aufnehmen", + "retake_btn": "🔄 Erneut aufnehmen", + "camera_error_hint": "Stelle sicher, dass du HTTPS verwendest und Kameraberechtigungen erteilt hast.
Du kannst den Barcode manuell eingeben oder die KI-Identifikation verwenden.", + "no_barcode": "Kein Barcode", + "save_new_btn": "🆕 Keines davon — als neu speichern" + }, + "lowstock": { + "title": "⚠️ Wird knapp!", + "message": "{name} wird knapp — nur noch {qty} übrig.", + "question": "Möchtest du es zur Einkaufsliste hinzufügen?", + "yes": "🛒 Ja, zu Bring! hinzufügen", + "no": "Nein, passt für jetzt" + }, + "move": { + "title": "📦 Den Rest bewegen?", + "question": "Möchtest du {thing} von {name} an einen anderen Ort bewegen?", + "question_short": "Möchtest du {thing} an einen anderen Ort bewegen?", + "thing_opened": "die offene Packung", + "thing_rest": "den Rest", + "stay_btn": "Nein, bleibt in {location}", + "moved_toast": "📦 Offene Packung bewegt nach {location}", + "vacuum_restore": "🫙 Vakuum wiederherstellen" + }, + "nova": { + "1": "Unverarbeitet", + "2": "Kulinarische Zutat", + "3": "Verarbeitet", + "4": "Hochverarbeitet" + }, + "meal_plan_types": { + "pasta": "Pasta", + "riso": "Reis", + "carne": "Fleisch", + "pesce": "Fisch", + "legumi": "Hülsenfrüchte", + "uova": "Eier", + "formaggio": "Käse", + "pizza": "Pizza", + "affettati": "Aufschnitt", + "verdure": "Gemüse", + "zuppa": "Suppe", + "insalata": "Salat", + "pane": "Brot/Sandwich", + "dolce": "Dessert", + "libero": "Frei" + }, + "meal_sub": { + "dolce_torta": "Kuchen", + "dolce_crema": "Creme / Pudding", + "dolce_crumble": "Crumble / Tarte", + "dolce_biscotti": "Kekse / Gebäck", + "dolce_frutta": "Fruchtdessert", + "succo_dolce": "Süß / Fruchtig", + "succo_energizzante": "Energetisierend", + "succo_detox": "Detox / Grün", + "succo_rinfrescante": "Erfrischend", + "succo_vitaminico": "Vitamin / Zitrus" + }, + "meal_plan": { + "reset_success": "Wochenplan zurückgesetzt", + "not_available": "nicht im Vorrat verfügbar", + "suggested_by": "vom Wochenplan vorgeschlagen" + }, + "nutrition": { + "title": "🥗 Ernährungsanalyse", + "score_excellent": "😄 Ausgezeichnet", + "score_good": "🙂 Gut", + "score_improve": "😬 Verbesserbar", + "label_health": "🌿 Gesundheit", + "label_variety": "🎨 Vielfalt", + "label_fresh": "❄️ Frisch", + "source": "Basierend auf {n} Produkten in deiner Vorratskammer · EverShelf", + "products_count": "Produkte", + "today_title": "🥗 Deine Vorratskammer heute", + "products_n": "{n} Produkte" + }, + "facts": { + "greeting_morning": "Guten Morgen", + "greeting_afternoon": "Guten Tag", + "greeting_evening": "Guten Abend", + "pantry_waiting": "{greeting}! Deine Vorratskammer wartet.", + "expired_one": "Du hast 1 abgelaufenes Produkt in der Vorratskammer. Bitte überprüfen!", + "expired_many": "Du hast {n} abgelaufene Produkte in der Vorratskammer. Bitte überprüfen!", + "expired_list": "Abgelaufene Produkte: {names}", + "expired_list_more": "und {n} weitere", + "freezer_expired_ok": "{name} ist abgelaufen, aber im Gefrierschrank könnte es noch gut sein! Überprüfe es.", + "freezer_expired_old": "{name} im Gefrierschrank ist zu lange abgelaufen. Besser wegwerfen.", + "fridge_expired_one": "Du hast 1 abgelaufenes Produkt im Kühlschrank!", + "fridge_expired_many": "Du hast {n} abgelaufene Produkte im Kühlschrank!", + "expiring_today": "{name} läuft heute ab! Sofort verbrauchen.", + "expiring_tomorrow": "{name} läuft morgen ab. Denk daran!", + "expiring_days": "{name} läuft in {days} Tagen ab.", + "expiring_many": "Du hast {n} Produkte, die bald ablaufen.", + "expiring_this_week": "{n} Produkte laufen diese Woche ab. Plane deine Mahlzeiten entsprechend!", + "expiring_item_loc": "{name} ({loc}) läuft in {days} {dayslabel} ab.", + "expiring_this_month": "{n} Produkte laufen diesen Monat ab.", + "shopping_add": "Zur Liste: {names} 🛒", + "shopping_more": "und {n} weitere", + "shopping_empty": "Einkaufsliste leer. Alles aufgefüllt! ✅", + "in_fridge": "Im Kühlschrank: {name}.", + "in_freezer": "Im Gefrierschrank: {name}. Vergiss es nicht!", + "top_category": "Häufigste Kategorie: {icon} {cat} mit {n} Produkten.", + "cat_meat": "Du hast {n} Fleischprodukte. 🥩", + "cat_dairy": "Du hast {n} Milchprodukte zu Hause. 🥛", + "cat_veggies": "Du hast {n} Gemüsesorten. Super für die Gesundheit! 🥬", + "cat_fruit": "Du hast {n} Obstsorten. 🍎", + "cat_drinks": "Du hast {n} Getränke verfügbar. 🥤", + "cat_frozen": "Du hast {n} Tiefkühlprodukte. ❄️", + "cat_pasta": "Du hast {n} Nudelsorten. 🍝 Wie wäre es mit einer Carbonara?", + "cat_canned": "Du hast {n} Konserven in der Vorratskammer. 🥫", + "cat_snacks": "Du hast {n} Snacks. Widerstand leisten! 🍪", + "cat_condiments": "Du hast {n} Gewürze zur Verfügung. 🧂", + "item_random": "Wusstest du? Du hast {name} in {loc}.", + "item_qty": "{name}: du hast {qty}.", + "no_expiry_count": "{n} Produkte haben kein Ablaufdatum.", + "furthest_expiry": "Das Produkt mit dem spätesten Ablaufdatum ist {name}: {months} Monate.", + "high_qty": "Du hast einen guten Vorrat von {name}: {qty}!", + "low_qty_item": "{name} geht zur Neige. Auf die Einkaufsliste?", + "low_qty_count": "{n} Produkte sind fast aufgebraucht.", + "morning_bread": "Guten Morgen! Du hast Brot für das Frühstück. 🍞", + "morning_milk": "Gibt es Milch im Kühlschrank für den Cappuccino? ☕🥛", + "morning_fruit": "Guten Morgen! Frisches Obst ist ein guter Start. 🍎", + "noon_pasta": "Mittagszeit… Wie wäre es mit Pasta? 🍝", + "noon_salad": "Ein frischer Salat zum Mittagessen? Du hast {n} Gemüsesorten! 🥗", + "evening_meat": "Zum Abendessen könntest du das Fleisch verwenden. 🥩", + "evening_fish": "Wie wäre es mit Fisch zum Abendessen? 🐟", + "evening_expiring": "Du hast {n} Produkte, die diese Woche ablaufen — heute Abend verwenden!", + "night_reminder": "Gute Nacht! Denk morgen daran zu verwenden: {names}.", + "weekly_balance": "Wochenbilanz: +{in} hinzugefügt, −{out} verbraucht.", + "weekly_added": "Du hast diese Woche {n} Produkte hinzugefügt.", + "weekly_consumed": "Du hast diese Woche {n} Produkte verbraucht. Gut gemacht!", + "tip_freezer": "💡 Tiefkühlprodukte halten viel länger als das Ablaufdatum.", + "tip_bread": "💡 Gefrorenes Brot behält seinen Duft wochenlang.", + "tip_fifo": "💡 Um Verschwendung zu vermeiden, zuerst Produkte mit nahem Ablaufdatum verwenden (FIFO).", + "tip_meat": "💡 Fleisch im Gefrierschrank hält bis zu 6 Monate problemlos.", + "tip_no_refreeze": "💡 Niemals ein aufgetautes Lebensmittel wieder einfrieren. Sofort zubereiten!", + "tip_fridge": "💡 Ein ordentlicher Kühlschrank spart Zeit und Geld.", + "tip_canned": "💡 Geöffnete Konserven in den Kühlschrank und in wenigen Tagen verbrauchen.", + "top_brand": "Die häufigste Marke in deiner Vorratskammer ist {brand} mit {n} Produkten.", + "combo_pasta": "Du hast Pasta und Gewürze: bereit für ein Erstgericht! 🍝", + "combo_sandwich": "Brot und Fleisch: ein schnelles Sandwich ist immer eine gute Idee! 🥪", + "combo_balanced": "Gemüse und Fleisch: du hast alles für eine ausgewogene Mahlzeit! 🥗🥩", + "pantry_empty": "Die Vorratskammer ist leer! Zeit zum Einkaufen. 🛒", + "pantry_empty_scan": "Keine Produkte erfasst. Scanne etwas um zu beginnen!", + "location_distribution": "Verteilung: {parts}", + "day": "Tag", + "days": "Tage" + }, + "kiosk_session": { + "first_item": "Erstes Produkt: {name}!", + "items_two_four": "{n} Artikel — Trägheit überwinden 🚀", + "items_five_nine": "{n} Artikel — super Tempo! 💪", + "items_ten_twenty": "{n} Artikel — fast Rekord 🏆", + "items_twenty_plus": "{n} Artikel — epischer Einkauf! 🛒🔥", + "duplicates_one": "1 Duplikat (gleiches Produkt zweimal)", + "duplicates_many": "{n} Duplikate (mehrfach genommen)", + "top_category": "Top-Kategorie: {cat} ({count}×)", + "items_fallback": "{n} Artikel hinzugefügt" + }, + "kiosk": { + "check_btn": "🔍 Nach Updates suchen", + "checking": "⏳ Prüfe…", + "error_check": "Fehler bei der Update-Prüfung", + "error_start_install": "Fehler beim Starten der Installation", + "version_installed": "Installiert: {v}", + "update_available": "⬆️ Neue Version verfügbar: {latest} (installiert: {current})", + "up_to_date": "✅ Du bist auf dem neuesten Stand — Version {v}", + "too_old": "⚠️ Der installierte Kiosk ist zu alt für die automatische Update-Prüfung.
Drücke den Knopf unten, um die neue Version direkt herunterzuladen.", + "manual_install": "⚠️ Dieser Kiosk unterstützt keine automatische Installation.
Manuelle Vorgehensweise:
1. Kiosk verlassen (✕ oben links)
2. EverShelf Kiosk App deinstallieren
3. Neue APK von GitHub herunterladen und installieren:", + "starting_download": "⏳ Download startet…", + "install_btn": "⬇️ Update installieren", + "exit_title": "Kiosk beenden", + "refresh_title": "Seite aktualisieren" + }, + "update": { + "new_version": "Neue Version", + "btn": "Aktualisieren" + }, + "gemini": { + "chat_title": "Mit Gemini chatten", + "not_configured": "🤖 Gemini nicht konfiguriert — GEMINI_API_KEY in den Einstellungen setzen" + }, + "appliances": { + "empty": "Kein Haushaltsgerät hinzugefügt" + } } \ No newline at end of file diff --git a/translations/en.json b/translations/en.json index 2d2240f..05a1364 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1,946 +1,1064 @@ { - "app": { - "name": "EverShelf", - "loading": "Loading..." + "app": { + "name": "EverShelf", + "loading": "Loading..." + }, + "nav": { + "title": "EverShelf", + "home": "Home", + "inventory": "Pantry", + "recipes": "Recipes", + "shopping": "Shopping", + "log": "Log" + }, + "btn": { + "back": "← Back", + "save": "💾 Save", + "cancel": "✕ Cancel", + "close": "Close", + "add": "✅ Add", + "delete": "Delete", + "edit": "✏️ Edit", + "search": "🔍 Search", + "go": "✅ Go", + "toggle_password": "👁️ Show/Hide", + "load_more": "Load more...", + "save_config": "💾 Save Configuration", + "save_product": "💾 Save Product", + "restart": "↺ Restart", + "reset_default": "↺ Reset to default", + "save_info": "💾 Save information", + "retry": "🔄 Retry" + }, + "locations": { + "dispensa": "Pantry", + "frigo": "Fridge", + "freezer": "Freezer", + "altro": "Other" + }, + "categories": { + "latticini": "Dairy", + "carne": "Meat", + "pesce": "Fish", + "frutta": "Fruit", + "verdura": "Vegetables", + "pasta": "Pasta & Rice", + "pane": "Bread & Bakery", + "surgelati": "Frozen", + "bevande": "Beverages", + "condimenti": "Condiments", + "snack": "Snacks & Sweets", + "conserve": "Canned Goods", + "cereali": "Cereals & Legumes", + "igiene": "Hygiene", + "pulizia": "Household", + "altro": "Other", + "select": "-- Select --" + }, + "units": { + "pz": "pcs", + "conf": "pkg", + "g": "g", + "ml": "ml", + "pieces": "Pieces", + "grams": "Grams", + "box": "Package", + "boxes": "Packages" + }, + "shopping_sections": { + "frutta_verdura": "Fruits & Vegetables", + "carne_pesce": "Meat & Fish", + "latticini": "Dairy & Fresh", + "pane_dolci": "Bread & Sweets", + "pasta": "Pasta & Cereals", + "conserve": "Canned & Sauces", + "surgelati": "Frozen", + "bevande": "Beverages", + "pulizia_igiene": "Cleaning & Hygiene", + "altro": "Other" + }, + "dashboard": { + "expired_title": "🚫 Expired", + "expiring_title": "⏰ Expiring Soon", + "stats_period": "📊 Last 30 days", + "opened_title": "📦 Opened Products", + "review_title": "🔍 To Review", + "review_hint": "Quantities that seem unusual. Confirm if correct or modify.", + "quick_recipe": "Quick recipe with expiring products", + "banner_review_title": "Anomalous quantity", + "banner_review_action_ok": "It's correct", + "banner_review_action_finish": "🗑️ All gone", + "banner_review_action_edit": "Correct", + "banner_review_action_weigh": "Weigh", + "banner_review_dismiss": "Dismiss", + "banner_prediction_title": "Anomalous consumption", + "banner_prediction_hint": "Based on predictions, this quantity doesn't match expected consumption.", + "banner_prediction_action_confirm": "Confirm {qty} {unit} is correct", + "banner_prediction_action_weigh": "Weigh now", + "banner_prediction_action_edit": "Update quantity", + "banner_expired_title": "Expired product", + "banner_expired_today": "Expired today", + "banner_expired_days": "Expired {days} days ago", + "banner_expired_action_use": "Use anyway", + "banner_expired_action_throw": "I threw it away", + "banner_expired_action_edit": "Fix date", + "banner_anomaly_action_edit": "Fix inventory", + "banner_anomaly_action_dismiss": "Quantity is correct", + "banner_no_expiry_title": "Missing expiry: {name}", + "banner_no_expiry_detail": "This product has no expiry date. Would you like to add one, or confirm it doesn't expire?", + "banner_no_expiry_action_set": "Set expiry date", + "banner_no_expiry_action_dismiss": "Doesn't expire ✓", + "banner_no_expiry_toast_dismissed": "Marked as 'no expiry'", + "banner_expiring_title": "Expiring soon", + "banner_expiring_today": "Expires today!", + "banner_expiring_tomorrow": "Expires tomorrow", + "banner_expiring_days": "Expires in {days} days", + "banner_expiring_action_use": "Use now", + "banner_finished_title": "finished?", + "banner_finished_detail": "I recorded that {name} reached zero stock. Is it really gone, or do you still have some?", + "banner_finished_action_yes": "Yes, it's done", + "banner_finished_action_no": "No, I still have some", + "banner_review_unusual_pkg_title": "Unusual package size", + "banner_review_unusual_pkg_detail": "You set a package of {qty} {unit} — the size seems very large. Check if correct or edit.", + "banner_review_low_qty_title": "Very low quantity", + "banner_review_low_qty_detail": "You only have {qty} in stock — seems very little, could be a typo. Confirm if correct.", + "banner_review_high_qty_title": "Unusually high quantity", + "banner_review_high_qty_detail": "You have {qty} in stock — the figure seems very high. Confirm if correct or edit.", + "banner_prediction_rate_day": "Average ~{n} {unit}/day", + "banner_prediction_rate_week": "Average ~{n} {unit}/week", + "banner_prediction_days_ago": "{n} days ago you restocked", + "banner_prediction_more": "I expected {expected} {unit}{time}, but you have {actual} {unit}. Did you add stock without recording it?", + "banner_prediction_less": "I expected {expected} {unit}{time}, but you only have {actual} {unit}. Did you use more than usual?", + "banner_finished_zero": "Inventory shows zero, but recorded movements suggest it shouldn't be empty.", + "banner_finished_expected": "According to records you should still have {qty} {unit}.", + "banner_finished_check": "Can you check?", + "banner_anomaly_phantom_title": "you have more stock than expected", + "banner_anomaly_phantom_detail": "Inventory shows {inv_qty} {unit}, but based on records you should only have {expected_qty} {unit}. Did you add stock without recording it?", + "banner_anomaly_untracked_title": "stock not recorded as an entry", + "banner_anomaly_untracked_detail": "You have {inv_qty} {unit} in inventory, but recorded outflows exceed inflows — the initial stock was likely never added as an \"in\" transaction. You can correct the quantity or log the missing entries.", + "banner_anomaly_ghost_title": "you have less stock than expected", + "banner_anomaly_ghost_detail": "Based on recorded operations you should have {expected_qty} {unit} of {name}, but inventory shows only {inv_qty} {unit}. Did you take stock without recording it?", + "consumed": "Consumed: {n} ({pct}%)", + "wasted": "Wasted: {n} ({pct}%)", + "more_opened": "and {n} more opened...", + "banner_expired_detail": "{when} · you still have {qty}.", + "banner_explain_title": "Ask Gemini for an explanation", + "banner_explain_btn": "Explain", + "banner_analyzing": "🤖 Analyzing…" + }, + "inventory": { + "title": "Pantry", + "filter_all": "All", + "search_placeholder": "🔍 Search product...", + "recent_title": "🕐 Recently used", + "popular_title": "⭐ Most used", + "empty": "No products here.\nScan a product to add it!", + "no_items_found": "No inventory items found", + "qty_remainder_suffix": "left", + "vacuum_badge": "🫙 Vacuum sealed", + "opened_badge": "📭 Opened", + "label_expiry": "📅 Expiry", + "label_storage": "🫙 Storage", + "label_status": "📭 Status", + "opened_since": "Opened since {date}", + "label_position": "📍 Location", + "label_quantity": "📦 Quantity", + "label_added": "📅 Added", + "empty_text": "No products here.
Scan a product to add it!", + "empty_db": "No products in the database.
Scan a product to get started!", + "qty_trace": "< 1" + }, + "scan": { + "title": "Scan Product", + "mode_shopping": "🛒 Shopping Mode", + "mode_shopping_end": "✅ End shopping", + "zoom": "Zoom", + "barcode_placeholder": "Enter barcode...", + "quick_name_divider": "or type the name", + "quick_name_placeholder": "E.g.: Apples, Zucchini, Bread...", + "manual_entry": "✏️ Manual Entry", + "ai_identify": "🤖 Identify with AI", + "hint": "Scan the barcode, type the product name, or use AI to identify it", + "debug_toggle": "🐛 Debug Log", + "barcode_acquired": "🔖 Barcode scanned: {code}", + "scan_barcode": "🔖 Scan Barcode", + "create_named": "Create {name}", + "new_without_barcode": "New product without barcode" + }, + "action": { + "title": "What do you want to do?", + "add_btn": "📥 ADD", + "add_sub": "to pantry/fridge", + "use_btn": "📤 USE / CONSUME", + "use_sub": "from pantry/fridge", + "have_title": "📦 Already in stock!", + "add_more_sub": "add more", + "use_qty_sub": "how much you used", + "throw_btn": "🗑️ DISCARD", + "throw_sub": "throw away", + "edit_sub": "expiry, location…", + "create_recipe_btn": "Create a recipe with this" + }, + "add": { + "title": "Add to Pantry", + "location_label": "📍 Where do you put it?", + "quantity_label": "📦 Quantity", + "conf_size_label": "📦 Each package contains:", + "conf_size_placeholder": "e.g. 300", + "vacuum_label": "🫙 Vacuum sealed", + "vacuum_hint": "Expiry date will be extended automatically", + "submit": "✅ Add", + "purchase_type_label": "🛒 This product is...", + "new_btn": "🆕 Just bought", + "existing_btn": "📦 I already had it", + "remaining_label": "📦 Remaining quantity", + "remaining_hint": "Approximately how much is left?", + "remaining_full": "🟢 Full", + "remaining_half": "🟠 Half", + "estimated_expiry": "Estimated expiry:", + "suffix_freezer": "(freezer)", + "suffix_vacuum": "(vacuum sealed)", + "hint_modify": "📝 You can change the date or scan it with the camera", + "scan_expiry_title": "📷 Scan Expiry Date", + "product_added": "✅ {name} added!{qty}", + "suffix_freezer_vacuum": "(freezer + vacuum sealed)", + "history_badge_tip": "Average from {n} previous entries" + }, + "use": { + "title": "Use / Consume", + "location_label": "📍 From where?", + "quantity_label": "How much did you use?", + "change": "change", + "partial_hint": "Or specify the quantity used:", + "partial_piece_hint": "Did you use only a part?", + "piece": "piece", + "one_whole": "1 whole", + "use_all": "🗑️ Used ALL / Finished", + "submit": "📤 Use this quantity", + "available": "📦 Available:", + "opened_badge": "OPENED", + "not_in_inventory": "⚠️ Product not in inventory.", + "expiry_warning": "⚠️ Use first the one{loc} that expires on {date} — {when}!", + "throw_title": "🗑️ Discard Product", + "throw_all": "🗑️ Discard ALL ({qty})", + "throw_qty_label": "How much to discard?", + "throw_qty_hint": "or enter a quantity:", + "throw_partial_btn": "🗑️ Discard this quantity", + "when_expired": "expired {n} days ago", + "when_today": "expires today", + "when_tomorrow": "expires tomorrow", + "when_days": "expires in {n} days", + "toast_used": "📤 Used {qty} of {name}", + "toast_bring": "🛒 Product finished → added to Bring!", + "toast_opened_finished": "🔓 Opened package of {name} finished!", + "disambiguation_hint": "What do you mean by \"all done\"?", + "disambiguation_all": "🗑️ Finish EVERYTHING ({qty})", + "error_exceeds_stock": "⚠️ You cannot use more than you have available!", + "use_all_confirm_title": "✅ Finish everything", + "use_all_confirm_msg": "Confirm that you have finished the product:", + "use_all_confirm_btn": "✅ Yes, finished", + "throw_all_confirm_title": "🗑️ Discard everything", + "throw_all_confirm_msg": "Do you really want to throw away the whole product?", + "throw_all_confirm_btn": "🗑️ Yes, discard" + }, + "product": { + "title_new": "New Product", + "title_edit": "Edit Product", + "ai_fill": "📷 Take photo and identify with AI", + "ai_fill_hint": "AI will automatically fill in the product fields", + "name_label": "🏷️ Product Name *", + "name_placeholder": "E.g.: Whole milk, Penne pasta...", + "brand_label": "🏢 Brand", + "brand_placeholder": "E.g.: Barilla, Granarolo, Mutti...", + "category_label": "📂 Category", + "unit_label": "📏 Unit of measure", + "default_qty_label": "🔢 Default quantity", + "conf_size_label": "📦 Each package contains:", + "conf_size_placeholder": "e.g. 300", + "notes_label": "📝 Notes", + "notes_placeholder": "E.g.: lactose free, organic, store in fridge after opening...", + "barcode_label": "🔖 Barcode", + "barcode_placeholder": "Barcode (if available)", + "barcode_hint": "⚠️ Add the barcode so next time you just need to scan it!", + "submit": "💾 Save Product", + "name_required": "Enter the product name", + "conf_size_required": "Specify the package content", + "expiry_estimated": "Estimated expiry:", + "scan_expiry": "Scan expiry date", + "expiry_hint": "📝 You can edit the date or scan it with the camera", + "add_batch": "📦 + Batch with different expiry", + "package_info": "📦 Package: {info}", + "edit_catalog": "⚙️ Edit product info (name, brand, category…)", + "not_recognized": "⚠️ Product not recognized", + "edit_info": "✏️ Edit information", + "modify_details": "EDIT\nexpiry, location…", + "already_in_pantry": "📋 Already in pantry", + "no_barcode": "No barcode" + }, + "products": { + "title": "📦 All Products", + "search_placeholder": "🔍 Search product...", + "empty": "No products in database.\nScan a product to get started!", + "no_category": "No products in this category" + }, + "recipes": { + "title": "🍳 Recipes", + "generate": "✨ Generate new recipe", + "archive_empty": "No recipes saved. Generate your first recipe!", + "dialog_title": "🍳 Recipe", + "dialog_desc": "I will generate a healthy recipe using pantry ingredients, prioritizing expiring items.", + "meal_label": "🕐 Which meal?", + "persons_label": "👥 How many people?", + "meal_type_label": "🎯 Meal type", + "opt_fast": "⚡ Quick meal", + "opt_light": "🥗 Light appetite", + "opt_expiry": "⏰ Prioritize expiring items", + "opt_healthy": "💚 Extra healthy", + "opt_opened": "📦 Prioritize opened items", + "opt_zero_waste": "♻️ Zero waste", + "generate_btn": "✨ Generate Recipe", + "loading_msg": "Preparing your recipe...", + "start_cooking": "👨‍🍳 Cooking Mode", + "regenerate": "🔄 Generate another one", + "close_btn": "✅ Close", + "ingredients_title": "🧾 Ingredients", + "steps_title": "👨‍🍳 Steps", + "no_steps": "No steps available", + "generate_error": "Generation error", + "persons_short": "serv.", + "use_ingredient_title": "Use ingredient", + "recipe_qty_label": "Recipe", + "from_where_label": "From where?", + "amount_label": "How much", + "use_amount_btn": "Use this amount", + "use_all_btn": "Use ALL / Finished", + "packs_label": "Packs", + "quantity_in_total": "Quantity in {unit} (total: {total})", + "packs_of_have": "Packs of {size} (you have {count} packs)", + "scale_wait_stable": "Wait 10s of stable weight for auto-fill…", + "ingredient_scaled_toast": "📦 Ingredient deducted from pantry!", + "finished_added_bring_toast": "🛒 Finished product → added to Bring!", + "load_error": "Loading error" + }, + "shopping": { + "title": "🛒 Shopping List", + "bring_loading": "Connecting to Bring!...", + "bring_not_configured": "Bring! is not configured. Add your email and password in settings.", + "tab_to_buy": "🛍️ To buy", + "tab_forecast": "🧠 Forecast", + "total_label": "💰 Estimated total", + "section_to_buy": "🛍️ To buy", + "suggestions_title": "💡 AI Suggestions", + "suggestions_add": "✅ Add selected to Bring!", + "search_prices": "🔍 Search all prices", + "suggest_btn": "Suggest what to buy", + "smart_title": "🧠 Smart Predictions", + "smart_empty": "No predictions available.
Add products to your pantry to receive smart predictions.", + "smart_filter_all": "All", + "smart_filter_critical": "🔴 Urgent", + "smart_filter_high": "🟠 Soon", + "smart_filter_medium": "🟡 Plan", + "smart_filter_low": "🟢 Forecast", + "smart_add": "🛒 Add selected to Bring!", + "empty": "Shopping list empty!\nUse the button below to generate suggestions.", + "already_in_list": "🛒 \"{name}\" is already in the shopping list", + "already_in_list_short": "ℹ️ Already in the shopping list", + "add_prompt": "Do you want to add it to the shopping list?", + "smart_already": "📊 Smart shopping already predicts {name}", + "all_searched": "All products have already been searched. Use 🔄 to search individual ones.", + "search_complete": "Search complete: {count} products", + "removed_sufficient": "🧹 {removed} product(s) with sufficient stock removed from the list", + "suggest_buy": "🛒 Buy: {qty} {unit}", + "suggest_buy_approx": "🛒 At least: {qty} {unit}", + "suggest_buy_tip": "Suggested quantity based on your last 14 days of consumption", + "suggest_buy_approx_tip": "Minimum estimate based on consumption (buy the nearest package size)", + "bring_badge": "🛒 Already on Bring!", + "add_urgent_toast": "🔴 {n} urgent product(s) automatically added to Bring!", + "migration_done": "✅ {migrated} updated, {skipped} already ok", + "added_to_bring": "🛒 {n} products added to Bring!", + "added_to_bring_skip": "{n} already present", + "all_on_bring": "All products were already on Bring!", + "freq_high": "📈 Frequent", + "freq_regular": "📊 Regular", + "freq_occasional": "📉 Occasional", + "out_of_stock": "Out of stock", + "scan_toast": "📷 Scan: {name}", + "empty_category": "No products in this category", + "session_empty": "🛒 No products yet", + "urgency_critical": "Urgent", + "urgency_high": "Soon", + "urgency_medium": "Plan", + "urgency_low": "Forecast", + "urgency_medium_short": "Medium", + "urgency_low_short": "Ok", + "tag_urgent": "🔴 Urgent", + "tag_priority": "⭐ Priority", + "tag_check": "✅ Check", + "smart_already_predicted": "📊 Smart shopping already predicts {name}{urgency}.", + "item_removed": "✅ {name} removed from list!", + "urgency_spec_critical": "⚡ Urgent", + "urgency_spec_high": "🟠 Soon", + "bring_add_n": "Add {n} to Bring!", + "bring_add_selected": "Add selected to Bring!", + "bring_adding": "Adding...", + "bring_added_one": "1 product added to Bring!", + "bring_added_many": "{n} products added to Bring!", + "bring_skipped": "({n} already in list)", + "force_sync": "Force Bring! sync", + "scan_target_label": "You are looking for", + "scan_target_found": "Found! Remove from list", + "bring_add_one": "Add 1 product to Bring!", + "bring_add_many": "Add {n} products to Bring!", + "syncing": "Syncing…", + "sync_done": "Sync completed", + "price_searching": "Searching...", + "search_action": "Search", + "open_action": "Open", + "not_found": "Not found", + "search_price": "Search price", + "tap_to_scan": "Tap to scan", + "tag_title": "Tag", + "remove_title": "Remove", + "found_count": "{found}/{total} products found", + "savings_offers": "· 🏷️ You save €{amount} with offers", + "searching_progress": "Searching {current}/{total}...", + "remove_error": "Removal error", + "btn_fetch_prices": "Find prices", + "price_total_label": "💰 Estimated total:", + "price_loading": "Looking up prices…", + "price_not_found": "price n/a", + "suggest_loading": "Analyzing...", + "suggest_error": "Suggestion generation error", + "priority_high": "High", + "priority_medium": "Medium", + "priority_low": "Low", + "smart_last_update": "Updated {time}", + "names_already_updated": "All names are already up to date" + }, + "ai": { + "title": "🤖 AI Identification", + "capture": "📸 Take Photo", + "retake": "🔄 Retake", + "hint": "Take a photo of the product and AI will try to identify it", + "identifying": "🤖 Identifying product...", + "no_api_key": "⚠️ Gemini API key not configured.\nAdd GEMINI_API_KEY to the .env file on the server.", + "fields_filled": "✅ Fields filled by AI", + "use_data": "✅ Use AI data", + "use_data_no_barcode": "✅ Use AI data (no barcode)" + }, + "log": { + "title": "📒 Operations Log", + "type_added": "Added", + "type_waste": "Discarded", + "type_used": "Used", + "type_bring": "Added to Bring!", + "undone_badge": "Undone", + "undo_title": "Undo this operation", + "load_error": "Error loading log", + "empty": "No operations recorded.", + "undo_action_remove": "removal of", + "undo_action_restore": "restock of", + "undo_confirm": "Undo this operation?\n→ {action} {name}", + "undo_success": "↩ Operation undone for {name}", + "already_undone": "Operation already undone", + "too_old": "Cannot undo operations older than 24 hours", + "undo_error": "Error during undo" + }, + "chat": { + "title": "Gemini Chef", + "welcome": "Hi! I'm your kitchen assistant", + "welcome_desc": "Ask me to make you a juice, a snack, a quick dish... I know your pantry, your appliances and your preferences!", + "suggestion_snack": "🍿 Quick snack", + "suggestion_juice": "🥤 Juice/Smoothie", + "suggestion_light": "🥗 Something light", + "suggestion_expiry": "⏰ Use expiring items", + "clear": "New conversation", + "placeholder": "Ask something...", + "cleared": "Chat cleared", + "suggestion_snack_text": "What can I make for a quick snack?", + "suggestion_juice_text": "Make me a juice or smoothie with what I have", + "suggestion_light_text": "I'm hungry but want something light", + "suggestion_expiry_text": "What's about to expire and how can I use it?", + "transfer_to_recipes": "Transfer to Recipes", + "transferring": "Transferring...", + "transferred": "Added to Recipes!", + "open_recipe": "Open recipe" + }, + "cooking": { + "close": "Close", + "tts_btn": "Read aloud", + "restart": "↺ Restart", + "replay": "🔊 Replay", + "timer": "⏱️ {time} · Timer", + "prev": "◀ Previous", + "next": "Next ▶", + "ingredient_used": "✔️ Deducted", + "ingredient_use_btn": "📦 Use", + "ingredient_deduct_title": "Deduct from pantry", + "timer_expired_tts": "Timer {label} expired!", + "timer_warning_tts": "Heads up! {label}: 10 seconds left!", + "recipe_done_tts": "Recipe complete! Enjoy your meal!", + "expires_chip": "exp. {date}", + "finish": "✅ Finish" + }, + "settings": { + "title": "⚙️ Settings", + "tab_api": "API Keys", + "tab_bring": "Bring!", + "tab_recipe": "Recipes", + "tab_mealplan": "Weekly Plan", + "tab_appliances": "Appliances", + "tab_spesa": "Online Shopping", + "tab_camera": "Camera", + "tab_security": "Security", + "tab_tts": "Voice (TTS)", + "tab_language": "Language", + "tab_scale": "Smart Scale", + "gemini": { + "title": "🤖 Google Gemini AI", + "hint": "API key for product identification, expiry dates and recipes.", + "key_label": "Gemini API Key" }, - "nav": { - "title": "EverShelf", - "home": "Home", - "inventory": "Pantry", - "recipes": "Recipes", - "shopping": "Shopping", - "log": "Log" + "bring": { + "title": "🛒 Bring! Shopping List", + "hint": "Credentials for the Bring! shopping list integration.", + "email_label": "📧 Bring! Email", + "password_label": "🔒 Bring! Password" }, - "btn": { - "back": "← Back", - "save": "💾 Save", - "cancel": "✕ Cancel", - "close": "Close", - "add": "✅ Add", - "delete": "Delete", - "edit": "✏️ Edit", - "search": "🔍 Search", - "go": "✅ Go", - "toggle_password": "👁️ Show/Hide", - "load_more": "Load more...", - "save_config": "💾 Save Configuration", - "save_product": "💾 Save Product", - "restart": "↺ Restart", - "reset_default": "↺ Reset to default", - "save_info": "💾 Save information", - "retry": "🔄 Retry" + "price": { + "title": "💰 Price Estimation (AI)", + "hint": "Show estimated cost per product in the shopping list using AI.", + "enabled_label": "Enable price estimation", + "country_label": "🌍 Reference country", + "currency_label": "💱 Currency", + "update_label": "🔄 Refresh prices every", + "update_suffix": "months" }, - "locations": { - "dispensa": "Pantry", - "frigo": "Fridge", - "freezer": "Freezer", - "altro": "Other" + "recipe": { + "title": "🍳 Recipe Preferences", + "hint": "Configure the default options for recipe generation.", + "persons_label": "👥 Default servings", + "options_label": "🎯 Default recipe options", + "fast": "⚡ Quick Meal", + "light": "🥗 Light Meal", + "expiry": "⏰ Expiry Priority", + "healthy": "💚 Extra Healthy", + "opened": "📦 Open Items Priority", + "zerowaste": "♻️ Zero Waste", + "dietary_label": "🚫 Intolerances / Restrictions", + "dietary_placeholder": "E.g.: gluten free, lactose free, vegetarian..." }, - "categories": { - "latticini": "Dairy", - "carne": "Meat", - "pesce": "Fish", - "frutta": "Fruit", - "verdura": "Vegetables", - "pasta": "Pasta & Rice", - "pane": "Bread & Bakery", - "surgelati": "Frozen", - "bevande": "Beverages", - "condimenti": "Condiments", - "snack": "Snacks & Sweets", - "conserve": "Canned Goods", - "cereali": "Cereals & Legumes", - "igiene": "Hygiene", - "pulizia": "Household", - "altro": "Other", - "select": "-- Select --" + "mealplan": { + "title": "📅 Weekly Meal Plan", + "hint": "Set the meal type for each day. It will be used as a guide in recipe generation.", + "enabled": "✅ Enable weekly meal plan", + "legend": "🌤️ = Lunch · 🌙 = Dinner · Tap a badge to change it.", + "types_title": "📋 Available types" }, - "units": { - "pz": "pcs", - "conf": "pkg", - "g": "g", - "ml": "ml", - "pieces": "Pieces", - "grams": "Grams", - "box": "Package", - "boxes": "Packages" + "appliances": { + "title": "🔌 Available Appliances", + "hint": "Indicate the appliances you have. They will be considered in recipe generation.", + "new_placeholder": "E.g.: Bread machine, Thermomix, Air fryer...", + "quick_title": "Quick add:", + "oven": "🔥 Oven", + "microwave": "📡 Microwave", + "air_fryer": "🍟 Air fryer", + "bread_maker": "🍞 Bread maker", + "bimby": "🤖 Thermomix/Cookeo", + "mixer": "🌀 Stand mixer", + "steamer": "♨️ Steamer", + "pressure_cooker": "🫕 Pressure cooker", + "toaster": "🍞 Toaster", + "blender": "🍹 Blender", + "empty": "No appliances added" }, - "shopping_sections": { - "frutta_verdura": "Fruits & Vegetables", - "carne_pesce": "Meat & Fish", - "latticini": "Dairy & Fresh", - "pane_dolci": "Bread & Sweets", - "pasta": "Pasta & Cereals", - "conserve": "Canned & Sauces", - "surgelati": "Frozen", - "bevande": "Beverages", - "pulizia_igiene": "Cleaning & Hygiene", - "altro": "Other" + "spesa": { + "title": "🛍️ Online Shopping", + "hint": "Configure the online shopping provider.", + "provider_label": "🏪 Provider", + "email_label": "📧 Email", + "password_label": "🔒 Password", + "login_btn": "🔐 Login", + "ai_prompt_label": "🤖 AI product selection prompt", + "ai_prompt_placeholder": "Instructions for AI when choosing between multiple products...", + "ai_prompt_hint": "AI uses this prompt to choose the most appropriate product from results. Leave empty for default behavior.", + "configure_first": "Configure Online Shopping in settings first", + "missing_credentials": "Enter email and password", + "login_in_progress": "Signing in...", + "login_error_prefix": "Error:", + "login_network_error_prefix": "Network error:", + "login_success_default": "Login successful!", + "result_name_label": "Name", + "result_card_label": "Card", + "result_pickup_label": "Pickup point", + "result_points_label": "Loyalty points", + "connected_relogin": "✅ Connected — Sign in again", + "connected_as": "Connected as {name}" }, - "dashboard": { - "expired_title": "🚫 Expired", - "expiring_title": "⏰ Expiring Soon", - "stats_period": "📊 Last 30 days", - "opened_title": "📦 Opened Products", - "review_title": "🔍 To Review", - "review_hint": "Quantities that seem unusual. Confirm if correct or modify.", - "quick_recipe": "Quick recipe with expiring products", - "banner_review_title": "Anomalous quantity", - "banner_review_action_ok": "It's correct", - "banner_review_action_finish": "🗑️ All gone", - "banner_review_action_edit": "Correct", - "banner_review_action_weigh": "Weigh", - "banner_review_dismiss": "Dismiss", - "banner_prediction_title": "Anomalous consumption", - "banner_prediction_hint": "Based on predictions, this quantity doesn't match expected consumption.", - "banner_prediction_action_confirm": "Confirm {qty} {unit} is correct", - "banner_prediction_action_weigh": "Weigh now", - "banner_prediction_action_edit": "Update quantity", - "banner_expired_title": "Expired product", - "banner_expired_today": "Expired today", - "banner_expired_days": "Expired {days} days ago", - "banner_expired_action_use": "Use anyway", - "banner_expired_action_throw": "I threw it away", - "banner_expired_action_edit": "Fix date", - "banner_anomaly_action_edit": "Fix inventory", - "banner_anomaly_action_dismiss": "Quantity is correct", - "banner_no_expiry_title": "Missing expiry: {name}", - "banner_no_expiry_detail": "This product has no expiry date. Would you like to add one, or confirm it doesn't expire?", - "banner_no_expiry_action_set": "Set expiry date", - "banner_no_expiry_action_dismiss": "Doesn't expire ✓", - "banner_no_expiry_toast_dismissed": "Marked as 'no expiry'", - "banner_expiring_title": "Expiring soon", - "banner_expiring_today": "Expires today!", - "banner_expiring_tomorrow": "Expires tomorrow", - "banner_expiring_days": "Expires in {days} days", - "banner_expiring_action_use": "Use now", - "banner_finished_title": "finished?", - "banner_finished_detail": "I recorded that {name} reached zero stock. Is it really gone, or do you still have some?", - "banner_finished_action_yes": "Yes, it's done", - "banner_finished_action_no": "No, I still have some", - "banner_review_unusual_pkg_title": "Unusual package size", - "banner_review_unusual_pkg_detail": "You set a package of {qty} {unit} — the size seems very large. Check if correct or edit.", - "banner_review_low_qty_title": "Very low quantity", - "banner_review_low_qty_detail": "You only have {qty} in stock — seems very little, could be a typo. Confirm if correct.", - "banner_review_high_qty_title": "Unusually high quantity", - "banner_review_high_qty_detail": "You have {qty} in stock — the figure seems very high. Confirm if correct or edit.", - "banner_prediction_rate_day": "Average ~{n} {unit}/day", - "banner_prediction_rate_week": "Average ~{n} {unit}/week", - "banner_prediction_days_ago": "{n} days ago you restocked", - "banner_prediction_more": "I expected {expected} {unit}{time}, but you have {actual} {unit}. Did you add stock without recording it?", - "banner_prediction_less": "I expected {expected} {unit}{time}, but you only have {actual} {unit}. Did you use more than usual?", - "banner_finished_zero": "Inventory shows zero, but recorded movements suggest it shouldn't be empty.", - "banner_finished_expected": "According to records you should still have {qty} {unit}.", - "banner_finished_check": "Can you check?", - "banner_anomaly_phantom_title": "you have more stock than expected", - "banner_anomaly_phantom_detail": "Inventory shows {inv_qty} {unit}, but based on records you should only have {expected_qty} {unit}. Did you add stock without recording it?", - "banner_anomaly_untracked_title": "stock not recorded as an entry", - "banner_anomaly_untracked_detail": "You have {inv_qty} {unit} in inventory, but recorded outflows exceed inflows — the initial stock was likely never added as an \"in\" transaction. You can correct the quantity or log the missing entries.", - "banner_anomaly_ghost_title": "you have less stock than expected", - "banner_anomaly_ghost_detail": "Based on recorded operations you should have {expected_qty} {unit} of {name}, but inventory shows only {inv_qty} {unit}. Did you take stock without recording it?", - "consumed": "Consumed: {n} ({pct}%)", - "wasted": "Wasted: {n} ({pct}%)", - "more_opened": "and {n} more opened...", - "banner_expired_detail": "{when} · you still have {qty}." + "camera": { + "title": "📷 Camera", + "hint": "Choose which camera to use for barcode scanning and AI identification.", + "device_label": "📸 Default camera", + "back": "📱 Rear (default)", + "front": "🤳 Front", + "devices_hint": "If you have multiple cameras, you can select a specific one from the list above after granting permissions.", + "detect_btn": "🔄 Detect cameras" }, - "inventory": { - "title": "Pantry", - "filter_all": "All", - "search_placeholder": "🔍 Search product...", - "recent_title": "🕐 Recently used", - "popular_title": "⭐ Most used", - "empty": "No products here.\nScan a product to add it!", - "no_items_found": "No inventory items found", - "qty_remainder_suffix": "left", - "vacuum_badge": "🫙 Vacuum sealed", - "opened_badge": "📭 Opened", - "label_expiry": "📅 Expiry", - "label_storage": "🫙 Storage", - "label_status": "📭 Status", - "opened_since": "Opened since {date}", - "label_position": "📍 Location", - "label_quantity": "📦 Quantity", - "label_added": "📅 Added", - "empty_text": "No products here.
Scan a product to add it!", - "empty_db": "No products in the database.
Scan a product to get started!", - "qty_trace": "< 1" + "security": { + "title": "🔒 HTTPS Certificate", + "hint": "If the browser shows the error \"Your connection is not private\" (ERR_CERT_AUTHORITY_INVALID), you need to install the CA certificate on the device.", + "download_btn": "📥 Download CA Certificate" }, - "scan": { - "title": "Scan Product", - "mode_shopping": "🛒 Shopping Mode", - "mode_shopping_end": "✅ End shopping", - "zoom": "Zoom", - "barcode_placeholder": "Enter barcode...", - "quick_name_divider": "or type the name", - "quick_name_placeholder": "E.g.: Apples, Zucchini, Bread...", - "manual_entry": "✏️ Manual Entry", - "ai_identify": "🤖 Identify with AI", - "hint": "Scan the barcode, type the product name, or use AI to identify it", - "debug_toggle": "🐛 Debug Log", - "barcode_acquired": "🔖 Barcode scanned: {code}", - "scan_barcode": "🔖 Scan Barcode", - "create_named": "Create {name}", - "new_without_barcode": "New product without barcode" + "tts": { + "title": "🔊 Voice & TTS", + "hint": "Configure text-to-speech via any external REST API. Recipe steps and expired timers will be sent to the configured endpoint.", + "enabled": "✅ Enable TTS", + "url_label": "🌐 Endpoint URL", + "method_label": "📡 HTTP Method", + "auth_label": "🔐 Authentication", + "auth_bearer": "Bearer Token", + "auth_custom": "Custom Header", + "auth_none": "None", + "token_label": "🔑 Bearer Token", + "custom_header_name": "📋 Header name", + "custom_header_value": "📋 Header value", + "content_type_label": "📄 Content-Type", + "payload_key_label": "🗝️ Text field in payload", + "payload_key_hint": "Name of the JSON field that will contain the text to read (e.g.: message, text).", + "extra_fields_label": "➕ Extra fields (JSON)", + "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}", + "extra_fields_hint": "Additional fields to include in the payload, in JSON format. Leave empty if not needed.", + "test_btn": "🔊 Send Test Voice" }, - "action": { - "title": "What do you want to do?", - "add_btn": "📥 ADD", - "add_sub": "to pantry/fridge", - "use_btn": "📤 USE / CONSUME", - "use_sub": "from pantry/fridge", - "have_title": "📦 Already in stock!", - "add_more_sub": "add more", - "use_qty_sub": "how much you used", - "throw_btn": "🗑️ DISCARD", - "throw_sub": "throw away", - "edit_sub": "expiry, location…", - "create_recipe_btn": "Create a recipe with this" - }, - "add": { - "title": "Add to Pantry", - "location_label": "📍 Where do you put it?", - "quantity_label": "📦 Quantity", - "conf_size_label": "📦 Each package contains:", - "conf_size_placeholder": "e.g. 300", - "vacuum_label": "🫙 Vacuum sealed", - "vacuum_hint": "Expiry date will be extended automatically", - "submit": "✅ Add", - "purchase_type_label": "🛒 This product is...", - "new_btn": "🆕 Just bought", - "existing_btn": "📦 I already had it", - "remaining_label": "📦 Remaining quantity", - "remaining_hint": "Approximately how much is left?", - "remaining_full": "🟢 Full", - "remaining_half": "🟠 Half", - "estimated_expiry": "Estimated expiry:", - "suffix_freezer": "(freezer)", - "suffix_vacuum": "(vacuum sealed)", - "hint_modify": "📝 You can change the date or scan it with the camera", - "scan_expiry_title": "📷 Scan Expiry Date", - "product_added": "✅ {name} added!{qty}", - "suffix_freezer_vacuum": "(freezer + vacuum sealed)" - }, - "use": { - "title": "Use / Consume", - "location_label": "📍 From where?", - "quantity_label": "How much did you use?", - "change": "change", - "partial_hint": "Or specify the quantity used:", - "partial_piece_hint": "Did you use only a part?", - "piece": "piece", - "one_whole": "1 whole", - "use_all": "🗑️ Used ALL / Finished", - "submit": "📤 Use this quantity", - "available": "📦 Available:", - "opened_badge": "OPENED", - "not_in_inventory": "⚠️ Product not in inventory.", - "expiry_warning": "⚠️ Use first the one{loc} that expires on {date} — {when}!", - "throw_title": "🗑️ Discard Product", - "throw_all": "🗑️ Discard ALL ({qty})", - "throw_qty_label": "How much to discard?", - "throw_qty_hint": "or enter a quantity:", - "throw_partial_btn": "🗑️ Discard this quantity", - "when_expired": "expired {n} days ago", - "when_today": "expires today", - "when_tomorrow": "expires tomorrow", - "when_days": "expires in {n} days", - "toast_used": "📤 Used {qty} of {name}", - "toast_bring": "🛒 Product finished → added to Bring!", - "toast_opened_finished": "🔓 Opened package of {name} finished!", - "disambiguation_hint": "What do you mean by \"all done\"?", - "disambiguation_all": "🗑️ Finish EVERYTHING ({qty})", - "error_exceeds_stock": "⚠️ You cannot use more than you have available!", - "use_all_confirm_title": "✅ Finish everything", - "use_all_confirm_msg": "Confirm that you have finished the product:", - "use_all_confirm_btn": "✅ Yes, finished", - "throw_all_confirm_title": "🗑️ Discard everything", - "throw_all_confirm_msg": "Do you really want to throw away the whole product?", - "throw_all_confirm_btn": "🗑️ Yes, discard" - }, - "product": { - "title_new": "New Product", - "title_edit": "Edit Product", - "ai_fill": "📷 Take photo and identify with AI", - "ai_fill_hint": "AI will automatically fill in the product fields", - "name_label": "🏷️ Product Name *", - "name_placeholder": "E.g.: Whole milk, Penne pasta...", - "brand_label": "🏢 Brand", - "brand_placeholder": "E.g.: Barilla, Granarolo, Mutti...", - "category_label": "📂 Category", - "unit_label": "📏 Unit of measure", - "default_qty_label": "🔢 Default quantity", - "conf_size_label": "📦 Each package contains:", - "conf_size_placeholder": "e.g. 300", - "notes_label": "📝 Notes", - "notes_placeholder": "E.g.: lactose free, organic, store in fridge after opening...", - "barcode_label": "🔖 Barcode", - "barcode_placeholder": "Barcode (if available)", - "barcode_hint": "⚠️ Add the barcode so next time you just need to scan it!", - "submit": "💾 Save Product", - "name_required": "Enter the product name", - "conf_size_required": "Specify the package content", - "expiry_estimated": "Estimated expiry:", - "scan_expiry": "Scan expiry date", - "expiry_hint": "📝 You can edit the date or scan it with the camera", - "add_batch": "📦 + Batch with different expiry", - "package_info": "📦 Package: {info}", - "edit_catalog": "⚙️ Edit product info (name, brand, category…)", - "not_recognized": "⚠️ Product not recognized", - "edit_info": "✏️ Edit information", - "modify_details": "EDIT\nexpiry, location…", - "already_in_pantry": "📋 Already in pantry", - "no_barcode": "No barcode" - }, - "products": { - "title": "📦 All Products", - "search_placeholder": "🔍 Search product...", - "empty": "No products in database.\nScan a product to get started!", - "no_category": "No products in this category" - }, - "recipes": { - "title": "🍳 Recipes", - "generate": "✨ Generate new recipe", - "archive_empty": "No recipes saved. Generate your first recipe!", - "dialog_title": "🍳 Recipe", - "dialog_desc": "I will generate a healthy recipe using pantry ingredients, prioritizing expiring items.", - "meal_label": "🕐 Which meal?", - "persons_label": "👥 How many people?", - "meal_type_label": "🎯 Meal type", - "opt_fast": "⚡ Quick meal", - "opt_light": "🥗 Light appetite", - "opt_expiry": "⏰ Prioritize expiring items", - "opt_healthy": "💚 Extra healthy", - "opt_opened": "📦 Prioritize opened items", - "opt_zero_waste": "♻️ Zero waste", - "generate_btn": "✨ Generate Recipe", - "loading_msg": "Preparing your recipe...", - "start_cooking": "👨‍🍳 Cooking Mode", - "regenerate": "🔄 Generate another one", - "close_btn": "✅ Close", - "ingredients_title": "🧾 Ingredients", - "steps_title": "👨‍🍳 Steps", - "no_steps": "No steps available", - "generate_error": "Generation error", - "persons_short": "serv.", - "use_ingredient_title": "Use ingredient", - "recipe_qty_label": "Recipe", - "from_where_label": "From where?", - "amount_label": "How much", - "use_amount_btn": "Use this amount", - "use_all_btn": "Use ALL / Finished", - "packs_label": "Packs", - "quantity_in_total": "Quantity in {unit} (total: {total})", - "packs_of_have": "Packs of {size} (you have {count} packs)", - "scale_wait_stable": "Wait 10s of stable weight for auto-fill…", - "ingredient_scaled_toast": "📦 Ingredient deducted from pantry!", - "finished_added_bring_toast": "🛒 Finished product → added to Bring!", - "load_error": "Loading error" - }, - "shopping": { - "title": "🛒 Shopping List", - "bring_loading": "Connecting to Bring!...", - "bring_not_configured": "Bring! is not configured. Add your email and password in settings.", - "tab_to_buy": "🛍️ To buy", - "tab_forecast": "🧠 Forecast", - "total_label": "💰 Estimated total", - "section_to_buy": "🛍️ To buy", - "suggestions_title": "💡 AI Suggestions", - "suggestions_add": "✅ Add selected to Bring!", - "search_prices": "🔍 Search all prices", - "suggest_btn": "Suggest what to buy", - "smart_title": "🧠 Smart Predictions", - "smart_empty": "No predictions available.
Add products to your pantry to receive smart predictions.", - "smart_filter_all": "All", - "smart_filter_critical": "🔴 Urgent", - "smart_filter_high": "🟠 Soon", - "smart_filter_medium": "🟡 Plan", - "smart_filter_low": "🟢 Forecast", - "smart_add": "🛒 Add selected to Bring!", - "empty": "Shopping list empty!\nUse the button below to generate suggestions.", - "already_in_list": "🛒 \"{name}\" is already in the shopping list", - "already_in_list_short": "ℹ️ Already in the shopping list", - "add_prompt": "Do you want to add it to the shopping list?", - "smart_already": "📊 Smart shopping already predicts {name}", - "all_searched": "All products have already been searched. Use 🔄 to search individual ones.", - "search_complete": "Search complete: {count} products", - "removed_sufficient": "🧹 {removed} product(s) with sufficient stock removed from the list", - "suggest_buy": "🛒 Buy: {qty} {unit}", - "suggest_buy_approx": "🛒 At least: {qty} {unit}", - "suggest_buy_tip": "Suggested quantity based on your last 14 days of consumption", - "suggest_buy_approx_tip": "Minimum estimate based on consumption (buy the nearest package size)", - "bring_badge": "🛒 Already on Bring!", - "add_urgent_toast": "🔴 {n} urgent product(s) automatically added to Bring!", - "migration_done": "✅ {migrated} updated, {skipped} already ok", - "added_to_bring": "🛒 {n} products added to Bring!", - "added_to_bring_skip": "{n} already present", - "all_on_bring": "All products were already on Bring!", - "freq_high": "📈 Frequent", - "freq_regular": "📊 Regular", - "freq_occasional": "📉 Occasional", - "out_of_stock": "Out of stock", - "scan_toast": "📷 Scan: {name}", - "empty_category": "No products in this category", - "session_empty": "🛒 No products yet", - "urgency_critical": "Urgent", - "urgency_high": "Soon", - "urgency_medium": "Plan", - "urgency_low": "Forecast", - "urgency_medium_short": "Medium", - "urgency_low_short": "Ok", - "tag_urgent": "🔴 Urgent", - "tag_priority": "⭐ Priority", - "tag_check": "✅ Check", - "smart_already_predicted": "📊 Smart shopping already predicts {name}{urgency}.", - "item_removed": "✅ {name} removed from list!", - "urgency_spec_critical": "⚡ Urgent", - "urgency_spec_high": "🟠 Soon", - "bring_add_n": "Add {n} to Bring!", - "bring_add_selected": "Add selected to Bring!", - "bring_adding": "Adding...", - "bring_added_one": "1 product added to Bring!", - "bring_added_many": "{n} products added to Bring!", - "bring_skipped": "({n} already in list)", - "force_sync": "Force Bring! sync", - "scan_target_label": "You are looking for", - "scan_target_found": "Found! Remove from list", - "bring_add_one": "Add 1 product to Bring!", - "bring_add_many": "Add {n} products to Bring!", - "syncing": "Syncing…", - "sync_done": "Sync completed", - "price_searching": "Searching...", - "search_action": "Search", - "open_action": "Open", - "not_found": "Not found", - "search_price": "Search price", - "tap_to_scan": "Tap to scan", - "tag_title": "Tag", - "remove_title": "Remove", - "found_count": "{found}/{total} products found", - "savings_offers": "· 🏷️ You save €{amount} with offers", - "searching_progress": "Searching {current}/{total}...", - "remove_error": "Removal error", - "btn_fetch_prices": "Find prices", - "price_total_label": "💰 Estimated total:", - "price_loading": "Looking up prices…", - "price_not_found": "price n/a", - "suggest_loading": "Analyzing...", - "suggest_error": "Suggestion generation error", - "priority_high": "High", - "priority_medium": "Medium", - "priority_low": "Low" - }, - "ai": { - "title": "🤖 AI Identification", - "capture": "📸 Take Photo", - "retake": "🔄 Retake", - "hint": "Take a photo of the product and AI will try to identify it", - "identifying": "🤖 Identifying product...", - "no_api_key": "⚠️ Gemini API key not configured.\nAdd GEMINI_API_KEY to the .env file on the server.", - "fields_filled": "✅ Fields filled by AI", - "use_data": "✅ Use AI data", - "use_data_no_barcode": "✅ Use AI data (no barcode)" - }, - "log": { - "title": "📒 Operations Log", - "type_added": "Added", - "type_waste": "Discarded", - "type_used": "Used", - "type_bring": "Added to Bring!", - "undone_badge": "Undone", - "undo_title": "Undo this operation", - "load_error": "Error loading log", - "empty": "No operations recorded.", - "undo_action_remove": "removal of", - "undo_action_restore": "restock of", - "undo_confirm": "Undo this operation?\n→ {action} {name}", - "undo_success": "↩ Operation undone for {name}", - "already_undone": "Operation already undone", - "too_old": "Cannot undo operations older than 24 hours", - "undo_error": "Error during undo" - }, - "chat": { - "title": "Gemini Chef", - "welcome": "Hi! I'm your kitchen assistant", - "welcome_desc": "Ask me to make you a juice, a snack, a quick dish... I know your pantry, your appliances and your preferences!", - "suggestion_snack": "🍿 Quick snack", - "suggestion_juice": "🥤 Juice/Smoothie", - "suggestion_light": "🥗 Something light", - "suggestion_expiry": "⏰ Use expiring items", - "clear": "New conversation", - "placeholder": "Ask something...", - "cleared": "Chat cleared", - "suggestion_snack_text": "What can I make for a quick snack?", - "suggestion_juice_text": "Make me a juice or smoothie with what I have", - "suggestion_light_text": "I'm hungry but want something light", - "suggestion_expiry_text": "What's about to expire and how can I use it?", - "transfer_to_recipes": "Transfer to Recipes", - "transferring": "Transferring...", - "transferred": "Added to Recipes!", - "open_recipe": "Open recipe" - }, - "cooking": { - "close": "Close", - "tts_btn": "Read aloud", - "restart": "↺ Restart", - "replay": "🔊 Replay", - "timer": "⏱️ {time} · Timer", - "prev": "◀ Previous", - "next": "Next ▶", - "ingredient_used": "✔️ Deducted", - "ingredient_use_btn": "📦 Use", - "ingredient_deduct_title": "Deduct from pantry", - "timer_expired_tts": "Timer {label} expired!", - "timer_warning_tts": "Heads up! {label}: 10 seconds left!", - "recipe_done_tts": "Recipe complete! Enjoy your meal!", - "expires_chip": "exp. {date}", - "finish": "✅ Finish" - }, - "settings": { - "title": "⚙️ Settings", - "tab_api": "API Keys", - "tab_bring": "Bring!", - "tab_recipe": "Recipes", - "tab_mealplan": "Weekly Plan", - "tab_appliances": "Appliances", - "tab_spesa": "Online Shopping", - "tab_camera": "Camera", - "tab_security": "Security", - "tab_tts": "Voice (TTS)", - "tab_language": "Language", - "tab_scale": "Smart Scale", - "gemini": { - "title": "🤖 Google Gemini AI", - "hint": "API key for product identification, expiry dates and recipes.", - "key_label": "Gemini API Key" - }, - "bring": { - "title": "🛒 Bring! Shopping List", - "hint": "Credentials for the Bring! shopping list integration.", - "email_label": "📧 Bring! Email", - "password_label": "🔒 Bring! Password" - }, - "price": { - "title": "💰 Price Estimation (AI)", - "hint": "Show estimated cost per product in the shopping list using AI.", - "enabled_label": "Enable price estimation", - "country_label": "🌍 Reference country", - "currency_label": "💱 Currency", - "update_label": "🔄 Refresh prices every", - "update_suffix": "months" - }, - "recipe": { - "title": "🍳 Recipe Preferences", - "hint": "Configure the default options for recipe generation.", - "persons_label": "👥 Default servings", - "options_label": "🎯 Default recipe options", - "fast": "⚡ Quick Meal", - "light": "🥗 Light Meal", - "expiry": "⏰ Expiry Priority", - "healthy": "💚 Extra Healthy", - "opened": "📦 Open Items Priority", - "zerowaste": "♻️ Zero Waste", - "dietary_label": "🚫 Intolerances / Restrictions", - "dietary_placeholder": "E.g.: gluten free, lactose free, vegetarian..." - }, - "mealplan": { - "title": "📅 Weekly Meal Plan", - "hint": "Set the meal type for each day. It will be used as a guide in recipe generation.", - "enabled": "✅ Enable weekly meal plan", - "legend": "🌤️ = Lunch · 🌙 = Dinner · Tap a badge to change it.", - "types_title": "📋 Available types" - }, - "appliances": { - "title": "🔌 Available Appliances", - "hint": "Indicate the appliances you have. They will be considered in recipe generation.", - "new_placeholder": "E.g.: Bread machine, Thermomix, Air fryer...", - "quick_title": "Quick add:", - "oven": "🔥 Oven", - "microwave": "📡 Microwave", - "air_fryer": "🍟 Air fryer", - "bread_maker": "🍞 Bread maker", - "bimby": "🤖 Thermomix/Cookeo", - "mixer": "🌀 Stand mixer", - "steamer": "♨️ Steamer", - "pressure_cooker": "🫕 Pressure cooker", - "toaster": "🍞 Toaster", - "blender": "🍹 Blender", - "empty": "No appliances added" - }, - "spesa": { - "title": "🛍️ Online Shopping", - "hint": "Configure the online shopping provider.", - "provider_label": "🏪 Provider", - "email_label": "📧 Email", - "password_label": "🔒 Password", - "login_btn": "🔐 Login", - "ai_prompt_label": "🤖 AI product selection prompt", - "ai_prompt_placeholder": "Instructions for AI when choosing between multiple products...", - "ai_prompt_hint": "AI uses this prompt to choose the most appropriate product from results. Leave empty for default behavior.", - "configure_first": "Configure Online Shopping in settings first", - "missing_credentials": "Enter email and password", - "login_in_progress": "Signing in...", - "login_error_prefix": "Error:", - "login_network_error_prefix": "Network error:", - "login_success_default": "Login successful!", - "result_name_label": "Name", - "result_card_label": "Card", - "result_pickup_label": "Pickup point", - "result_points_label": "Loyalty points", - "connected_relogin": "✅ Connected — Sign in again", - "connected_as": "Connected as {name}" - }, - "camera": { - "title": "📷 Camera", - "hint": "Choose which camera to use for barcode scanning and AI identification.", - "device_label": "📸 Default camera", - "back": "📱 Rear (default)", - "front": "🤳 Front", - "devices_hint": "If you have multiple cameras, you can select a specific one from the list above after granting permissions.", - "detect_btn": "🔄 Detect cameras" - }, - "security": { - "title": "🔒 HTTPS Certificate", - "hint": "If the browser shows the error \"Your connection is not private\" (ERR_CERT_AUTHORITY_INVALID), you need to install the CA certificate on the device.", - "download_btn": "📥 Download CA Certificate" - }, - "tts": { - "title": "🔊 Voice & TTS", - "hint": "Configure text-to-speech via any external REST API. Recipe steps and expired timers will be sent to the configured endpoint.", - "enabled": "✅ Enable TTS", - "url_label": "🌐 Endpoint URL", - "method_label": "📡 HTTP Method", - "auth_label": "🔐 Authentication", - "auth_bearer": "Bearer Token", - "auth_custom": "Custom Header", - "auth_none": "None", - "token_label": "🔑 Bearer Token", - "custom_header_name": "📋 Header name", - "custom_header_value": "📋 Header value", - "content_type_label": "📄 Content-Type", - "payload_key_label": "🗝️ Text field in payload", - "payload_key_hint": "Name of the JSON field that will contain the text to read (e.g.: message, text).", - "extra_fields_label": "➕ Extra fields (JSON)", - "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}", - "extra_fields_hint": "Additional fields to include in the payload, in JSON format. Leave empty if not needed.", - "test_btn": "🔊 Send Test Voice" - }, - "language": { - "title": "🌐 Language", - "hint": "Select the interface language.", - "label": "🌐 Language", - "restart_notice": "The page will reload to apply the new language." - }, - "screensaver": { - "label": "Enable screensaver", - "card_title": "🌙 Screensaver", - "card_hint": "Shows a clock with useful facts after 5 minutes of inactivity. Disabled by default." - }, - "scale": { - "title": "⚖️ Smart Scale", - "hint": "Connect a Bluetooth scale via the Android gateway to automatically read weight.", - "tab": "Smart Scale", - "enabled": "✅ Enable smart scale", - "url_label": "🌐 WebSocket Gateway URL", - "url_placeholder": "ws://192.168.1.x:8765", - "url_hint": "URL shown by the Android app (same Wi-Fi network). E.g.:", - "test_btn": "🔗 Test connection", - "download_btn": "📥 Download Android Gateway (APK)", - "download_hint": "Android app that bridges your BLE scale and EverShelf.", - "download_sub": "Source: evershelf-scale-gateway/ in the project root" - }, - "kiosk": { - "hint": "Turn an Android tablet into an always-on EverShelf panel with built-in BLE scale gateway.", - "download_btn": "📥 Download EverShelf Kiosk (APK)", - "download_sub": "Full-screen kiosk mode + integrated scale gateway. Source: evershelf-kiosk/" - }, - "saved": "✅ Configuration saved!", - "saved_local": "✅ Configuration saved locally", - "saved_local_error": "⚠️ Saved locally, server error: {error}" - }, - "expiry": { - "today": "TODAY", - "tomorrow": "Tomorrow", - "days": "{days} days", - "expired_days": "{days}d ago", - "expired_yesterday": "Yesterday", - "expired_today": "Today", - "badge_today": "⚠️ Expires today!", - "badge_tomorrow": "⏰ Tomorrow", - "badge_tomorrow_long": "⏰ Expires tomorrow", - "badge_days": "⏰ {n} days", - "badge_expired_ago": "⚠️ Expired {n}d ago", - "badge_expired": "⛔ Expired!", - "badge_stable": "✅ Stable", - "badge_expiring_short": "⏰ Exp. in {n}d", - "badge_ok_still": "✅ Still {n}d", - "badge_expires_red": "🔴 Exp. in {n}d", - "badge_expires_yellow": "🟡 Exp. in {n}d", - "badge_expired_bare": "⚠️ Expired", - "badge_expires_warn": "⚠️ Exp. in {n}d", - "badge_days_left": "⏳ ~{n}d left", - "days_approx": "~{n} days", - "weeks_approx": "~{n} weeks", - "months_approx": "~{n} months", - "years_approx": "~{n} years", - "expired_today_long": "Expired today", - "expired_ago_long": "Expired {n} days ago", - "expired_suffix": "— Expired!", - "expired_suffix_ok": "— Expired (still ok)", - "expired_suffix_warning": "— Expired (check first)", - "days_compact": "{n}d" - }, - "status": { - "ok": "OK", - "check": "Check", - "discard": "Discard", - "tip_freezer_ok": "In freezer: still safe (~{n}d margin)", - "tip_freezer_check": "In freezer for a long time, may have lost quality. Consume soon", - "tip_freezer_danger": "In freezer too long, risk of freezer burn and degradation", - "tip_highRisk_check": "Expired recently, check smell and appearance before consuming", - "tip_highRisk_danger": "Perishable product expired: discard for safety", - "tip_medRisk_check1": "Check appearance and smell before consuming", - "tip_medRisk_check2": "Expired a while ago, check carefully before use", - "tip_medRisk_danger": "Too long since expiry, better to discard", - "tip_lowRisk_ok": "Long-lasting product, still safe to consume", - "tip_lowRisk_check": "Expired over a month ago, check package integrity", - "tip_lowRisk_danger": "Expired too long ago, better not to risk it" - }, - "toast": { - "product_saved": "Product saved!", - "product_created": "Product created!", - "product_updated": "✅ Product updated!", - "product_removed": "Product removed", - "updated": "Updated!", - "quantity_confirmed": "✓ Quantity confirmed", - "added_to_inventory": "✅ {name} added!", - "removed_from_list": "✅ {name} removed from the list!", - "removed_from_list_short": "Removed from the list", - "added_to_shopping": "🛒 Added to the shopping list!", - "removed_from_shopping": "🛒 Removed from the shopping list", - "finished_to_bring": "🛒 Product finished → added to Bring!", - "thrown_away": "🗑️ {name} thrown away!", - "thrown_away_partial": "🗑️ Thrown away {qty} {unit} of {name}", - "product_finished_confirmed": "✅ Removed — add it again when you restock", - "appliance_added": "Appliance added", - "item_added": "{name} added" - }, - "antiwaste": { - "title": "🌱 Anti-Waste Report", - "grade_label": "Grade", - "you": "You", - "avg_label": "Avg", - "better": "🎉 You lose {diff}% less than the {country}!", - "worse": "⚠️ You lose more than the {country}. Room for improvement!", - "on_par": "→ You're at the {country}. You can do better!", - "saved_money": "~{amount}/month saved", - "saved_meals": "~{n} meals saved", - "saved_co2": "{n} kg CO₂ avoided", - "trend_title": "Trend (last 3 months)", - "months_ago_2": "-60 days", - "months_ago_1": "-30 days", - "this_month": "Now", - "country_it": "Italian avg", - "country_de": "German avg", - "country_en": "US average", - "source": "Sources: REDUCE, Eurostat, USDA 2021", - "live_on": "Live data", - "live_off": "Offline", - "meals": "meals", - "annual_info": "📅 You ~{you} kg/yr · avg ~{avg} kg/yr", - "badge_rate": "loss rate", - "badge_saved_money": "saved vs avg", - "badge_wasted": "items lost", - "badge_better": "less than avg" - }, - "error": { - "generic": "Error", - "network": "Network error", - "no_api_key": "Configure the API key in settings", - "loading": "Error loading product", - "not_found": "Product not found", - "not_found_manual": "Product not found. Enter it manually.", - "search": "Search error. Try again.", - "search_short": "Search error", - "save": "Error saving", - "connection": "Connection error", - "camera": "Cannot access camera", - "bring_add": "Error adding to Bring!", - "bring_connection": "Bring! connection error", - "identification": "Identification error", - "ai_quota": "AI quota exhausted. Please try again in a few minutes.", - "barcode_empty": "Enter a barcode", - "barcode_format": "Barcode must contain only numbers (4-14 digits)", - "min_chars": "Type at least 2 characters", - "not_in_inventory": "Product not in inventory", - "appliance_exists": "Appliance already exists", - "already_exists": "Already exists", - "network_retry": "Connection error. Try again.", - "select_items": "Select at least one product", - "server_offline": "Server connection lost", - "server_restored": "Server connection restored", - "server_retry": "Retry" - }, - "confirm": { - "remove_item": "Do you really want to remove this product from inventory?", - "kiosk_exit": "Exit kiosk mode?", - "cancel": "Cancel", - "proceed": "Confirm" - }, - "location": { - "dispensa": "Pantry", - "frigo": "Fridge", - "freezer": "Freezer" - }, - "edit": { - "title": "Edit {name}", - "unknown_hint": "Enter the product name and information", - "label_name": "🏷️ Product name" + "language": { + "title": "🌐 Language", + "hint": "Select the interface language.", + "label": "🌐 Language", + "restart_notice": "The page will reload to apply the new language." }, "screensaver": { - "recipe_btn": "Recipes", - "scan_btn": "Scan product" - }, - "days": { - "mon": "Monday", - "tue": "Tuesday", - "wed": "Wednesday", - "thu": "Thursday", - "fri": "Friday", - "sat": "Saturday", - "sun": "Sunday", - "mon_short": "Mon", - "tue_short": "Tue", - "wed_short": "Wed", - "thu_short": "Thu", - "fri_short": "Fri", - "sat_short": "Sat", - "sun_short": "Sun" - }, - "meal_types": { - "lunch": "Lunch", - "dinner": "Dinner", - "colazione": "Breakfast", - "merenda": "Snack", - "dolce": "Dessert", - "succo": "Fruit Juice", - "pranzo": "Lunch", - "cena": "Dinner" + "label": "Enable screensaver", + "card_title": "🌙 Screensaver", + "card_hint": "Shows a clock with useful facts after 5 minutes of inactivity. Disabled by default." }, "scale": { - "status_connected": "Scale connected", - "status_searching": "Gateway connected, waiting for scale…", - "status_disconnected": "Scale gateway unreachable", - "status_error": "Gateway connection error", - "not_connected": "Scale gateway not connected", - "read_btn": "⚖️ Read from scale", - "reading_title": "Scale reading", - "place_on_scale": "Place the product on the scale…", - "waiting_stable": "Weight will be captured automatically once the reading is stable.", - "no_url": "Enter the gateway URL", - "testing": "⏳ Testing connection…", - "connected_ok": "Gateway connection successful!", - "timeout": "Timeout: no response from gateway", - "error_connect": "Cannot connect to gateway", - "tab": "Smart Scale", - "low_weight": "Weight < 10 g · enter manually\n(auto-reading requires at least 10 g)", - "density_hint": "(density {density} g/ml)", - "ml_hint": "(will be converted to ml)", - "weight_detected": "Weight detected — wait 10s for stability…", - "weight_too_low": "Weight too low — waiting…", - "stable": "✓ Stable", - "auto_confirm": "✅ {val} {unit} — auto-confirm in 5s (tap to cancel)", - "cancelled_replace": "Cancelled — replace the ingredient on the scale to resume" + "title": "⚖️ Smart Scale", + "hint": "Connect a Bluetooth scale via the Android gateway to automatically read weight.", + "tab": "Smart Scale", + "enabled": "✅ Enable smart scale", + "url_label": "🌐 WebSocket Gateway URL", + "url_placeholder": "ws://192.168.1.x:8765", + "url_hint": "URL shown by the Android app (same Wi-Fi network). E.g.:", + "test_btn": "🔗 Test connection", + "download_btn": "📥 Download Android Gateway (APK)", + "download_hint": "Android app that bridges your BLE scale and EverShelf.", + "download_sub": "Source: evershelf-scale-gateway/ in the project root" }, - "prediction": { - "expected_qty": "Expected: {expected} {unit}", - "actual_qty": "Current: {actual} {unit}", - "check_suggestion": "Check or weigh the remaining quantity" + "kiosk": { + "hint": "Turn an Android tablet into an always-on EverShelf panel with built-in BLE scale gateway.", + "download_btn": "📥 Download EverShelf Kiosk (APK)", + "download_sub": "Full-screen kiosk mode + integrated scale gateway. Source: evershelf-kiosk/" }, - "date": { - "today": "📅 Today", - "yesterday": "📅 Yesterday" - }, - "scanner": { - "title_barcode": "🔖 Scan Barcode", - "barcode_hint": "Frame the product barcode", - "barcode_manual_placeholder": "Or enter manually...", - "barcode_use_btn": "✅ Use this code", - "ai_identifying": "🤖 Identifying product...", - "ai_analyzing": "🤖 AI analysis in progress...", - "product_label_hint": "Frame the product label", - "expiry_label_hint": "Frame the expiry date printed on the product", - "capture_btn": "📸 Capture", - "capture_photo_btn": "📸 Take Photo", - "retake_btn": "🔄 Retake", - "camera_error_hint": "Ensure you use HTTPS and have granted camera permissions.
You can enter the barcode manually or use AI identification.", - "no_barcode": "No barcode" - }, - "lowstock": { - "title": "⚠️ Running low!", - "message": "{name} is running low — only {qty} remaining.", - "question": "Do you want to add it to the shopping list?", - "yes": "🛒 Yes, add to Bring!", - "no": "No, I'm fine for now" - }, - "move": { - "title": "📦 Move the rest?", - "question": "Do you want to move the {thing} of {name} to another location?", - "question_short": "Do you want to move the {thing} to another location?", - "thing_opened": "opened package", - "thing_rest": "rest", - "stay_btn": "No, stay in {location}", - "moved_toast": "📦 Opened package moved to {location}", - "vacuum_restore": "🫙 Restore vacuum sealed" - }, - "nova": { - "1": "Unprocessed", - "2": "Culinary ingredient", - "3": "Processed", - "4": "Ultra-processed" - }, - "meal_plan_types": { - "pasta": "Pasta", - "riso": "Rice", - "carne": "Meat", - "pesce": "Fish", - "legumi": "Legumes", - "uova": "Eggs", - "formaggio": "Cheese", - "pizza": "Pizza", - "affettati": "Cold Cuts", - "verdure": "Veggies", - "zuppa": "Soup", - "insalata": "Salad", - "pane": "Bread/Sandwich", - "dolce": "Dessert", - "libero": "Free" - }, - "meal_sub": { - "dolce_torta": "Cake", - "dolce_crema": "Cream / Pudding", - "dolce_crumble": "Crumble / Tart", - "dolce_biscotti": "Cookies / Pastries", - "dolce_frutta": "Fruit Dessert", - "succo_dolce": "Sweet / Fruity", - "succo_energizzante": "Energizing", - "succo_detox": "Detox / Green", - "succo_rinfrescante": "Refreshing", - "succo_vitaminico": "Vitamin / Citrus" - }, - "meal_plan": { - "reset_success": "Weekly plan reset", - "not_available": "not available in pantry", - "suggested_by": "suggested by weekly plan" - }, - "kiosk_session": { - "first_item": "First item: {name}!", - "items_two_four": "{n} items — warming up 🚀", - "items_five_nine": "{n} items — great pace! 💪", - "items_ten_twenty": "{n} items — almost a record 🏆", - "items_twenty_plus": "{n} items — epic shopping! 🛒🔥", - "duplicates_one": "1 duplicate (same thing twice)", - "duplicates_many": "{n} duplicates (picked multiple times)", - "top_category": "Top category: {cat} ({count}×)", - "items_fallback": "{n} item{plural} added" - } + "saved": "✅ Configuration saved!", + "saved_local": "✅ Configuration saved locally", + "saved_local_error": "⚠️ Saved locally, server error: {error}" + }, + "expiry": { + "today": "TODAY", + "tomorrow": "Tomorrow", + "days": "{days} days", + "expired_days": "{days}d ago", + "expired_yesterday": "Yesterday", + "expired_today": "Today", + "badge_today": "⚠️ Expires today!", + "badge_tomorrow": "⏰ Tomorrow", + "badge_tomorrow_long": "⏰ Expires tomorrow", + "badge_days": "⏰ {n} days", + "badge_expired_ago": "⚠️ Expired {n}d ago", + "badge_expired": "⛔ Expired!", + "badge_stable": "✅ Stable", + "badge_expiring_short": "⏰ Exp. in {n}d", + "badge_ok_still": "✅ Still {n}d", + "badge_expires_red": "🔴 Exp. in {n}d", + "badge_expires_yellow": "🟡 Exp. in {n}d", + "badge_expired_bare": "⚠️ Expired", + "badge_expires_warn": "⚠️ Exp. in {n}d", + "badge_days_left": "⏳ ~{n}d left", + "days_approx": "~{n} days", + "weeks_approx": "~{n} weeks", + "months_approx": "~{n} months", + "years_approx": "~{n} years", + "expired_today_long": "Expired today", + "expired_ago_long": "Expired {n} days ago", + "expired_suffix": "— Expired!", + "expired_suffix_ok": "— Expired (still ok)", + "expired_suffix_warning": "— Expired (check first)", + "days_compact": "{n}d" + }, + "status": { + "ok": "OK", + "check": "Check", + "discard": "Discard", + "tip_freezer_ok": "In freezer: still safe (~{n}d margin)", + "tip_freezer_check": "In freezer for a long time, may have lost quality. Consume soon", + "tip_freezer_danger": "In freezer too long, risk of freezer burn and degradation", + "tip_highRisk_check": "Expired recently, check smell and appearance before consuming", + "tip_highRisk_danger": "Perishable product expired: discard for safety", + "tip_medRisk_check1": "Check appearance and smell before consuming", + "tip_medRisk_check2": "Expired a while ago, check carefully before use", + "tip_medRisk_danger": "Too long since expiry, better to discard", + "tip_lowRisk_ok": "Long-lasting product, still safe to consume", + "tip_lowRisk_check": "Expired over a month ago, check package integrity", + "tip_lowRisk_danger": "Expired too long ago, better not to risk it" + }, + "toast": { + "product_saved": "Product saved!", + "product_created": "Product created!", + "product_updated": "✅ Product updated!", + "product_removed": "Product removed", + "updated": "Updated!", + "quantity_confirmed": "✓ Quantity confirmed", + "added_to_inventory": "✅ {name} added!", + "removed_from_list": "✅ {name} removed from the list!", + "removed_from_list_short": "Removed from the list", + "added_to_shopping": "🛒 Added to the shopping list!", + "removed_from_shopping": "🛒 Removed from the shopping list", + "finished_to_bring": "🛒 Product finished → added to Bring!", + "thrown_away": "🗑️ {name} thrown away!", + "thrown_away_partial": "🗑️ Thrown away {qty} {unit} of {name}", + "product_finished_confirmed": "✅ Removed — add it again when you restock", + "appliance_added": "Appliance added", + "item_added": "{name} added" + }, + "antiwaste": { + "title": "🌱 Anti-Waste Report", + "grade_label": "Grade", + "you": "You", + "avg_label": "Avg", + "better": "🎉 You lose {diff}% less than the {country}!", + "worse": "⚠️ You lose more than the {country}. Room for improvement!", + "on_par": "→ You're at the {country}. You can do better!", + "saved_money": "~{amount}/month saved", + "saved_meals": "~{n} meals saved", + "saved_co2": "{n} kg CO₂ avoided", + "trend_title": "Trend (last 3 months)", + "months_ago_2": "-60 days", + "months_ago_1": "-30 days", + "this_month": "Now", + "country_it": "Italian avg", + "country_de": "German avg", + "country_en": "US average", + "source": "Sources: REDUCE, Eurostat, USDA 2021", + "live_on": "Live data", + "live_off": "Offline", + "meals": "meals", + "annual_info": "📅 You ~{you} kg/yr · avg ~{avg} kg/yr", + "badge_rate": "loss rate", + "badge_saved_money": "saved vs avg", + "badge_wasted": "items lost", + "badge_better": "less than avg" + }, + "error": { + "generic": "Error", + "network": "Network error", + "no_api_key": "Configure the API key in settings", + "loading": "Error loading product", + "not_found": "Product not found", + "not_found_manual": "Product not found. Enter it manually.", + "search": "Search error. Try again.", + "search_short": "Search error", + "save": "Error saving", + "connection": "Connection error", + "camera": "Cannot access camera", + "bring_add": "Error adding to Bring!", + "bring_connection": "Bring! connection error", + "identification": "Identification error", + "ai_quota": "AI quota exhausted. Please try again in a few minutes.", + "barcode_empty": "Enter a barcode", + "barcode_format": "Barcode must contain only numbers (4-14 digits)", + "min_chars": "Type at least 2 characters", + "not_in_inventory": "Product not in inventory", + "appliance_exists": "Appliance already exists", + "already_exists": "Already exists", + "network_retry": "Connection error. Try again.", + "select_items": "Select at least one product", + "server_offline": "Server connection lost", + "server_restored": "Server connection restored", + "server_retry": "Retry" + }, + "confirm": { + "remove_item": "Do you really want to remove this product from inventory?", + "kiosk_exit": "Exit kiosk mode?", + "cancel": "Cancel", + "proceed": "Confirm" + }, + "location": { + "dispensa": "Pantry", + "frigo": "Fridge", + "freezer": "Freezer" + }, + "edit": { + "title": "Edit {name}", + "unknown_hint": "Enter the product name and information", + "label_name": "🏷️ Product name" + }, + "screensaver": { + "recipe_btn": "Recipes", + "scan_btn": "Scan product" + }, + "days": { + "mon": "Monday", + "tue": "Tuesday", + "wed": "Wednesday", + "thu": "Thursday", + "fri": "Friday", + "sat": "Saturday", + "sun": "Sunday", + "mon_short": "Mon", + "tue_short": "Tue", + "wed_short": "Wed", + "thu_short": "Thu", + "fri_short": "Fri", + "sat_short": "Sat", + "sun_short": "Sun" + }, + "meal_types": { + "lunch": "Lunch", + "dinner": "Dinner", + "colazione": "Breakfast", + "merenda": "Snack", + "dolce": "Dessert", + "succo": "Fruit Juice", + "pranzo": "Lunch", + "cena": "Dinner" + }, + "scale": { + "status_connected": "Scale connected", + "status_searching": "Gateway connected, waiting for scale…", + "status_disconnected": "Scale gateway unreachable", + "status_error": "Gateway connection error", + "not_connected": "Scale gateway not connected", + "read_btn": "⚖️ Read from scale", + "reading_title": "Scale reading", + "place_on_scale": "Place the product on the scale…", + "waiting_stable": "Weight will be captured automatically once the reading is stable.", + "no_url": "Enter the gateway URL", + "testing": "⏳ Testing connection…", + "connected_ok": "Gateway connection successful!", + "timeout": "Timeout: no response from gateway", + "error_connect": "Cannot connect to gateway", + "tab": "Smart Scale", + "low_weight": "Weight < 10 g · enter manually\n(auto-reading requires at least 10 g)", + "density_hint": "(density {density} g/ml)", + "ml_hint": "(will be converted to ml)", + "weight_detected": "Weight detected — wait 10s for stability…", + "weight_too_low": "Weight too low — waiting…", + "stable": "✓ Stable", + "auto_confirm": "✅ {val} {unit} — auto-confirm in 5s (tap to cancel)", + "cancelled_replace": "Cancelled — replace the ingredient on the scale to resume" + }, + "prediction": { + "expected_qty": "Expected: {expected} {unit}", + "actual_qty": "Current: {actual} {unit}", + "check_suggestion": "Check or weigh the remaining quantity" + }, + "date": { + "today": "📅 Today", + "yesterday": "📅 Yesterday" + }, + "scanner": { + "title_barcode": "🔖 Scan Barcode", + "barcode_hint": "Frame the product barcode", + "barcode_manual_placeholder": "Or enter manually...", + "barcode_use_btn": "✅ Use this code", + "ai_identifying": "🤖 Identifying product...", + "ai_analyzing": "🤖 AI analysis in progress...", + "product_label_hint": "Frame the product label", + "expiry_label_hint": "Frame the expiry date printed on the product", + "capture_btn": "📸 Capture", + "capture_photo_btn": "📸 Take Photo", + "retake_btn": "🔄 Retake", + "camera_error_hint": "Ensure you use HTTPS and have granted camera permissions.
You can enter the barcode manually or use AI identification.", + "no_barcode": "No barcode", + "save_new_btn": "🆕 None of these — save as new" + }, + "lowstock": { + "title": "⚠️ Running low!", + "message": "{name} is running low — only {qty} remaining.", + "question": "Do you want to add it to the shopping list?", + "yes": "🛒 Yes, add to Bring!", + "no": "No, I'm fine for now" + }, + "move": { + "title": "📦 Move the rest?", + "question": "Do you want to move the {thing} of {name} to another location?", + "question_short": "Do you want to move the {thing} to another location?", + "thing_opened": "opened package", + "thing_rest": "rest", + "stay_btn": "No, stay in {location}", + "moved_toast": "📦 Opened package moved to {location}", + "vacuum_restore": "🫙 Restore vacuum sealed" + }, + "nova": { + "1": "Unprocessed", + "2": "Culinary ingredient", + "3": "Processed", + "4": "Ultra-processed" + }, + "meal_plan_types": { + "pasta": "Pasta", + "riso": "Rice", + "carne": "Meat", + "pesce": "Fish", + "legumi": "Legumes", + "uova": "Eggs", + "formaggio": "Cheese", + "pizza": "Pizza", + "affettati": "Cold Cuts", + "verdure": "Veggies", + "zuppa": "Soup", + "insalata": "Salad", + "pane": "Bread/Sandwich", + "dolce": "Dessert", + "libero": "Free" + }, + "meal_sub": { + "dolce_torta": "Cake", + "dolce_crema": "Cream / Pudding", + "dolce_crumble": "Crumble / Tart", + "dolce_biscotti": "Cookies / Pastries", + "dolce_frutta": "Fruit Dessert", + "succo_dolce": "Sweet / Fruity", + "succo_energizzante": "Energizing", + "succo_detox": "Detox / Green", + "succo_rinfrescante": "Refreshing", + "succo_vitaminico": "Vitamin / Citrus" + }, + "meal_plan": { + "reset_success": "Weekly plan reset", + "not_available": "not available in pantry", + "suggested_by": "suggested by weekly plan" + }, + "nutrition": { + "title": "🥗 Food Analysis", + "score_excellent": "😄 Excellent", + "score_good": "🙂 Good", + "score_improve": "😬 Improvable", + "label_health": "🌿 Health", + "label_variety": "🎨 Variety", + "label_fresh": "❄️ Fresh", + "source": "Based on {n} products in your pantry · EverShelf", + "products_count": "products", + "today_title": "🥗 Your pantry today", + "products_n": "{n} products" + }, + "facts": { + "greeting_morning": "Good morning", + "greeting_afternoon": "Good afternoon", + "greeting_evening": "Good evening", + "pantry_waiting": "{greeting}! Your Pantry awaits.", + "expired_one": "You have 1 expired product in your pantry. Check it!", + "expired_many": "You have {n} expired products in your pantry. Check them!", + "expired_list": "Expired products: {names}", + "expired_list_more": "and {n} more", + "freezer_expired_ok": "{name} is expired, but being in the freezer it may still be fine! Check it.", + "freezer_expired_old": "{name} in the freezer has been expired too long. Better to discard it.", + "fridge_expired_one": "You have 1 expired product in the fridge!", + "fridge_expired_many": "You have {n} expired products in the fridge!", + "expiring_today": "{name} expires today! Use it right away.", + "expiring_tomorrow": "{name} expires tomorrow. Plan ahead!", + "expiring_days": "{name} expires in {days} days.", + "expiring_many": "You have {n} products expiring soon.", + "expiring_this_week": "{n} products expire this week. Plan your meals accordingly!", + "expiring_item_loc": "{name} ({loc}) expires in {days} {dayslabel}.", + "expiring_this_month": "{n} products will expire this month.", + "shopping_add": "Add to list: {names} 🛒", + "shopping_more": "and {n} more", + "shopping_empty": "Shopping list empty. All stocked up! ✅", + "in_fridge": "In the fridge: {name}.", + "in_freezer": "In the freezer: {name}. Don't forget it!", + "top_category": "Top category is {icon} {cat} with {n} products.", + "cat_meat": "You have {n} meat products. 🥩", + "cat_dairy": "You have {n} dairy products at home. 🥛", + "cat_veggies": "You have {n} types of vegetables. Great for your health! 🥬", + "cat_fruit": "You have {n} types of fruit. 🍎", + "cat_drinks": "You have {n} drinks available. 🥤", + "cat_frozen": "You have {n} frozen items. ❄️", + "cat_pasta": "You have {n} types of pasta. 🍝 How about a carbonara?", + "cat_canned": "You have {n} canned goods in your pantry. 🥫", + "cat_snacks": "You have {n} snacks. Resist the temptation! 🍪", + "cat_condiments": "You have {n} condiments available. 🧂", + "item_random": "Did you know? You have {name} in {loc}.", + "item_qty": "{name}: you have {qty}.", + "no_expiry_count": "{n} products have no expiry date set.", + "furthest_expiry": "The product with the furthest expiry is {name}: {months} months.", + "high_qty": "You have a great stock of {name}: {qty}!", + "low_qty_item": "{name} is running low. Add it to your shopping list?", + "low_qty_count": "{n} products are almost out.", + "morning_bread": "Good morning! You have bread for breakfast. 🍞", + "morning_milk": "Is there milk in the fridge for a cappuccino? ☕🥛", + "morning_fruit": "Good morning! Some fresh fruit is a great way to start. 🍎", + "noon_pasta": "Lunchtime… How about a nice bowl of pasta? 🍝", + "noon_salad": "A fresh salad for lunch? You have {n} vegetables! 🥗", + "evening_meat": "For dinner you could use the meat you have. 🥩", + "evening_fish": "How about fish for dinner? 🐟", + "evening_expiring": "You have {n} products expiring this week — use them tonight!", + "night_reminder": "Good night! Remember to use tomorrow: {names}.", + "weekly_balance": "Weekly balance: +{in} added, −{out} consumed.", + "weekly_added": "You added {n} products this week.", + "weekly_consumed": "You consumed {n} products this week. Well done!", + "tip_freezer": "💡 Frozen products last much longer than the expiry date.", + "tip_bread": "💡 Frozen bread keeps its freshness for weeks.", + "tip_fifo": "💡 To avoid waste, use products closest to expiry first (FIFO).", + "tip_meat": "💡 Meat in the freezer can last up to 6 months safely.", + "tip_no_refreeze": "💡 Never refreeze a thawed product. Cook it right away!", + "tip_fridge": "💡 A tidy fridge saves you time and money.", + "tip_canned": "💡 Opened canned goods should go in the fridge and be consumed within a few days.", + "top_brand": "The most common brand in your pantry is {brand} with {n} products.", + "combo_pasta": "You have pasta and condiments: ready for a first course! 🍝", + "combo_sandwich": "Bread and meat: a quick sandwich is always a good idea! 🥪", + "combo_balanced": "Vegetables and meat: you have everything for a balanced meal! 🥗🥩", + "pantry_empty": "The pantry is empty! Time to go shopping. 🛒", + "pantry_empty_scan": "No products registered. Scan something to start!", + "location_distribution": "Distribution: {parts}", + "day": "day", + "days": "days" + }, + "kiosk_session": { + "first_item": "First item: {name}!", + "items_two_four": "{n} items — warming up 🚀", + "items_five_nine": "{n} items — great pace! 💪", + "items_ten_twenty": "{n} items — almost a record 🏆", + "items_twenty_plus": "{n} items — epic shopping! 🛒🔥", + "duplicates_one": "1 duplicate (same thing twice)", + "duplicates_many": "{n} duplicates (picked multiple times)", + "top_category": "Top category: {cat} ({count}×)", + "items_fallback": "{n} item{plural} added" + }, + "kiosk": { + "check_btn": "🔍 Check for updates", + "checking": "⏳ Checking…", + "error_check": "Error during update check", + "error_start_install": "Error starting installation", + "version_installed": "Installed: {v}", + "update_available": "⬆️ New version available: {latest} (installed: {current})", + "up_to_date": "✅ You are up to date — version {v}", + "too_old": "⚠️ The installed kiosk is too old for automatic update checking.
Press the button below to download and install the new version directly.", + "manual_install": "⚠️ This kiosk does not support automatic installation.
Manual procedure:
1. Exit the kiosk (✕ button top left)
2. Uninstall the EverShelf Kiosk app
3. Download and install the new APK from GitHub:", + "starting_download": "⏳ Starting download…", + "install_btn": "⬇️ Install update", + "exit_title": "Exit kiosk", + "refresh_title": "Refresh page" + }, + "update": { + "new_version": "New version", + "btn": "Update" + }, + "gemini": { + "chat_title": "Chat with Gemini", + "not_configured": "🤖 Gemini not configured — set GEMINI_API_KEY in settings" + }, + "appliances": { + "empty": "No appliances added" + } } \ No newline at end of file diff --git a/translations/it.json b/translations/it.json index 5479a25..496f05f 100644 --- a/translations/it.json +++ b/translations/it.json @@ -1,946 +1,1064 @@ { - "app": { - "name": "EverShelf", - "loading": "Caricamento..." + "app": { + "name": "EverShelf", + "loading": "Caricamento..." + }, + "nav": { + "title": "EverShelf", + "home": "Home", + "inventory": "Dispensa", + "recipes": "Ricette", + "shopping": "Spesa", + "log": "Storico" + }, + "btn": { + "back": "← Indietro", + "save": "💾 Salva", + "cancel": "✕ Annulla", + "close": "Chiudi", + "add": "✅ Aggiungi", + "delete": "Elimina", + "edit": "✏️ Modifica", + "search": "🔍 Cerca", + "go": "✅ Vai", + "toggle_password": "👁️ Mostra/Nascondi", + "load_more": "Carica altri...", + "save_config": "💾 Salva Configurazione", + "save_product": "💾 Salva Prodotto", + "restart": "↺ Ricomincia", + "reset_default": "↺ Ripristina default", + "save_info": "💾 Salva informazioni", + "retry": "🔄 Riprova" + }, + "locations": { + "dispensa": "Dispensa", + "frigo": "Frigo", + "freezer": "Freezer", + "altro": "Altro" + }, + "categories": { + "latticini": "Latticini", + "carne": "Carne", + "pesce": "Pesce", + "frutta": "Frutta", + "verdura": "Verdura", + "pasta": "Pasta & Riso", + "pane": "Pane & Forno", + "surgelati": "Surgelati", + "bevande": "Bevande", + "condimenti": "Condimenti", + "snack": "Snack & Dolci", + "conserve": "Conserve", + "cereali": "Cereali & Legumi", + "igiene": "Igiene", + "pulizia": "Pulizia Casa", + "altro": "Altro", + "select": "-- Seleziona --" + }, + "units": { + "pz": "pz", + "conf": "conf", + "g": "g", + "ml": "ml", + "pieces": "Pezzi", + "grams": "Grammi", + "box": "Confezione", + "boxes": "Confezioni" + }, + "shopping_sections": { + "frutta_verdura": "Frutta & Verdura", + "carne_pesce": "Carne & Pesce", + "latticini": "Latticini & Fresco", + "pane_dolci": "Pane & Dolci", + "pasta": "Pasta & Cereali", + "conserve": "Conserve & Salse", + "surgelati": "Surgelati", + "bevande": "Bevande", + "pulizia_igiene": "Pulizia & Igiene", + "altro": "Altro" + }, + "dashboard": { + "expired_title": "🚫 Scaduti", + "expiring_title": "⏰ Prossime Scadenze", + "stats_period": "📊 Ultimi 30 giorni", + "opened_title": "📦 Prodotti Aperti", + "review_title": "🔍 Da revisionare", + "review_hint": "Quantità che sembrano anomale. Conferma se corrette o modifica.", + "quick_recipe": "Ricetta veloce con prodotti in scadenza", + "banner_review_title": "Quantità anomala", + "banner_review_action_ok": "È corretto", + "banner_review_action_finish": "🗑️ È finito tutto", + "banner_review_action_edit": "Correggi", + "banner_review_action_weigh": "Pesa", + "banner_review_dismiss": "Ignora", + "banner_prediction_title": "Consumo anomalo", + "banner_prediction_hint": "Secondo le previsioni, questa quantità non corrisponde al consumo previsto.", + "banner_prediction_action_confirm": "Confermo la quantità di {qty} {unit}", + "banner_prediction_action_weigh": "Pesa ora", + "banner_prediction_action_edit": "Aggiorna quantità", + "banner_expired_title": "Prodotto scaduto", + "banner_expired_today": "Scaduto oggi", + "banner_expired_days": "Scaduto da {days} giorni", + "banner_expired_action_use": "Usa comunque", + "banner_expired_action_throw": "L'ho buttato", + "banner_expired_action_edit": "Correggi data", + "banner_anomaly_action_edit": "Correggi inventario", + "banner_anomaly_action_dismiss": "La quantità è giusta", + "banner_no_expiry_title": "Scadenza mancante: {name}", + "banner_no_expiry_detail": "Questo prodotto non ha una data di scadenza. Vuoi aggiungerla o confermare che non scade?", + "banner_no_expiry_action_set": "Imposta scadenza", + "banner_no_expiry_action_dismiss": "Non scade ✓", + "banner_no_expiry_toast_dismissed": "Segnato come 'non scade'", + "banner_expiring_title": "In scadenza", + "banner_expiring_today": "Scade oggi!", + "banner_expiring_tomorrow": "Scade domani", + "banner_expiring_days": "Scade tra {days} giorni", + "banner_expiring_action_use": "Usa ora", + "banner_finished_title": "è finito?", + "banner_finished_detail": "Ho registrato che {name} ha toccato quota zero. È davvero finito o hai ancora delle scorte?", + "banner_finished_action_yes": "Sì, è finito", + "banner_finished_action_no": "No, ne ho ancora", + "banner_review_unusual_pkg_title": "Confezione insolita", + "banner_review_unusual_pkg_detail": "Hai impostato una confezione da {qty} {unit} — la dimensione sembra molto alta. Controlla se è corretta o modifica.", + "banner_review_low_qty_title": "Quantità molto bassa", + "banner_review_low_qty_detail": "Hai solo {qty} in inventario — sembra poco, potrebbe essere un errore. Conferma se è corretto.", + "banner_review_high_qty_title": "Quantità insolitamente alta", + "banner_review_high_qty_detail": "Hai {qty} in inventario — la cifra sembra molto alta. Conferma se è corretto o correggi.", + "banner_prediction_rate_day": "Media ~{n} {unit}/giorno", + "banner_prediction_rate_week": "Media ~{n} {unit}/settimana", + "banner_prediction_days_ago": "{n} giorni fa hai rifornito", + "banner_prediction_more": "mi aspettavo {expected} {unit}{time}, ne hai invece {actual} {unit}. Hai aggiunto scorte senza registrarle?", + "banner_prediction_less": "mi aspettavo {expected} {unit}{time}, ne hai solo {actual} {unit}. Hai consumato di più del solito?", + "banner_finished_zero": "L'inventario segna zero, ma i movimenti registrati dicono che non dovrebbe essere finito.", + "banner_finished_expected": "Secondo le registrazioni dovresti averne ancora {qty} {unit}.", + "banner_finished_check": "Puoi controllare?", + "banner_anomaly_phantom_title": "hai più scorte del previsto", + "banner_anomaly_phantom_detail": "L'inventario segna {inv_qty} {unit}, ma in base alle registrazioni ne dovresti avere solo {expected_qty} {unit}. Hai aggiunto scorte senza registrarle?", + "banner_anomaly_untracked_title": "scorte non registrate come entrata", + "banner_anomaly_untracked_detail": "Hai {inv_qty} {unit} in inventario, ma le uscite registrate superano le entrate — le scorte iniziali probabilmente non sono mai state aggiunte come entrata. Puoi correggere la quantità o registrare le entrate mancanti.", + "banner_anomaly_ghost_title": "hai meno scorte del previsto", + "banner_anomaly_ghost_detail": "In base alle operazioni registrate dovresti avere {expected_qty} {unit} di {name}, ma l'inventario mostra solo {inv_qty} {unit}. Hai prelevato senza registrarlo?", + "consumed": "Consumati: {n} ({pct}%)", + "wasted": "Buttati: {n} ({pct}%)", + "more_opened": "e altri {n} prodotti aperti...", + "banner_expired_detail": "{when} · hai ancora {qty}.", + "banner_explain_title": "Chiedi a Gemini una spiegazione", + "banner_explain_btn": "Spiega", + "banner_analyzing": "🤖 Analizzo…" + }, + "inventory": { + "title": "Dispensa", + "filter_all": "Tutti", + "search_placeholder": "🔍 Cerca prodotto...", + "recent_title": "🕐 Ultimi usati", + "popular_title": "⭐ Più usati", + "empty": "Nessun prodotto qui.\nScansiona un prodotto per aggiungerlo!", + "no_items_found": "Nessuna voce di inventario trovata", + "qty_remainder_suffix": "rimasti", + "vacuum_badge": "🫙 Sotto vuoto", + "opened_badge": "📭 Aperto", + "label_expiry": "📅 Scadenza", + "label_storage": "🫙 Conservazione", + "label_status": "📭 Stato", + "opened_since": "Aperto dal {date}", + "label_position": "📍 Posizione", + "label_quantity": "📦 Quantità", + "label_added": "📅 Aggiunto", + "empty_text": "Nessun prodotto qui.
Scansiona un prodotto per aggiungerlo!", + "empty_db": "Nessun prodotto nel database.
Scansiona un prodotto per iniziare!", + "qty_trace": "< 1" + }, + "scan": { + "title": "Scansiona Prodotto", + "mode_shopping": "🛒 Modalità Spesa", + "mode_shopping_end": "✅ Fine spesa", + "zoom": "Zoom", + "barcode_placeholder": "Inserisci codice a barre...", + "quick_name_divider": "oppure scrivi il nome", + "quick_name_placeholder": "Es: Mele, Zucchine, Pane...", + "manual_entry": "✏️ Inserimento Manuale", + "ai_identify": "🤖 Identifica con AI", + "hint": "Scansiona il barcode, scrivi il nome del prodotto, oppure usa l'AI per identificarlo", + "debug_toggle": "🐛 Debug Log", + "barcode_acquired": "🔖 Barcode acquisito: {code}", + "scan_barcode": "🔖 Scansiona Barcode", + "create_named": "Crea {name}", + "new_without_barcode": "Nuovo prodotto senza barcode" + }, + "action": { + "title": "Cosa vuoi fare?", + "add_btn": "📥 AGGIUNGI", + "add_sub": "in dispensa/frigo", + "use_btn": "📤 USA / CONSUMA", + "use_sub": "dalla dispensa/frigo", + "have_title": "📦 Ce l'hai già!", + "add_more_sub": "altra quantità", + "use_qty_sub": "quanto ne hai usato", + "throw_btn": "🗑️ BUTTA", + "throw_sub": "butta il prodotto", + "edit_sub": "scadenza, luogo…", + "create_recipe_btn": "Crea una ricetta con questo" + }, + "add": { + "title": "Aggiungi alla Dispensa", + "location_label": "📍 Dove lo metti?", + "quantity_label": "📦 Quantità", + "conf_size_label": "📦 Ogni confezione contiene:", + "conf_size_placeholder": "es. 300", + "vacuum_label": "🫙 Sotto vuoto", + "vacuum_hint": "La scadenza verrà estesa automaticamente", + "submit": "✅ Aggiungi", + "purchase_type_label": "🛒 Questo prodotto è...", + "new_btn": "🆕 Appena comprato", + "existing_btn": "📦 Ce l'avevo già", + "remaining_label": "📦 Quantità rimasta", + "remaining_hint": "Quanto è rimasto approssimativamente?", + "remaining_full": "🟢 Pieno", + "remaining_half": "🟠 Metà", + "estimated_expiry": "Scadenza stimata:", + "suffix_freezer": "(freezer)", + "suffix_vacuum": "(sotto vuoto)", + "hint_modify": "📝 Puoi modificare la data o scansionarla con la fotocamera", + "scan_expiry_title": "📷 Scansiona Data Scadenza", + "product_added": "✅ {name} aggiunto!{qty}", + "suffix_freezer_vacuum": "(freezer + sotto vuoto)", + "history_badge_tip": "Media da {n} inserimenti precedenti" + }, + "use": { + "title": "Usa / Consuma", + "location_label": "📍 Da dove?", + "quantity_label": "Quanto hai usato?", + "change": "cambia", + "partial_hint": "Oppure specifica la quantità usata:", + "partial_piece_hint": "Hai usato solo una parte?", + "piece": "pezzo", + "one_whole": "1 intero", + "use_all": "🗑️ Usato TUTTO / Finito", + "submit": "📤 Usa questa quantità", + "available": "📦 Disponibile:", + "opened_badge": "APERTO", + "not_in_inventory": "⚠️ Prodotto non presente nell'inventario.", + "expiry_warning": "⚠️ Usa prima quella{loc} che scade il {date} — {when}!", + "throw_title": "🗑️ Butta Prodotto", + "throw_all": "🗑️ Butta TUTTO ({qty})", + "throw_qty_label": "Quanto butti?", + "throw_qty_hint": "oppure specifica la quantità:", + "throw_partial_btn": "🗑️ Butta questa quantità", + "when_expired": "scaduta da {n} giorni", + "when_today": "scade oggi", + "when_tomorrow": "scade domani", + "when_days": "scade tra {n} giorni", + "toast_used": "📤 Usato {qty} di {name}", + "toast_bring": "🛒 Prodotto finito → aggiunto a Bring!", + "toast_opened_finished": "🔓 Confezione aperta di {name} finita!", + "disambiguation_hint": "Cosa intendi con \"finito tutto\"?", + "disambiguation_all": "🗑️ Finito TUTTO ({qty})", + "error_exceeds_stock": "⚠️ Non puoi usare più di quanto hai disponibile!", + "use_all_confirm_title": "✅ Finisci tutto", + "use_all_confirm_msg": "Conferma che hai finito tutto il prodotto:", + "use_all_confirm_btn": "✅ Sì, finito", + "throw_all_confirm_title": "🗑️ Butta tutto", + "throw_all_confirm_msg": "Vuoi davvero buttare via tutto il prodotto?", + "throw_all_confirm_btn": "🗑️ Sì, butta" + }, + "product": { + "title_new": "Nuovo Prodotto", + "title_edit": "Modifica Prodotto", + "ai_fill": "📷 Scatta foto e identifica con AI", + "ai_fill_hint": "L'AI compilerà automaticamente i campi del prodotto", + "name_label": "🏷️ Nome Prodotto *", + "name_placeholder": "Es: Latte intero, Pasta penne rigate...", + "brand_label": "🏢 Marca", + "brand_placeholder": "Es: Barilla, Granarolo, Mutti...", + "category_label": "📂 Categoria", + "unit_label": "📏 Unità di misura", + "default_qty_label": "🔢 Quantità default", + "conf_size_label": "📦 Ogni confezione contiene:", + "conf_size_placeholder": "es. 300", + "notes_label": "📝 Note", + "notes_placeholder": "Es: senza lattosio, bio, conservare in frigo dopo apertura...", + "barcode_label": "🔖 Barcode", + "barcode_placeholder": "Codice a barre (se disponibile)", + "barcode_hint": "⚠️ Aggiungi il barcode così al prossimo acquisto basta scansionarlo!", + "submit": "💾 Salva Prodotto", + "name_required": "Inserisci il nome del prodotto", + "conf_size_required": "Specifica il contenuto di ogni confezione", + "expiry_estimated": "Scadenza stimata:", + "scan_expiry": "Scansiona data scadenza", + "expiry_hint": "📝 Puoi modificare la data o scansionarla con la fotocamera", + "add_batch": "📦 + Lotto con scadenza diversa", + "package_info": "📦 Confezione: {info}", + "edit_catalog": "⚙️ Modifica scheda prodotto (nome, marca, categoria…)", + "not_recognized": "⚠️ Prodotto non riconosciuto", + "edit_info": "✏️ Modifica informazioni", + "modify_details": "MODIFICA\nscadenza, luogo…", + "already_in_pantry": "📋 Già in dispensa", + "no_barcode": "Senza barcode" + }, + "products": { + "title": "📦 Tutti i Prodotti", + "search_placeholder": "🔍 Cerca prodotto...", + "empty": "Nessun prodotto nel database.\nScansiona un prodotto per iniziare!", + "no_category": "Nessun prodotto in questa categoria" + }, + "recipes": { + "title": "🍳 Ricette", + "generate": "✨ Genera nuova ricetta", + "archive_empty": "Nessuna ricetta salvata. Genera la tua prima ricetta!", + "dialog_title": "🍳 Ricetta", + "dialog_desc": "Genero una ricetta sana con gli ingredienti in dispensa, dando priorità a quelli in scadenza.", + "meal_label": "🕐 Per quale pasto?", + "persons_label": "👥 Quante persone?", + "meal_type_label": "🎯 Tipo di pasto", + "opt_fast": "⚡ Pasto Veloce", + "opt_light": "🥗 Poca Fame", + "opt_expiry": "⏰ Priorità Scadenze", + "opt_healthy": "💚 Extra Salutare", + "opt_opened": "📦 Priorità Cose Aperte", + "opt_zero_waste": "♻️ Zero Sprechi", + "generate_btn": "✨ Genera Ricetta", + "loading_msg": "Sto preparando la ricetta...", + "start_cooking": "👨‍🍳 Modalità Cucina", + "regenerate": "🔄 Generane un'altra", + "close_btn": "✅ Chiudi", + "ingredients_title": "🧾 Ingredienti", + "steps_title": "👨‍🍳 Procedimento", + "no_steps": "Nessun procedimento disponibile", + "generate_error": "Errore nella generazione", + "persons_short": "pers.", + "use_ingredient_title": "Usa ingrediente", + "recipe_qty_label": "Ricetta", + "from_where_label": "Da dove?", + "amount_label": "Quanto", + "use_amount_btn": "Usa questa quantità", + "use_all_btn": "Usa TUTTO / Finito", + "packs_label": "Confezioni", + "quantity_in_total": "Quantità in {unit} (totale: {total})", + "packs_of_have": "Confezioni da {size} (hai {count} conf)", + "scale_wait_stable": "Attendi 10s di stabilità per la compilazione automatica…", + "ingredient_scaled_toast": "📦 Ingrediente scalato dalla dispensa!", + "finished_added_bring_toast": "🛒 Prodotto finito → aggiunto a Bring!", + "load_error": "Errore nel caricamento" + }, + "shopping": { + "title": "🛒 Lista della Spesa", + "bring_loading": "Connessione a Bring!...", + "bring_not_configured": "Bring! non è configurato. Aggiungi email e password nelle impostazioni.", + "tab_to_buy": "🛍️ Da comprare", + "tab_forecast": "🧠 In previsione", + "total_label": "💰 Totale stimato", + "section_to_buy": "🛍️ Da comprare", + "suggestions_title": "💡 Suggerimenti AI", + "suggestions_add": "✅ Aggiungi selezionati a Bring!", + "search_prices": "🔍 Cerca tutti i prezzi", + "suggest_btn": "Suggerisci cosa comprare", + "smart_title": "🧠 Previsioni intelligenti", + "smart_empty": "Nessuna previsione disponibile.
Aggiungi prodotti alla dispensa per ricevere previsioni intelligenti.", + "smart_filter_all": "Tutti", + "smart_filter_critical": "🔴 Urgenti", + "smart_filter_high": "🟠 Presto", + "smart_filter_medium": "🟡 Pianifica", + "smart_filter_low": "🟢 Previsione", + "smart_add": "🛒 Aggiungi selezionati a Bring!", + "empty": "Lista della spesa vuota!\nUsa il pulsante sotto per generare suggerimenti.", + "already_in_list": "🛒 \"{name}\" già nella lista della spesa", + "already_in_list_short": "ℹ️ Già nella lista della spesa", + "add_prompt": "Vuoi aggiungerlo alla lista della spesa?", + "smart_already": "📊 La spesa intelligente prevede già {name}", + "all_searched": "Tutti i prodotti sono già stati cercati. Usa 🔄 per ricercare singoli.", + "search_complete": "Ricerca completata: {count} prodotti", + "removed_sufficient": "🧹 {removed} prodotto/i con scorte sufficienti rimosso/i dalla lista", + "suggest_buy": "🛒 Compra: {qty} {unit}", + "suggest_buy_approx": "🛒 Almeno: {qty} {unit}", + "suggest_buy_tip": "Quantità suggerita in base al consumo degli ultimi 14 giorni", + "suggest_buy_approx_tip": "Stima minima basata sul consumo (compra la confezione più vicina)", + "bring_badge": "🛒 Già su Bring!", + "add_urgent_toast": "🔴 {n} prodotto/i urgente/i aggiunto/i automaticamente a Bring!", + "migration_done": "✅ {migrated} aggiornati, {skipped} già ok", + "added_to_bring": "🛒 {n} prodotti aggiunti a Bring!", + "added_to_bring_skip": "{n} già presenti", + "all_on_bring": "Tutti i prodotti erano già su Bring!", + "freq_high": "📈 Uso frequente", + "freq_regular": "📊 Uso regolare", + "freq_occasional": "📉 Uso occasionale", + "out_of_stock": "Esaurito", + "scan_toast": "📷 Scansiona: {name}", + "empty_category": "Nessun prodotto in questa categoria", + "session_empty": "🛒 Nessun prodotto ancora", + "urgency_critical": "Urgente", + "urgency_high": "Presto", + "urgency_medium": "Pianifica", + "urgency_low": "Previsione", + "urgency_medium_short": "Medio", + "urgency_low_short": "Ok", + "tag_urgent": "🔴 Urgente", + "tag_priority": "⭐ Priorità", + "tag_check": "✅ Verificare", + "smart_already_predicted": "📊 La spesa intelligente prevede già {name}{urgency}.", + "item_removed": "✅ {name} rimosso dalla lista!", + "urgency_spec_critical": "⚡ Urgente", + "urgency_spec_high": "🟠 Presto", + "bring_add_n": "Aggiungi {n} a Bring!", + "bring_add_selected": "Aggiungi selezionati a Bring!", + "bring_adding": "Aggiunta in corso...", + "bring_added_one": "1 prodotto aggiunto a Bring!", + "bring_added_many": "{n} prodotti aggiunti a Bring!", + "bring_skipped": "({n} già in lista)", + "force_sync": "Forza sincronizzazione Bring!", + "scan_target_label": "Stai cercando", + "scan_target_found": "Trovato! Rimuovi dalla lista", + "bring_add_one": "Aggiungi 1 prodotto a Bring!", + "bring_add_many": "Aggiungi {n} prodotti a Bring!", + "syncing": "Sincronizzazione…", + "sync_done": "Sincronizzazione completata", + "price_searching": "Cerco...", + "search_action": "Ricerca", + "open_action": "Apri", + "not_found": "Non trovato", + "search_price": "Cerca prezzo", + "tap_to_scan": "Tocca per scansionare", + "tag_title": "Tag", + "remove_title": "Rimuovi", + "found_count": "{found}/{total} prodotti trovati", + "savings_offers": "· 🏷️ Risparmi €{amount} con le offerte", + "searching_progress": "Cerco {current}/{total}...", + "remove_error": "Errore nella rimozione", + "btn_fetch_prices": "Cerca i prezzi", + "price_total_label": "💰 Spesa stimata:", + "price_loading": "Ricerca prezzi…", + "price_not_found": "prezzo n/d", + "suggest_loading": "Analisi in corso...", + "suggest_error": "Errore nella generazione", + "priority_high": "Alta", + "priority_medium": "Media", + "priority_low": "Bassa", + "smart_last_update": "Aggiornato {time}", + "names_already_updated": "Tutti i nomi sono già aggiornati" + }, + "ai": { + "title": "🤖 Identificazione AI", + "capture": "📸 Scatta Foto", + "retake": "🔄 Riscatta", + "hint": "Scatta una foto del prodotto e l'AI cercherà di identificarlo", + "identifying": "🤖 Identifico il prodotto...", + "no_api_key": "⚠️ Chiave API Gemini non configurata.\nAggiungi GEMINI_API_KEY nel file .env sul server.", + "fields_filled": "✅ Campi compilati dall'AI", + "use_data": "✅ Usa dati AI", + "use_data_no_barcode": "✅ Usa dati AI (senza barcode)" + }, + "log": { + "title": "📒 Storico", + "type_added": "Aggiunto", + "type_waste": "Buttato", + "type_used": "Usato", + "type_bring": "Aggiunto a Bring!", + "undone_badge": "Annullato", + "undo_title": "Annulla questa operazione", + "load_error": "Errore nel caricamento log", + "empty": "Nessuna operazione registrata.", + "undo_action_remove": "rimozione di", + "undo_action_restore": "ripristino di", + "undo_confirm": "Annullare questa operazione?\n→ {action} {name}", + "undo_success": "↩ Operazione annullata per {name}", + "already_undone": "Operazione già annullata", + "too_old": "Non è possibile annullare operazioni più vecchie di 24 ore", + "undo_error": "Errore durante l'annullamento" + }, + "chat": { + "title": "Gemini Chef", + "welcome": "Ciao! Sono il tuo assistente cucina", + "welcome_desc": "Chiedimi di prepararti un succo, uno spuntino, un piatto veloce... Conosco la tua dispensa, i tuoi elettrodomestici e le tue preferenze!", + "suggestion_snack": "🍿 Spuntino veloce", + "suggestion_juice": "🥤 Succo/Frullato", + "suggestion_light": "🥗 Qualcosa di leggero", + "suggestion_expiry": "⏰ Usa le scadenze", + "clear": "Nuova conversazione", + "placeholder": "Chiedi qualcosa...", + "cleared": "Chat cancellata", + "suggestion_snack_text": "Cosa posso preparare per uno spuntino veloce?", + "suggestion_juice_text": "Fammi un succo o frullato con quello che ho", + "suggestion_light_text": "Ho fame ma voglio qualcosa di leggero", + "suggestion_expiry_text": "Cosa sta per scadere e come posso usarlo?", + "transfer_to_recipes": "Trasferisci a Ricette", + "transferring": "Trasferimento in corso...", + "transferred": "Aggiunta alle Ricette!", + "open_recipe": "Apri la ricetta" + }, + "cooking": { + "close": "Chiudi", + "tts_btn": "Leggi ad alta voce", + "restart": "↺ Ricomincia", + "replay": "🔊 Rileggi", + "timer": "⏱️ {time} · Timer", + "prev": "◀ Precedente", + "next": "Successivo ▶", + "ingredient_used": "✔️ Scalato", + "ingredient_use_btn": "📦 Usa", + "ingredient_deduct_title": "Scala dalla dispensa", + "timer_expired_tts": "Timer {label} scaduto!", + "timer_warning_tts": "Attenzione! {label}: mancano 10 secondi!", + "recipe_done_tts": "Ricetta completata! Buon appetito!", + "expires_chip": "scade {date}", + "finish": "✅ Fine" + }, + "settings": { + "title": "⚙️ Configurazione", + "tab_api": "API Keys", + "tab_bring": "Bring!", + "tab_recipe": "Ricette", + "tab_mealplan": "Piano Settimanale", + "tab_appliances": "Elettrodomestici", + "tab_spesa": "Spesa Online", + "tab_camera": "Fotocamera", + "tab_security": "Sicurezza", + "tab_tts": "Voce (TTS)", + "tab_language": "Lingua", + "tab_scale": "Bilancia Smart", + "gemini": { + "title": "🤖 Google Gemini AI", + "hint": "Chiave API per identificazione prodotti, scadenze e ricette.", + "key_label": "API Key Gemini" }, - "nav": { - "title": "EverShelf", - "home": "Home", - "inventory": "Dispensa", - "recipes": "Ricette", - "shopping": "Spesa", - "log": "Storico" + "bring": { + "title": "🛒 Bring! Shopping List", + "hint": "Credenziali per l'integrazione con la lista della spesa Bring!", + "email_label": "📧 Email Bring!", + "password_label": "🔒 Password Bring!" }, - "btn": { - "back": "← Indietro", - "save": "💾 Salva", - "cancel": "✕ Annulla", - "close": "Chiudi", - "add": "✅ Aggiungi", - "delete": "Elimina", - "edit": "✏️ Modifica", - "search": "🔍 Cerca", - "go": "✅ Vai", - "toggle_password": "👁️ Mostra/Nascondi", - "load_more": "Carica altri...", - "save_config": "💾 Salva Configurazione", - "save_product": "💾 Salva Prodotto", - "restart": "↺ Ricomincia", - "reset_default": "↺ Ripristina default", - "save_info": "💾 Salva informazioni", - "retry": "🔄 Riprova" + "price": { + "title": "💰 Stima Prezzi (AI)", + "hint": "Mostra il costo stimato di ogni prodotto nella lista della spesa usando l'AI.", + "enabled_label": "Attiva stima prezzi", + "country_label": "🌍 Paese di riferimento", + "currency_label": "💱 Valuta", + "update_label": "🔄 Aggiorna prezzi ogni", + "update_suffix": "mesi" }, - "locations": { - "dispensa": "Dispensa", - "frigo": "Frigo", - "freezer": "Freezer", - "altro": "Altro" + "recipe": { + "title": "🍳 Preferenze Ricette", + "hint": "Configura le opzioni predefinite per la generazione delle ricette.", + "persons_label": "👥 Persone predefinite", + "options_label": "🎯 Opzioni ricetta predefinite", + "fast": "⚡ Pasto Veloce", + "light": "🥗 Poca Fame", + "expiry": "⏰ Priorità Scadenze", + "healthy": "💚 Extra Salutare", + "opened": "📦 Priorità Cose Aperte", + "zerowaste": "♻️ Zero Sprechi", + "dietary_label": "🚫 Intolleranze / Restrizioni", + "dietary_placeholder": "Es: senza glutine, senza lattosio, vegetariano..." }, - "categories": { - "latticini": "Latticini", - "carne": "Carne", - "pesce": "Pesce", - "frutta": "Frutta", - "verdura": "Verdura", - "pasta": "Pasta & Riso", - "pane": "Pane & Forno", - "surgelati": "Surgelati", - "bevande": "Bevande", - "condimenti": "Condimenti", - "snack": "Snack & Dolci", - "conserve": "Conserve", - "cereali": "Cereali & Legumi", - "igiene": "Igiene", - "pulizia": "Pulizia Casa", - "altro": "Altro", - "select": "-- Seleziona --" + "mealplan": { + "title": "📅 Piano Pasti Settimanale", + "hint": "Imposta la tipologia di pasto per ogni giorno. Sarà usata come guida nella generazione delle ricette.", + "enabled": "✅ Attiva piano pasti settimanale", + "legend": "🌤️ = Pranzo · 🌙 = Cena · Tocca un badge per cambiarlo.", + "types_title": "📋 Tipologie disponibili" }, - "units": { - "pz": "pz", - "conf": "conf", - "g": "g", - "ml": "ml", - "pieces": "Pezzi", - "grams": "Grammi", - "box": "Confezione", - "boxes": "Confezioni" + "appliances": { + "title": "🔌 Elettrodomestici Disponibili", + "hint": "Indica gli elettrodomestici che hai a disposizione. Saranno considerati nella generazione delle ricette.", + "new_placeholder": "Es: Macchina del pane, Bimby, Friggitrice ad aria...", + "quick_title": "Aggiungi velocemente:", + "oven": "🔥 Forno", + "microwave": "📡 Microonde", + "air_fryer": "🍟 Friggitrice ad aria", + "bread_maker": "🍞 Macchina pane", + "bimby": "🤖 Bimby/Cookeo", + "mixer": "🌀 Planetaria", + "steamer": "♨️ Vaporiera", + "pressure_cooker": "🫕 Pentola pressione", + "toaster": "🍞 Tostapane", + "blender": "🍹 Frullatore", + "empty": "Nessun elettrodomestico aggiunto" }, - "shopping_sections": { - "frutta_verdura": "Frutta & Verdura", - "carne_pesce": "Carne & Pesce", - "latticini": "Latticini & Fresco", - "pane_dolci": "Pane & Dolci", - "pasta": "Pasta & Cereali", - "conserve": "Conserve & Salse", - "surgelati": "Surgelati", - "bevande": "Bevande", - "pulizia_igiene": "Pulizia & Igiene", - "altro": "Altro" + "spesa": { + "title": "🛍️ Spesa Online", + "hint": "Configura il provider per la spesa online.", + "provider_label": "🏪 Provider", + "email_label": "📧 Email", + "password_label": "🔒 Password", + "login_btn": "🔐 Accedi", + "ai_prompt_label": "🤖 Prompt AI selezione prodotto", + "ai_prompt_placeholder": "Istruzioni per l'AI quando deve scegliere tra più prodotti...", + "ai_prompt_hint": "L'AI usa questo prompt per scegliere il prodotto più appropriato tra i risultati. Lascia vuoto per il comportamento predefinito.", + "configure_first": "Configura prima la Spesa Online nelle impostazioni", + "missing_credentials": "Inserisci email e password", + "login_in_progress": "Accesso in corso...", + "login_error_prefix": "Errore:", + "login_network_error_prefix": "Errore di rete:", + "login_success_default": "Login effettuato!", + "result_name_label": "Nome", + "result_card_label": "Tessera", + "result_pickup_label": "Punto Ritiro", + "result_points_label": "Punti Fedeltà", + "connected_relogin": "✅ Connesso — Riaccedi", + "connected_as": "Connesso come {name}" }, - "dashboard": { - "expired_title": "🚫 Scaduti", - "expiring_title": "⏰ Prossime Scadenze", - "stats_period": "📊 Ultimi 30 giorni", - "opened_title": "📦 Prodotti Aperti", - "review_title": "🔍 Da revisionare", - "review_hint": "Quantità che sembrano anomale. Conferma se corrette o modifica.", - "quick_recipe": "Ricetta veloce con prodotti in scadenza", - "banner_review_title": "Quantità anomala", - "banner_review_action_ok": "È corretto", - "banner_review_action_finish": "🗑️ È finito tutto", - "banner_review_action_edit": "Correggi", - "banner_review_action_weigh": "Pesa", - "banner_review_dismiss": "Ignora", - "banner_prediction_title": "Consumo anomalo", - "banner_prediction_hint": "Secondo le previsioni, questa quantità non corrisponde al consumo previsto.", - "banner_prediction_action_confirm": "Confermo la quantità di {qty} {unit}", - "banner_prediction_action_weigh": "Pesa ora", - "banner_prediction_action_edit": "Aggiorna quantità", - "banner_expired_title": "Prodotto scaduto", - "banner_expired_today": "Scaduto oggi", - "banner_expired_days": "Scaduto da {days} giorni", - "banner_expired_action_use": "Usa comunque", - "banner_expired_action_throw": "L'ho buttato", - "banner_expired_action_edit": "Correggi data", - "banner_anomaly_action_edit": "Correggi inventario", - "banner_anomaly_action_dismiss": "La quantità è giusta", - "banner_no_expiry_title": "Scadenza mancante: {name}", - "banner_no_expiry_detail": "Questo prodotto non ha una data di scadenza. Vuoi aggiungerla o confermare che non scade?", - "banner_no_expiry_action_set": "Imposta scadenza", - "banner_no_expiry_action_dismiss": "Non scade ✓", - "banner_no_expiry_toast_dismissed": "Segnato come 'non scade'", - "banner_expiring_title": "In scadenza", - "banner_expiring_today": "Scade oggi!", - "banner_expiring_tomorrow": "Scade domani", - "banner_expiring_days": "Scade tra {days} giorni", - "banner_expiring_action_use": "Usa ora", - "banner_finished_title": "è finito?", - "banner_finished_detail": "Ho registrato che {name} ha toccato quota zero. È davvero finito o hai ancora delle scorte?", - "banner_finished_action_yes": "Sì, è finito", - "banner_finished_action_no": "No, ne ho ancora", - "banner_review_unusual_pkg_title": "Confezione insolita", - "banner_review_unusual_pkg_detail": "Hai impostato una confezione da {qty} {unit} — la dimensione sembra molto alta. Controlla se è corretta o modifica.", - "banner_review_low_qty_title": "Quantità molto bassa", - "banner_review_low_qty_detail": "Hai solo {qty} in inventario — sembra poco, potrebbe essere un errore. Conferma se è corretto.", - "banner_review_high_qty_title": "Quantità insolitamente alta", - "banner_review_high_qty_detail": "Hai {qty} in inventario — la cifra sembra molto alta. Conferma se è corretto o correggi.", - "banner_prediction_rate_day": "Media ~{n} {unit}/giorno", - "banner_prediction_rate_week": "Media ~{n} {unit}/settimana", - "banner_prediction_days_ago": "{n} giorni fa hai rifornito", - "banner_prediction_more": "mi aspettavo {expected} {unit}{time}, ne hai invece {actual} {unit}. Hai aggiunto scorte senza registrarle?", - "banner_prediction_less": "mi aspettavo {expected} {unit}{time}, ne hai solo {actual} {unit}. Hai consumato di più del solito?", - "banner_finished_zero": "L'inventario segna zero, ma i movimenti registrati dicono che non dovrebbe essere finito.", - "banner_finished_expected": "Secondo le registrazioni dovresti averne ancora {qty} {unit}.", - "banner_finished_check": "Puoi controllare?", - "banner_anomaly_phantom_title": "hai più scorte del previsto", - "banner_anomaly_phantom_detail": "L'inventario segna {inv_qty} {unit}, ma in base alle registrazioni ne dovresti avere solo {expected_qty} {unit}. Hai aggiunto scorte senza registrarle?", - "banner_anomaly_untracked_title": "scorte non registrate come entrata", - "banner_anomaly_untracked_detail": "Hai {inv_qty} {unit} in inventario, ma le uscite registrate superano le entrate — le scorte iniziali probabilmente non sono mai state aggiunte come entrata. Puoi correggere la quantità o registrare le entrate mancanti.", - "banner_anomaly_ghost_title": "hai meno scorte del previsto", - "banner_anomaly_ghost_detail": "In base alle operazioni registrate dovresti avere {expected_qty} {unit} di {name}, ma l'inventario mostra solo {inv_qty} {unit}. Hai prelevato senza registrarlo?", - "consumed": "Consumati: {n} ({pct}%)", - "wasted": "Buttati: {n} ({pct}%)", - "more_opened": "e altri {n} prodotti aperti...", - "banner_expired_detail": "{when} · hai ancora {qty}." + "camera": { + "title": "📷 Fotocamera", + "hint": "Scegli quale fotocamera utilizzare per la scansione barcode e l'identificazione AI.", + "device_label": "📸 Fotocamera predefinita", + "back": "📱 Posteriore (default)", + "front": "🤳 Anteriore", + "devices_hint": "Se hai più fotocamere, puoi selezionarne una specifica dall'elenco sopra dopo aver concesso i permessi.", + "detect_btn": "🔄 Rileva fotocamere" }, - "inventory": { - "title": "Dispensa", - "filter_all": "Tutti", - "search_placeholder": "🔍 Cerca prodotto...", - "recent_title": "🕐 Ultimi usati", - "popular_title": "⭐ Più usati", - "empty": "Nessun prodotto qui.\nScansiona un prodotto per aggiungerlo!", - "no_items_found": "Nessuna voce di inventario trovata", - "qty_remainder_suffix": "rimasti", - "vacuum_badge": "🫙 Sotto vuoto", - "opened_badge": "📭 Aperto", - "label_expiry": "📅 Scadenza", - "label_storage": "🫙 Conservazione", - "label_status": "📭 Stato", - "opened_since": "Aperto dal {date}", - "label_position": "📍 Posizione", - "label_quantity": "📦 Quantità", - "label_added": "📅 Aggiunto", - "empty_text": "Nessun prodotto qui.
Scansiona un prodotto per aggiungerlo!", - "empty_db": "Nessun prodotto nel database.
Scansiona un prodotto per iniziare!", - "qty_trace": "< 1" + "security": { + "title": "🔒 Certificato HTTPS", + "hint": "Se il browser mostra l'errore \"La connessione non è privata\" (ERR_CERT_AUTHORITY_INVALID), devi installare il certificato CA nel dispositivo.", + "download_btn": "📥 Scarica Certificato CA" }, - "scan": { - "title": "Scansiona Prodotto", - "mode_shopping": "🛒 Modalità Spesa", - "mode_shopping_end": "✅ Fine spesa", - "zoom": "Zoom", - "barcode_placeholder": "Inserisci codice a barre...", - "quick_name_divider": "oppure scrivi il nome", - "quick_name_placeholder": "Es: Mele, Zucchine, Pane...", - "manual_entry": "✏️ Inserimento Manuale", - "ai_identify": "🤖 Identifica con AI", - "hint": "Scansiona il barcode, scrivi il nome del prodotto, oppure usa l'AI per identificarlo", - "debug_toggle": "🐛 Debug Log", - "barcode_acquired": "🔖 Barcode acquisito: {code}", - "scan_barcode": "🔖 Scansiona Barcode", - "create_named": "Crea {name}", - "new_without_barcode": "Nuovo prodotto senza barcode" + "tts": { + "title": "🔊 Voce & TTS", + "hint": "Configura la sintesi vocale tramite qualsiasi API REST esterna. I passi della ricetta e i timer scaduti verranno inviati all'endpoint configurato.", + "enabled": "✅ Attiva TTS", + "url_label": "🌐 URL Endpoint", + "method_label": "📡 Metodo HTTP", + "auth_label": "🔐 Autenticazione", + "auth_bearer": "Bearer Token", + "auth_custom": "Header personalizzato", + "auth_none": "Nessuna", + "token_label": "🔑 Bearer Token", + "custom_header_name": "📋 Nome header", + "custom_header_value": "📋 Valore header", + "content_type_label": "📄 Content-Type", + "payload_key_label": "🗝️ Campo testo nel payload", + "payload_key_hint": "Nome del campo JSON che conterrà il testo da leggere (es: message, text).", + "extra_fields_label": "➕ Campi extra (JSON)", + "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}", + "extra_fields_hint": "Campi aggiuntivi da includere nel payload, in formato JSON. Lascia vuoto se non necessario.", + "test_btn": "🔊 Invia Test Vocale" }, - "action": { - "title": "Cosa vuoi fare?", - "add_btn": "📥 AGGIUNGI", - "add_sub": "in dispensa/frigo", - "use_btn": "📤 USA / CONSUMA", - "use_sub": "dalla dispensa/frigo", - "have_title": "📦 Ce l'hai già!", - "add_more_sub": "altra quantità", - "use_qty_sub": "quanto ne hai usato", - "throw_btn": "🗑️ BUTTA", - "throw_sub": "butta il prodotto", - "edit_sub": "scadenza, luogo…", - "create_recipe_btn": "Crea una ricetta con questo" - }, - "add": { - "title": "Aggiungi alla Dispensa", - "location_label": "📍 Dove lo metti?", - "quantity_label": "📦 Quantità", - "conf_size_label": "📦 Ogni confezione contiene:", - "conf_size_placeholder": "es. 300", - "vacuum_label": "🫙 Sotto vuoto", - "vacuum_hint": "La scadenza verrà estesa automaticamente", - "submit": "✅ Aggiungi", - "purchase_type_label": "🛒 Questo prodotto è...", - "new_btn": "🆕 Appena comprato", - "existing_btn": "📦 Ce l'avevo già", - "remaining_label": "📦 Quantità rimasta", - "remaining_hint": "Quanto è rimasto approssimativamente?", - "remaining_full": "🟢 Pieno", - "remaining_half": "🟠 Metà", - "estimated_expiry": "Scadenza stimata:", - "suffix_freezer": "(freezer)", - "suffix_vacuum": "(sotto vuoto)", - "hint_modify": "📝 Puoi modificare la data o scansionarla con la fotocamera", - "scan_expiry_title": "📷 Scansiona Data Scadenza", - "product_added": "✅ {name} aggiunto!{qty}", - "suffix_freezer_vacuum": "(freezer + sotto vuoto)" - }, - "use": { - "title": "Usa / Consuma", - "location_label": "📍 Da dove?", - "quantity_label": "Quanto hai usato?", - "change": "cambia", - "partial_hint": "Oppure specifica la quantità usata:", - "partial_piece_hint": "Hai usato solo una parte?", - "piece": "pezzo", - "one_whole": "1 intero", - "use_all": "🗑️ Usato TUTTO / Finito", - "submit": "📤 Usa questa quantità", - "available": "📦 Disponibile:", - "opened_badge": "APERTO", - "not_in_inventory": "⚠️ Prodotto non presente nell'inventario.", - "expiry_warning": "⚠️ Usa prima quella{loc} che scade il {date} — {when}!", - "throw_title": "🗑️ Butta Prodotto", - "throw_all": "🗑️ Butta TUTTO ({qty})", - "throw_qty_label": "Quanto butti?", - "throw_qty_hint": "oppure specifica la quantità:", - "throw_partial_btn": "🗑️ Butta questa quantità", - "when_expired": "scaduta da {n} giorni", - "when_today": "scade oggi", - "when_tomorrow": "scade domani", - "when_days": "scade tra {n} giorni", - "toast_used": "📤 Usato {qty} di {name}", - "toast_bring": "🛒 Prodotto finito → aggiunto a Bring!", - "toast_opened_finished": "🔓 Confezione aperta di {name} finita!", - "disambiguation_hint": "Cosa intendi con \"finito tutto\"?", - "disambiguation_all": "🗑️ Finito TUTTO ({qty})", - "error_exceeds_stock": "⚠️ Non puoi usare più di quanto hai disponibile!", - "use_all_confirm_title": "✅ Finisci tutto", - "use_all_confirm_msg": "Conferma che hai finito tutto il prodotto:", - "use_all_confirm_btn": "✅ Sì, finito", - "throw_all_confirm_title": "🗑️ Butta tutto", - "throw_all_confirm_msg": "Vuoi davvero buttare via tutto il prodotto?", - "throw_all_confirm_btn": "🗑️ Sì, butta" - }, - "product": { - "title_new": "Nuovo Prodotto", - "title_edit": "Modifica Prodotto", - "ai_fill": "📷 Scatta foto e identifica con AI", - "ai_fill_hint": "L'AI compilerà automaticamente i campi del prodotto", - "name_label": "🏷️ Nome Prodotto *", - "name_placeholder": "Es: Latte intero, Pasta penne rigate...", - "brand_label": "🏢 Marca", - "brand_placeholder": "Es: Barilla, Granarolo, Mutti...", - "category_label": "📂 Categoria", - "unit_label": "📏 Unità di misura", - "default_qty_label": "🔢 Quantità default", - "conf_size_label": "📦 Ogni confezione contiene:", - "conf_size_placeholder": "es. 300", - "notes_label": "📝 Note", - "notes_placeholder": "Es: senza lattosio, bio, conservare in frigo dopo apertura...", - "barcode_label": "🔖 Barcode", - "barcode_placeholder": "Codice a barre (se disponibile)", - "barcode_hint": "⚠️ Aggiungi il barcode così al prossimo acquisto basta scansionarlo!", - "submit": "💾 Salva Prodotto", - "name_required": "Inserisci il nome del prodotto", - "conf_size_required": "Specifica il contenuto di ogni confezione", - "expiry_estimated": "Scadenza stimata:", - "scan_expiry": "Scansiona data scadenza", - "expiry_hint": "📝 Puoi modificare la data o scansionarla con la fotocamera", - "add_batch": "📦 + Lotto con scadenza diversa", - "package_info": "📦 Confezione: {info}", - "edit_catalog": "⚙️ Modifica scheda prodotto (nome, marca, categoria…)", - "not_recognized": "⚠️ Prodotto non riconosciuto", - "edit_info": "✏️ Modifica informazioni", - "modify_details": "MODIFICA\nscadenza, luogo…", - "already_in_pantry": "📋 Già in dispensa", - "no_barcode": "Senza barcode" - }, - "products": { - "title": "📦 Tutti i Prodotti", - "search_placeholder": "🔍 Cerca prodotto...", - "empty": "Nessun prodotto nel database.\nScansiona un prodotto per iniziare!", - "no_category": "Nessun prodotto in questa categoria" - }, - "recipes": { - "title": "🍳 Ricette", - "generate": "✨ Genera nuova ricetta", - "archive_empty": "Nessuna ricetta salvata. Genera la tua prima ricetta!", - "dialog_title": "🍳 Ricetta", - "dialog_desc": "Genero una ricetta sana con gli ingredienti in dispensa, dando priorità a quelli in scadenza.", - "meal_label": "🕐 Per quale pasto?", - "persons_label": "👥 Quante persone?", - "meal_type_label": "🎯 Tipo di pasto", - "opt_fast": "⚡ Pasto Veloce", - "opt_light": "🥗 Poca Fame", - "opt_expiry": "⏰ Priorità Scadenze", - "opt_healthy": "💚 Extra Salutare", - "opt_opened": "📦 Priorità Cose Aperte", - "opt_zero_waste": "♻️ Zero Sprechi", - "generate_btn": "✨ Genera Ricetta", - "loading_msg": "Sto preparando la ricetta...", - "start_cooking": "👨‍🍳 Modalità Cucina", - "regenerate": "🔄 Generane un'altra", - "close_btn": "✅ Chiudi", - "ingredients_title": "🧾 Ingredienti", - "steps_title": "👨‍🍳 Procedimento", - "no_steps": "Nessun procedimento disponibile", - "generate_error": "Errore nella generazione", - "persons_short": "pers.", - "use_ingredient_title": "Usa ingrediente", - "recipe_qty_label": "Ricetta", - "from_where_label": "Da dove?", - "amount_label": "Quanto", - "use_amount_btn": "Usa questa quantità", - "use_all_btn": "Usa TUTTO / Finito", - "packs_label": "Confezioni", - "quantity_in_total": "Quantità in {unit} (totale: {total})", - "packs_of_have": "Confezioni da {size} (hai {count} conf)", - "scale_wait_stable": "Attendi 10s di stabilità per la compilazione automatica…", - "ingredient_scaled_toast": "📦 Ingrediente scalato dalla dispensa!", - "finished_added_bring_toast": "🛒 Prodotto finito → aggiunto a Bring!", - "load_error": "Errore nel caricamento" - }, - "shopping": { - "title": "🛒 Lista della Spesa", - "bring_loading": "Connessione a Bring!...", - "bring_not_configured": "Bring! non è configurato. Aggiungi email e password nelle impostazioni.", - "tab_to_buy": "🛍️ Da comprare", - "tab_forecast": "🧠 In previsione", - "total_label": "💰 Totale stimato", - "section_to_buy": "🛍️ Da comprare", - "suggestions_title": "💡 Suggerimenti AI", - "suggestions_add": "✅ Aggiungi selezionati a Bring!", - "search_prices": "🔍 Cerca tutti i prezzi", - "suggest_btn": "Suggerisci cosa comprare", - "smart_title": "🧠 Previsioni intelligenti", - "smart_empty": "Nessuna previsione disponibile.
Aggiungi prodotti alla dispensa per ricevere previsioni intelligenti.", - "smart_filter_all": "Tutti", - "smart_filter_critical": "🔴 Urgenti", - "smart_filter_high": "🟠 Presto", - "smart_filter_medium": "🟡 Pianifica", - "smart_filter_low": "🟢 Previsione", - "smart_add": "🛒 Aggiungi selezionati a Bring!", - "empty": "Lista della spesa vuota!\nUsa il pulsante sotto per generare suggerimenti.", - "already_in_list": "🛒 \"{name}\" già nella lista della spesa", - "already_in_list_short": "ℹ️ Già nella lista della spesa", - "add_prompt": "Vuoi aggiungerlo alla lista della spesa?", - "smart_already": "📊 La spesa intelligente prevede già {name}", - "all_searched": "Tutti i prodotti sono già stati cercati. Usa 🔄 per ricercare singoli.", - "search_complete": "Ricerca completata: {count} prodotti", - "removed_sufficient": "🧹 {removed} prodotto/i con scorte sufficienti rimosso/i dalla lista", - "suggest_buy": "🛒 Compra: {qty} {unit}", - "suggest_buy_approx": "🛒 Almeno: {qty} {unit}", - "suggest_buy_tip": "Quantità suggerita in base al consumo degli ultimi 14 giorni", - "suggest_buy_approx_tip": "Stima minima basata sul consumo (compra la confezione più vicina)", - "bring_badge": "🛒 Già su Bring!", - "add_urgent_toast": "🔴 {n} prodotto/i urgente/i aggiunto/i automaticamente a Bring!", - "migration_done": "✅ {migrated} aggiornati, {skipped} già ok", - "added_to_bring": "🛒 {n} prodotti aggiunti a Bring!", - "added_to_bring_skip": "{n} già presenti", - "all_on_bring": "Tutti i prodotti erano già su Bring!", - "freq_high": "📈 Uso frequente", - "freq_regular": "📊 Uso regolare", - "freq_occasional": "📉 Uso occasionale", - "out_of_stock": "Esaurito", - "scan_toast": "📷 Scansiona: {name}", - "empty_category": "Nessun prodotto in questa categoria", - "session_empty": "🛒 Nessun prodotto ancora", - "urgency_critical": "Urgente", - "urgency_high": "Presto", - "urgency_medium": "Pianifica", - "urgency_low": "Previsione", - "urgency_medium_short": "Medio", - "urgency_low_short": "Ok", - "tag_urgent": "🔴 Urgente", - "tag_priority": "⭐ Priorità", - "tag_check": "✅ Verificare", - "smart_already_predicted": "📊 La spesa intelligente prevede già {name}{urgency}.", - "item_removed": "✅ {name} rimosso dalla lista!", - "urgency_spec_critical": "⚡ Urgente", - "urgency_spec_high": "🟠 Presto", - "bring_add_n": "Aggiungi {n} a Bring!", - "bring_add_selected": "Aggiungi selezionati a Bring!", - "bring_adding": "Aggiunta in corso...", - "bring_added_one": "1 prodotto aggiunto a Bring!", - "bring_added_many": "{n} prodotti aggiunti a Bring!", - "bring_skipped": "({n} già in lista)", - "force_sync": "Forza sincronizzazione Bring!", - "scan_target_label": "Stai cercando", - "scan_target_found": "Trovato! Rimuovi dalla lista", - "bring_add_one": "Aggiungi 1 prodotto a Bring!", - "bring_add_many": "Aggiungi {n} prodotti a Bring!", - "syncing": "Sincronizzazione…", - "sync_done": "Sincronizzazione completata", - "price_searching": "Cerco...", - "search_action": "Ricerca", - "open_action": "Apri", - "not_found": "Non trovato", - "search_price": "Cerca prezzo", - "tap_to_scan": "Tocca per scansionare", - "tag_title": "Tag", - "remove_title": "Rimuovi", - "found_count": "{found}/{total} prodotti trovati", - "savings_offers": "· 🏷️ Risparmi €{amount} con le offerte", - "searching_progress": "Cerco {current}/{total}...", - "remove_error": "Errore nella rimozione", - "btn_fetch_prices": "Cerca i prezzi", - "price_total_label": "💰 Spesa stimata:", - "price_loading": "Ricerca prezzi…", - "price_not_found": "prezzo n/d", - "suggest_loading": "Analisi in corso...", - "suggest_error": "Errore nella generazione", - "priority_high": "Alta", - "priority_medium": "Media", - "priority_low": "Bassa" - }, - "ai": { - "title": "🤖 Identificazione AI", - "capture": "📸 Scatta Foto", - "retake": "🔄 Riscatta", - "hint": "Scatta una foto del prodotto e l'AI cercherà di identificarlo", - "identifying": "🤖 Identifico il prodotto...", - "no_api_key": "⚠️ Chiave API Gemini non configurata.\nAggiungi GEMINI_API_KEY nel file .env sul server.", - "fields_filled": "✅ Campi compilati dall'AI", - "use_data": "✅ Usa dati AI", - "use_data_no_barcode": "✅ Usa dati AI (senza barcode)" - }, - "log": { - "title": "📒 Storico", - "type_added": "Aggiunto", - "type_waste": "Buttato", - "type_used": "Usato", - "type_bring": "Aggiunto a Bring!", - "undone_badge": "Annullato", - "undo_title": "Annulla questa operazione", - "load_error": "Errore nel caricamento log", - "empty": "Nessuna operazione registrata.", - "undo_action_remove": "rimozione di", - "undo_action_restore": "ripristino di", - "undo_confirm": "Annullare questa operazione?\n→ {action} {name}", - "undo_success": "↩ Operazione annullata per {name}", - "already_undone": "Operazione già annullata", - "too_old": "Non è possibile annullare operazioni più vecchie di 24 ore", - "undo_error": "Errore durante l'annullamento" - }, - "chat": { - "title": "Gemini Chef", - "welcome": "Ciao! Sono il tuo assistente cucina", - "welcome_desc": "Chiedimi di prepararti un succo, uno spuntino, un piatto veloce... Conosco la tua dispensa, i tuoi elettrodomestici e le tue preferenze!", - "suggestion_snack": "🍿 Spuntino veloce", - "suggestion_juice": "🥤 Succo/Frullato", - "suggestion_light": "🥗 Qualcosa di leggero", - "suggestion_expiry": "⏰ Usa le scadenze", - "clear": "Nuova conversazione", - "placeholder": "Chiedi qualcosa...", - "cleared": "Chat cancellata", - "suggestion_snack_text": "Cosa posso preparare per uno spuntino veloce?", - "suggestion_juice_text": "Fammi un succo o frullato con quello che ho", - "suggestion_light_text": "Ho fame ma voglio qualcosa di leggero", - "suggestion_expiry_text": "Cosa sta per scadere e come posso usarlo?", - "transfer_to_recipes": "Trasferisci a Ricette", - "transferring": "Trasferimento in corso...", - "transferred": "Aggiunta alle Ricette!", - "open_recipe": "Apri la ricetta" - }, - "cooking": { - "close": "Chiudi", - "tts_btn": "Leggi ad alta voce", - "restart": "↺ Ricomincia", - "replay": "🔊 Rileggi", - "timer": "⏱️ {time} · Timer", - "prev": "◀ Precedente", - "next": "Successivo ▶", - "ingredient_used": "✔️ Scalato", - "ingredient_use_btn": "📦 Usa", - "ingredient_deduct_title": "Scala dalla dispensa", - "timer_expired_tts": "Timer {label} scaduto!", - "timer_warning_tts": "Attenzione! {label}: mancano 10 secondi!", - "recipe_done_tts": "Ricetta completata! Buon appetito!", - "expires_chip": "scade {date}", - "finish": "✅ Fine" - }, - "settings": { - "title": "⚙️ Configurazione", - "tab_api": "API Keys", - "tab_bring": "Bring!", - "tab_recipe": "Ricette", - "tab_mealplan": "Piano Settimanale", - "tab_appliances": "Elettrodomestici", - "tab_spesa": "Spesa Online", - "tab_camera": "Fotocamera", - "tab_security": "Sicurezza", - "tab_tts": "Voce (TTS)", - "tab_language": "Lingua", - "tab_scale": "Bilancia Smart", - "gemini": { - "title": "🤖 Google Gemini AI", - "hint": "Chiave API per identificazione prodotti, scadenze e ricette.", - "key_label": "API Key Gemini" - }, - "bring": { - "title": "🛒 Bring! Shopping List", - "hint": "Credenziali per l'integrazione con la lista della spesa Bring!", - "email_label": "📧 Email Bring!", - "password_label": "🔒 Password Bring!" - }, - "price": { - "title": "💰 Stima Prezzi (AI)", - "hint": "Mostra il costo stimato di ogni prodotto nella lista della spesa usando l'AI.", - "enabled_label": "Attiva stima prezzi", - "country_label": "🌍 Paese di riferimento", - "currency_label": "💱 Valuta", - "update_label": "🔄 Aggiorna prezzi ogni", - "update_suffix": "mesi" - }, - "recipe": { - "title": "🍳 Preferenze Ricette", - "hint": "Configura le opzioni predefinite per la generazione delle ricette.", - "persons_label": "👥 Persone predefinite", - "options_label": "🎯 Opzioni ricetta predefinite", - "fast": "⚡ Pasto Veloce", - "light": "🥗 Poca Fame", - "expiry": "⏰ Priorità Scadenze", - "healthy": "💚 Extra Salutare", - "opened": "📦 Priorità Cose Aperte", - "zerowaste": "♻️ Zero Sprechi", - "dietary_label": "🚫 Intolleranze / Restrizioni", - "dietary_placeholder": "Es: senza glutine, senza lattosio, vegetariano..." - }, - "mealplan": { - "title": "📅 Piano Pasti Settimanale", - "hint": "Imposta la tipologia di pasto per ogni giorno. Sarà usata come guida nella generazione delle ricette.", - "enabled": "✅ Attiva piano pasti settimanale", - "legend": "🌤️ = Pranzo · 🌙 = Cena · Tocca un badge per cambiarlo.", - "types_title": "📋 Tipologie disponibili" - }, - "appliances": { - "title": "🔌 Elettrodomestici Disponibili", - "hint": "Indica gli elettrodomestici che hai a disposizione. Saranno considerati nella generazione delle ricette.", - "new_placeholder": "Es: Macchina del pane, Bimby, Friggitrice ad aria...", - "quick_title": "Aggiungi velocemente:", - "oven": "🔥 Forno", - "microwave": "📡 Microonde", - "air_fryer": "🍟 Friggitrice ad aria", - "bread_maker": "🍞 Macchina pane", - "bimby": "🤖 Bimby/Cookeo", - "mixer": "🌀 Planetaria", - "steamer": "♨️ Vaporiera", - "pressure_cooker": "🫕 Pentola pressione", - "toaster": "🍞 Tostapane", - "blender": "🍹 Frullatore", - "empty": "Nessun elettrodomestico aggiunto" - }, - "spesa": { - "title": "🛍️ Spesa Online", - "hint": "Configura il provider per la spesa online.", - "provider_label": "🏪 Provider", - "email_label": "📧 Email", - "password_label": "🔒 Password", - "login_btn": "🔐 Accedi", - "ai_prompt_label": "🤖 Prompt AI selezione prodotto", - "ai_prompt_placeholder": "Istruzioni per l'AI quando deve scegliere tra più prodotti...", - "ai_prompt_hint": "L'AI usa questo prompt per scegliere il prodotto più appropriato tra i risultati. Lascia vuoto per il comportamento predefinito.", - "configure_first": "Configura prima la Spesa Online nelle impostazioni", - "missing_credentials": "Inserisci email e password", - "login_in_progress": "Accesso in corso...", - "login_error_prefix": "Errore:", - "login_network_error_prefix": "Errore di rete:", - "login_success_default": "Login effettuato!", - "result_name_label": "Nome", - "result_card_label": "Tessera", - "result_pickup_label": "Punto Ritiro", - "result_points_label": "Punti Fedeltà", - "connected_relogin": "✅ Connesso — Riaccedi", - "connected_as": "Connesso come {name}" - }, - "camera": { - "title": "📷 Fotocamera", - "hint": "Scegli quale fotocamera utilizzare per la scansione barcode e l'identificazione AI.", - "device_label": "📸 Fotocamera predefinita", - "back": "📱 Posteriore (default)", - "front": "🤳 Anteriore", - "devices_hint": "Se hai più fotocamere, puoi selezionarne una specifica dall'elenco sopra dopo aver concesso i permessi.", - "detect_btn": "🔄 Rileva fotocamere" - }, - "security": { - "title": "🔒 Certificato HTTPS", - "hint": "Se il browser mostra l'errore \"La connessione non è privata\" (ERR_CERT_AUTHORITY_INVALID), devi installare il certificato CA nel dispositivo.", - "download_btn": "📥 Scarica Certificato CA" - }, - "tts": { - "title": "🔊 Voce & TTS", - "hint": "Configura la sintesi vocale tramite qualsiasi API REST esterna. I passi della ricetta e i timer scaduti verranno inviati all'endpoint configurato.", - "enabled": "✅ Attiva TTS", - "url_label": "🌐 URL Endpoint", - "method_label": "📡 Metodo HTTP", - "auth_label": "🔐 Autenticazione", - "auth_bearer": "Bearer Token", - "auth_custom": "Header personalizzato", - "auth_none": "Nessuna", - "token_label": "🔑 Bearer Token", - "custom_header_name": "📋 Nome header", - "custom_header_value": "📋 Valore header", - "content_type_label": "📄 Content-Type", - "payload_key_label": "🗝️ Campo testo nel payload", - "payload_key_hint": "Nome del campo JSON che conterrà il testo da leggere (es: message, text).", - "extra_fields_label": "➕ Campi extra (JSON)", - "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}", - "extra_fields_hint": "Campi aggiuntivi da includere nel payload, in formato JSON. Lascia vuoto se non necessario.", - "test_btn": "🔊 Invia Test Vocale" - }, - "language": { - "title": "🌐 Lingua / Language", - "hint": "Seleziona la lingua dell'interfaccia. Select the interface language.", - "label": "🌐 Lingua", - "restart_notice": "La pagina verrà ricaricata per applicare la nuova lingua." - }, - "screensaver": { - "label": "Attiva salvaschermo", - "card_title": "🌙 Salvaschermo", - "card_hint": "Mostra un orologio con fatti utili dopo 5 minuti di inattività. Di default è disattivato." - }, - "scale": { - "title": "⚖️ Bilancia Smart", - "hint": "Collega una bilancia Bluetooth tramite il gateway Android per leggere il peso automaticamente.", - "tab": "Bilancia Smart", - "enabled": "✅ Abilita bilancia smart", - "url_label": "🌐 URL Gateway WebSocket", - "url_placeholder": "ws://192.168.1.x:8765", - "url_hint": "URL mostrato dall'app Android (stessa rete Wi-Fi). Es:", - "test_btn": "🔗 Testa connessione", - "download_btn": "📥 Scarica Gateway Android (APK)", - "download_hint": "App Android che fa da ponte tra la bilancia BLE e questo sito.", - "download_sub": "Sorgente: evershelf-scale-gateway/ nella root del progetto" - }, - "kiosk": { - "hint": "Trasforma un tablet Android in un pannello EverShelf sempre acceso, con bilancia BLE integrata.", - "download_btn": "📥 Scarica EverShelf Kiosk (APK)", - "download_sub": "Modalità kiosk full-screen + gateway bilancia integrato. Sorgente: evershelf-kiosk/" - }, - "saved": "✅ Configurazione salvata!", - "saved_local": "✅ Configurazione salvata localmente", - "saved_local_error": "⚠️ Salvato localmente, errore server: {error}" - }, - "expiry": { - "today": "OGGI", - "tomorrow": "Domani", - "days": "{days} giorni", - "expired_days": "Da {days}g", - "expired_yesterday": "Da ieri", - "expired_today": "Oggi", - "badge_today": "⚠️ Scade oggi!", - "badge_tomorrow": "⏰ Domani", - "badge_tomorrow_long": "⏰ Scade domani", - "badge_days": "⏰ {n} giorni", - "badge_expired_ago": "⚠️ Scaduto da {n}g", - "badge_expired": "⛔ Scaduto!", - "badge_stable": "✅ Stabile", - "badge_expiring_short": "⏰ Scade fra {n}gg", - "badge_ok_still": "✅ Ancora {n}gg", - "badge_expires_red": "🔴 Scade tra {n}g", - "badge_expires_yellow": "🟡 Scade tra {n}g", - "badge_expired_bare": "⚠️ Scaduto", - "badge_expires_warn": "⚠️ Scade tra {n}gg", - "badge_days_left": "⏳ ~{n}gg rimasti", - "days_approx": "~{n} giorni", - "weeks_approx": "~{n} settimane", - "months_approx": "~{n} mesi", - "years_approx": "~{n} anni", - "expired_today_long": "Scaduto oggi", - "expired_ago_long": "Scaduto da {n} giorni", - "expired_suffix": "— Scaduto!", - "expired_suffix_ok": "— Scaduto (ancora ok)", - "expired_suffix_warning": "— Scaduto (controlla)", - "days_compact": "{n}gg" - }, - "status": { - "ok": "OK", - "check": "Controlla", - "discard": "Buttare", - "tip_freezer_ok": "In freezer: ancora sicuro (~{n}g di margine)", - "tip_freezer_check": "In freezer da molto, potrebbe aver perso qualità. Consumare presto", - "tip_freezer_danger": "In freezer da troppo tempo, rischio di bruciatura da gelo e degrado", - "tip_highRisk_check": "Scaduto da poco, controlla odore e aspetto prima di consumare", - "tip_highRisk_danger": "Prodotto deperibile scaduto: da buttare per sicurezza", - "tip_medRisk_check1": "Controlla aspetto e odore prima di consumare", - "tip_medRisk_check2": "Scaduto da un po', verificare bene prima dell'uso", - "tip_medRisk_danger": "Troppo tempo dalla scadenza, meglio buttare", - "tip_lowRisk_ok": "Prodotto a lunga conservazione, ancora sicuro da consumare", - "tip_lowRisk_check": "Scaduto da oltre un mese, controllare integrità confezione", - "tip_lowRisk_danger": "Scaduto da troppo tempo, meglio non rischiare" - }, - "toast": { - "product_saved": "Prodotto salvato!", - "product_created": "Prodotto creato!", - "product_updated": "✅ Prodotto aggiornato!", - "product_removed": "Prodotto rimosso", - "updated": "Aggiornato!", - "quantity_confirmed": "✓ Quantità confermata", - "added_to_inventory": "✅ {name} aggiunto!", - "removed_from_list": "✅ {name} rimosso dalla lista!", - "removed_from_list_short": "Rimosso dalla lista", - "added_to_shopping": "🛒 Aggiunto alla lista della spesa!", - "removed_from_shopping": "🛒 Rimosso dalla lista della spesa", - "finished_to_bring": "🛒 Prodotto finito → aggiunto a Bring!", - "thrown_away": "🗑️ {name} buttato!", - "thrown_away_partial": "🗑️ Buttato {qty} {unit} di {name}", - "product_finished_confirmed": "✅ Rimosso — riaggiungi quando ne ricompri", - "appliance_added": "Elettrodomestico aggiunto", - "item_added": "{name} aggiunto" - }, - "antiwaste": { - "title": "🌱 Rapporto Anti-Spreco", - "grade_label": "Voto", - "you": "Tu", - "avg_label": "Media", - "better": "🎉 Perdi il {diff}% in meno della {country}!", - "worse": "⚠️ Perdi più della media {country}. Puoi migliorare!", - "on_par": "→ Sei nella media {country}. Prova a fare ancora meglio!", - "saved_money": "~{amount}/mese risparmiati", - "saved_meals": "~{n} pasti salvati", - "saved_co2": "{n} kg CO₂ evitata", - "trend_title": "Andamento (ultimi 3 mesi)", - "months_ago_2": "-60 gg", - "months_ago_1": "-30 gg", - "this_month": "Ora", - "country_it": "media italiana", - "country_de": "media tedesca", - "country_en": "media USA", - "source": "Fonti: REDUCE, Eurostat, USDA 2021", - "live_on": "Dati in tempo reale", - "live_off": "Offline", - "meals": "pasti", - "annual_info": "📅 Tu ~{you} kg/anno · media ~{avg} kg/anno", - "badge_rate": "tasso perdita", - "badge_saved_money": "risparmio vs media", - "badge_wasted": "prod. persi", - "badge_better": "in meno vs media" - }, - "error": { - "generic": "Errore", - "network": "Errore di rete", - "no_api_key": "Configura la chiave API nelle impostazioni", - "loading": "Errore nel caricamento del prodotto", - "not_found": "Prodotto non trovato", - "not_found_manual": "Prodotto non trovato. Inseriscilo manualmente.", - "search": "Errore nella ricerca. Riprova.", - "search_short": "Errore nella ricerca", - "save": "Errore nel salvataggio", - "connection": "Errore di connessione", - "camera": "Impossibile accedere alla fotocamera", - "bring_add": "Errore nell'aggiunta a Bring!", - "bring_connection": "Errore connessione Bring!", - "identification": "Errore nell'identificazione", - "ai_quota": "Quota AI esaurita. Riprova tra qualche minuto.", - "barcode_empty": "Inserisci un codice a barre", - "barcode_format": "Il codice a barre deve contenere solo numeri (4-14 cifre)", - "min_chars": "Scrivi almeno 2 caratteri", - "not_in_inventory": "Prodotto non nell'inventario", - "appliance_exists": "Elettrodomestico già presente", - "already_exists": "Già presente", - "network_retry": "Errore di connessione. Riprova.", - "select_items": "Seleziona almeno un prodotto", - "server_offline": "Connessione al server persa", - "server_restored": "Connessione al server ripristinata", - "server_retry": "Riprova" - }, - "confirm": { - "remove_item": "Vuoi davvero rimuovere questo prodotto dall'inventario?", - "kiosk_exit": "Uscire dalla modalità kiosk?", - "cancel": "Annulla", - "proceed": "Conferma" - }, - "location": { - "dispensa": "Dispensa", - "frigo": "Frigo", - "freezer": "Freezer" - }, - "edit": { - "title": "Modifica {name}", - "unknown_hint": "Inserisci il nome e le informazioni del prodotto", - "label_name": "🏷️ Nome prodotto" + "language": { + "title": "🌐 Lingua / Language", + "hint": "Seleziona la lingua dell'interfaccia. Select the interface language.", + "label": "🌐 Lingua", + "restart_notice": "La pagina verrà ricaricata per applicare la nuova lingua." }, "screensaver": { - "recipe_btn": "Ricette", - "scan_btn": "Scansiona prodotto" - }, - "days": { - "mon": "Lunedì", - "tue": "Martedì", - "wed": "Mercoledì", - "thu": "Giovedì", - "fri": "Venerdì", - "sat": "Sabato", - "sun": "Domenica", - "mon_short": "Lun", - "tue_short": "Mar", - "wed_short": "Mer", - "thu_short": "Gio", - "fri_short": "Ven", - "sat_short": "Sab", - "sun_short": "Dom" - }, - "meal_types": { - "lunch": "Pranzo", - "dinner": "Cena", - "colazione": "Colazione", - "merenda": "Merenda", - "dolce": "Dolce", - "succo": "Succo di Frutta", - "pranzo": "Pranzo", - "cena": "Cena" + "label": "Attiva salvaschermo", + "card_title": "🌙 Salvaschermo", + "card_hint": "Mostra un orologio con fatti utili dopo 5 minuti di inattività. Di default è disattivato." }, "scale": { - "status_connected": "Bilancia connessa", - "status_searching": "Connesso al gateway, attesa bilancia…", - "status_disconnected": "Gateway bilancia non raggiungibile", - "status_error": "Errore connessione gateway", - "not_connected": "Gateway bilancia non connesso", - "read_btn": "⚖️ Leggi dalla bilancia", - "reading_title": "Lettura bilancia", - "place_on_scale": "Metti il prodotto sulla bilancia…", - "waiting_stable": "Il peso venire rilevato automaticamente quando la lettura sarà stabile.", - "no_url": "Inserisci l'URL del gateway", - "testing": "⏳ Test connessione…", - "connected_ok": "Connessione gateway riuscita!", - "timeout": "Timeout: nessuna risposta dal gateway", - "error_connect": "Impossibile connettersi al gateway", - "tab": "Bilancia Smart", - "low_weight": "Peso < 10 g · inserisci manualmente\n(la lettura automatica richiede almeno 10 g)", - "density_hint": "(densità {density} g/ml)", - "ml_hint": "(verrà convertito in ml)", - "weight_detected": "Peso rilevato — attendi 10s di stabilità…", - "weight_too_low": "Peso troppo basso — attendi…", - "stable": "✓ Stabile", - "auto_confirm": "✅ {val} {unit} — conferma automatica tra 5s (tocca per annullare)", - "cancelled_replace": "Annullato — rimetti l'ingrediente sulla bilancia per riprendere" + "title": "⚖️ Bilancia Smart", + "hint": "Collega una bilancia Bluetooth tramite il gateway Android per leggere il peso automaticamente.", + "tab": "Bilancia Smart", + "enabled": "✅ Abilita bilancia smart", + "url_label": "🌐 URL Gateway WebSocket", + "url_placeholder": "ws://192.168.1.x:8765", + "url_hint": "URL mostrato dall'app Android (stessa rete Wi-Fi). Es:", + "test_btn": "🔗 Testa connessione", + "download_btn": "📥 Scarica Gateway Android (APK)", + "download_hint": "App Android che fa da ponte tra la bilancia BLE e questo sito.", + "download_sub": "Sorgente: evershelf-scale-gateway/ nella root del progetto" }, - "prediction": { - "expected_qty": "Previsto: {expected} {unit}", - "actual_qty": "Attuale: {actual} {unit}", - "check_suggestion": "Verifica o pesa la quantità residua" + "kiosk": { + "hint": "Trasforma un tablet Android in un pannello EverShelf sempre acceso, con bilancia BLE integrata.", + "download_btn": "📥 Scarica EverShelf Kiosk (APK)", + "download_sub": "Modalità kiosk full-screen + gateway bilancia integrato. Sorgente: evershelf-kiosk/" }, - "date": { - "today": "📅 Oggi", - "yesterday": "📅 Ieri" - }, - "scanner": { - "title_barcode": "🔖 Scansiona Barcode", - "barcode_hint": "Inquadra il codice a barre del prodotto", - "barcode_manual_placeholder": "O inserisci manualmente...", - "barcode_use_btn": "✅ Usa questo codice", - "ai_identifying": "🤖 Identifico il prodotto...", - "ai_analyzing": "🤖 Analisi AI in corso...", - "product_label_hint": "Inquadra l'etichetta del prodotto", - "expiry_label_hint": "Inquadra la data di scadenza stampata sul prodotto", - "capture_btn": "📸 Scatta", - "capture_photo_btn": "📸 Scatta Foto", - "retake_btn": "🔄 Riscatta", - "camera_error_hint": "Assicurati di usare HTTPS e di aver concesso i permessi della fotocamera.
Puoi inserire il barcode manualmente o usare l'identificazione AI.", - "no_barcode": "Senza barcode" - }, - "lowstock": { - "title": "⚠️ Sta per finire!", - "message": "{name} sta per finire — rimangono solo {qty}.", - "question": "Vuoi aggiungerlo alla lista della spesa?", - "yes": "🛒 Sì, aggiungi a Bring!", - "no": "No, per ora va bene" - }, - "move": { - "title": "📦 Spostare il resto?", - "question": "Vuoi spostare {thing} di {name} in un'altra posizione?", - "question_short": "Vuoi spostare {thing} in un'altra posizione?", - "thing_opened": "la confezione aperta", - "thing_rest": "il resto", - "stay_btn": "No, resta in {location}", - "moved_toast": "📦 Confezione aperta spostata in {location}", - "vacuum_restore": "🫙 Torna sotto vuoto" - }, - "nova": { - "1": "Non trasformato", - "2": "Ingrediente culinario", - "3": "Trasformato", - "4": "Ultra-trasformato" - }, - "meal_plan_types": { - "pasta": "Pasta", - "riso": "Riso", - "carne": "Carne", - "pesce": "Pesce", - "legumi": "Legumi", - "uova": "Uova", - "formaggio": "Formaggio", - "pizza": "Pizza", - "affettati": "Affettati", - "verdure": "Verdure", - "zuppa": "Zuppa", - "insalata": "Insalata", - "pane": "Pane/Sandwich", - "dolce": "Dolce", - "libero": "Libero" - }, - "meal_sub": { - "dolce_torta": "Torta", - "dolce_crema": "Crema / Budino", - "dolce_crumble": "Crumble / Crostata", - "dolce_biscotti": "Biscotti / Pasticcini", - "dolce_frutta": "Dolce alla Frutta", - "succo_dolce": "Dolce / Fruttato", - "succo_energizzante": "Energizzante", - "succo_detox": "Detox / Verde", - "succo_rinfrescante": "Rinfrescante", - "succo_vitaminico": "Vitaminico / Agrumi" - }, - "meal_plan": { - "reset_success": "Piano settimanale ripristinato", - "suggested_by": "suggerito dal piano settimanale", - "not_available": "non disponibile in dispensa" - }, - "kiosk_session": { - "first_item": "Primo prodotto: {name}!", - "items_two_four": "{n} prodotti — stai scaldando i motori 🚀", - "items_five_nine": "{n} prodotti — ottimo ritmo! 💪", - "items_ten_twenty": "{n} prodotti — quasi un recordman 🏆", - "items_twenty_plus": "{n} prodotti — spesa epica! 🛒🔥", - "duplicates_one": "1 bis (stessa cosa due volte)", - "duplicates_many": "{n} bis (roba presa più volte)", - "top_category": "Categoria top: {cat} ({count}×)", - "items_fallback": "{n} prodott{n} aggiunti" - } + "saved": "✅ Configurazione salvata!", + "saved_local": "✅ Configurazione salvata localmente", + "saved_local_error": "⚠️ Salvato localmente, errore server: {error}" + }, + "expiry": { + "today": "OGGI", + "tomorrow": "Domani", + "days": "{days} giorni", + "expired_days": "Da {days}g", + "expired_yesterday": "Da ieri", + "expired_today": "Oggi", + "badge_today": "⚠️ Scade oggi!", + "badge_tomorrow": "⏰ Domani", + "badge_tomorrow_long": "⏰ Scade domani", + "badge_days": "⏰ {n} giorni", + "badge_expired_ago": "⚠️ Scaduto da {n}g", + "badge_expired": "⛔ Scaduto!", + "badge_stable": "✅ Stabile", + "badge_expiring_short": "⏰ Scade fra {n}gg", + "badge_ok_still": "✅ Ancora {n}gg", + "badge_expires_red": "🔴 Scade tra {n}g", + "badge_expires_yellow": "🟡 Scade tra {n}g", + "badge_expired_bare": "⚠️ Scaduto", + "badge_expires_warn": "⚠️ Scade tra {n}gg", + "badge_days_left": "⏳ ~{n}gg rimasti", + "days_approx": "~{n} giorni", + "weeks_approx": "~{n} settimane", + "months_approx": "~{n} mesi", + "years_approx": "~{n} anni", + "expired_today_long": "Scaduto oggi", + "expired_ago_long": "Scaduto da {n} giorni", + "expired_suffix": "— Scaduto!", + "expired_suffix_ok": "— Scaduto (ancora ok)", + "expired_suffix_warning": "— Scaduto (controlla)", + "days_compact": "{n}gg" + }, + "status": { + "ok": "OK", + "check": "Controlla", + "discard": "Buttare", + "tip_freezer_ok": "In freezer: ancora sicuro (~{n}g di margine)", + "tip_freezer_check": "In freezer da molto, potrebbe aver perso qualità. Consumare presto", + "tip_freezer_danger": "In freezer da troppo tempo, rischio di bruciatura da gelo e degrado", + "tip_highRisk_check": "Scaduto da poco, controlla odore e aspetto prima di consumare", + "tip_highRisk_danger": "Prodotto deperibile scaduto: da buttare per sicurezza", + "tip_medRisk_check1": "Controlla aspetto e odore prima di consumare", + "tip_medRisk_check2": "Scaduto da un po', verificare bene prima dell'uso", + "tip_medRisk_danger": "Troppo tempo dalla scadenza, meglio buttare", + "tip_lowRisk_ok": "Prodotto a lunga conservazione, ancora sicuro da consumare", + "tip_lowRisk_check": "Scaduto da oltre un mese, controllare integrità confezione", + "tip_lowRisk_danger": "Scaduto da troppo tempo, meglio non rischiare" + }, + "toast": { + "product_saved": "Prodotto salvato!", + "product_created": "Prodotto creato!", + "product_updated": "✅ Prodotto aggiornato!", + "product_removed": "Prodotto rimosso", + "updated": "Aggiornato!", + "quantity_confirmed": "✓ Quantità confermata", + "added_to_inventory": "✅ {name} aggiunto!", + "removed_from_list": "✅ {name} rimosso dalla lista!", + "removed_from_list_short": "Rimosso dalla lista", + "added_to_shopping": "🛒 Aggiunto alla lista della spesa!", + "removed_from_shopping": "🛒 Rimosso dalla lista della spesa", + "finished_to_bring": "🛒 Prodotto finito → aggiunto a Bring!", + "thrown_away": "🗑️ {name} buttato!", + "thrown_away_partial": "🗑️ Buttato {qty} {unit} di {name}", + "product_finished_confirmed": "✅ Rimosso — riaggiungi quando ne ricompri", + "appliance_added": "Elettrodomestico aggiunto", + "item_added": "{name} aggiunto" + }, + "antiwaste": { + "title": "🌱 Rapporto Anti-Spreco", + "grade_label": "Voto", + "you": "Tu", + "avg_label": "Media", + "better": "🎉 Perdi il {diff}% in meno della {country}!", + "worse": "⚠️ Perdi più della media {country}. Puoi migliorare!", + "on_par": "→ Sei nella media {country}. Prova a fare ancora meglio!", + "saved_money": "~{amount}/mese risparmiati", + "saved_meals": "~{n} pasti salvati", + "saved_co2": "{n} kg CO₂ evitata", + "trend_title": "Andamento (ultimi 3 mesi)", + "months_ago_2": "-60 gg", + "months_ago_1": "-30 gg", + "this_month": "Ora", + "country_it": "media italiana", + "country_de": "media tedesca", + "country_en": "media USA", + "source": "Fonti: REDUCE, Eurostat, USDA 2021", + "live_on": "Dati in tempo reale", + "live_off": "Offline", + "meals": "pasti", + "annual_info": "📅 Tu ~{you} kg/anno · media ~{avg} kg/anno", + "badge_rate": "tasso perdita", + "badge_saved_money": "risparmio vs media", + "badge_wasted": "prod. persi", + "badge_better": "in meno vs media" + }, + "error": { + "generic": "Errore", + "network": "Errore di rete", + "no_api_key": "Configura la chiave API nelle impostazioni", + "loading": "Errore nel caricamento del prodotto", + "not_found": "Prodotto non trovato", + "not_found_manual": "Prodotto non trovato. Inseriscilo manualmente.", + "search": "Errore nella ricerca. Riprova.", + "search_short": "Errore nella ricerca", + "save": "Errore nel salvataggio", + "connection": "Errore di connessione", + "camera": "Impossibile accedere alla fotocamera", + "bring_add": "Errore nell'aggiunta a Bring!", + "bring_connection": "Errore connessione Bring!", + "identification": "Errore nell'identificazione", + "ai_quota": "Quota AI esaurita. Riprova tra qualche minuto.", + "barcode_empty": "Inserisci un codice a barre", + "barcode_format": "Il codice a barre deve contenere solo numeri (4-14 cifre)", + "min_chars": "Scrivi almeno 2 caratteri", + "not_in_inventory": "Prodotto non nell'inventario", + "appliance_exists": "Elettrodomestico già presente", + "already_exists": "Già presente", + "network_retry": "Errore di connessione. Riprova.", + "select_items": "Seleziona almeno un prodotto", + "server_offline": "Connessione al server persa", + "server_restored": "Connessione al server ripristinata", + "server_retry": "Riprova" + }, + "confirm": { + "remove_item": "Vuoi davvero rimuovere questo prodotto dall'inventario?", + "kiosk_exit": "Uscire dalla modalità kiosk?", + "cancel": "Annulla", + "proceed": "Conferma" + }, + "location": { + "dispensa": "Dispensa", + "frigo": "Frigo", + "freezer": "Freezer" + }, + "edit": { + "title": "Modifica {name}", + "unknown_hint": "Inserisci il nome e le informazioni del prodotto", + "label_name": "🏷️ Nome prodotto" + }, + "screensaver": { + "recipe_btn": "Ricette", + "scan_btn": "Scansiona prodotto" + }, + "days": { + "mon": "Lunedì", + "tue": "Martedì", + "wed": "Mercoledì", + "thu": "Giovedì", + "fri": "Venerdì", + "sat": "Sabato", + "sun": "Domenica", + "mon_short": "Lun", + "tue_short": "Mar", + "wed_short": "Mer", + "thu_short": "Gio", + "fri_short": "Ven", + "sat_short": "Sab", + "sun_short": "Dom" + }, + "meal_types": { + "lunch": "Pranzo", + "dinner": "Cena", + "colazione": "Colazione", + "merenda": "Merenda", + "dolce": "Dolce", + "succo": "Succo di Frutta", + "pranzo": "Pranzo", + "cena": "Cena" + }, + "scale": { + "status_connected": "Bilancia connessa", + "status_searching": "Connesso al gateway, attesa bilancia…", + "status_disconnected": "Gateway bilancia non raggiungibile", + "status_error": "Errore connessione gateway", + "not_connected": "Gateway bilancia non connesso", + "read_btn": "⚖️ Leggi dalla bilancia", + "reading_title": "Lettura bilancia", + "place_on_scale": "Metti il prodotto sulla bilancia…", + "waiting_stable": "Il peso venire rilevato automaticamente quando la lettura sarà stabile.", + "no_url": "Inserisci l'URL del gateway", + "testing": "⏳ Test connessione…", + "connected_ok": "Connessione gateway riuscita!", + "timeout": "Timeout: nessuna risposta dal gateway", + "error_connect": "Impossibile connettersi al gateway", + "tab": "Bilancia Smart", + "low_weight": "Peso < 10 g · inserisci manualmente\n(la lettura automatica richiede almeno 10 g)", + "density_hint": "(densità {density} g/ml)", + "ml_hint": "(verrà convertito in ml)", + "weight_detected": "Peso rilevato — attendi 10s di stabilità…", + "weight_too_low": "Peso troppo basso — attendi…", + "stable": "✓ Stabile", + "auto_confirm": "✅ {val} {unit} — conferma automatica tra 5s (tocca per annullare)", + "cancelled_replace": "Annullato — rimetti l'ingrediente sulla bilancia per riprendere" + }, + "prediction": { + "expected_qty": "Previsto: {expected} {unit}", + "actual_qty": "Attuale: {actual} {unit}", + "check_suggestion": "Verifica o pesa la quantità residua" + }, + "date": { + "today": "📅 Oggi", + "yesterday": "📅 Ieri" + }, + "scanner": { + "title_barcode": "🔖 Scansiona Barcode", + "barcode_hint": "Inquadra il codice a barre del prodotto", + "barcode_manual_placeholder": "O inserisci manualmente...", + "barcode_use_btn": "✅ Usa questo codice", + "ai_identifying": "🤖 Identifico il prodotto...", + "ai_analyzing": "🤖 Analisi AI in corso...", + "product_label_hint": "Inquadra l'etichetta del prodotto", + "expiry_label_hint": "Inquadra la data di scadenza stampata sul prodotto", + "capture_btn": "📸 Scatta", + "capture_photo_btn": "📸 Scatta Foto", + "retake_btn": "🔄 Riscatta", + "camera_error_hint": "Assicurati di usare HTTPS e di aver concesso i permessi della fotocamera.
Puoi inserire il barcode manualmente o usare l'identificazione AI.", + "no_barcode": "Senza barcode", + "save_new_btn": "🆕 Non è nessuno di questi — salva come nuovo" + }, + "lowstock": { + "title": "⚠️ Sta per finire!", + "message": "{name} sta per finire — rimangono solo {qty}.", + "question": "Vuoi aggiungerlo alla lista della spesa?", + "yes": "🛒 Sì, aggiungi a Bring!", + "no": "No, per ora va bene" + }, + "move": { + "title": "📦 Spostare il resto?", + "question": "Vuoi spostare {thing} di {name} in un'altra posizione?", + "question_short": "Vuoi spostare {thing} in un'altra posizione?", + "thing_opened": "la confezione aperta", + "thing_rest": "il resto", + "stay_btn": "No, resta in {location}", + "moved_toast": "📦 Confezione aperta spostata in {location}", + "vacuum_restore": "🫙 Torna sotto vuoto" + }, + "nova": { + "1": "Non trasformato", + "2": "Ingrediente culinario", + "3": "Trasformato", + "4": "Ultra-trasformato" + }, + "meal_plan_types": { + "pasta": "Pasta", + "riso": "Riso", + "carne": "Carne", + "pesce": "Pesce", + "legumi": "Legumi", + "uova": "Uova", + "formaggio": "Formaggio", + "pizza": "Pizza", + "affettati": "Affettati", + "verdure": "Verdure", + "zuppa": "Zuppa", + "insalata": "Insalata", + "pane": "Pane/Sandwich", + "dolce": "Dolce", + "libero": "Libero" + }, + "meal_sub": { + "dolce_torta": "Torta", + "dolce_crema": "Crema / Budino", + "dolce_crumble": "Crumble / Crostata", + "dolce_biscotti": "Biscotti / Pasticcini", + "dolce_frutta": "Dolce alla Frutta", + "succo_dolce": "Dolce / Fruttato", + "succo_energizzante": "Energizzante", + "succo_detox": "Detox / Verde", + "succo_rinfrescante": "Rinfrescante", + "succo_vitaminico": "Vitaminico / Agrumi" + }, + "meal_plan": { + "reset_success": "Piano settimanale ripristinato", + "suggested_by": "suggerito dal piano settimanale", + "not_available": "non disponibile in dispensa" + }, + "kiosk_session": { + "first_item": "Primo prodotto: {name}!", + "items_two_four": "{n} prodotti — stai scaldando i motori 🚀", + "items_five_nine": "{n} prodotti — ottimo ritmo! 💪", + "items_ten_twenty": "{n} prodotti — quasi un recordman 🏆", + "items_twenty_plus": "{n} prodotti — spesa epica! 🛒🔥", + "duplicates_one": "1 bis (stessa cosa due volte)", + "duplicates_many": "{n} bis (roba presa più volte)", + "top_category": "Categoria top: {cat} ({count}×)", + "items_fallback": "{n} prodott{n} aggiunti" + }, + "nutrition": { + "title": "🥗 Analisi Alimentare", + "score_excellent": "😄 Ottimo", + "score_good": "🙂 Discreto", + "score_improve": "😬 Migliorabile", + "label_health": "🌿 Salute", + "label_variety": "🎨 Varietà", + "label_fresh": "❄️ Freschi", + "source": "Basato su {n} prodotti in dispensa · EverShelf", + "products_count": "prodotti", + "today_title": "🥗 La tua dispensa oggi", + "products_n": "{n} prodotti" + }, + "facts": { + "greeting_morning": "Buongiorno", + "greeting_afternoon": "Buon pomeriggio", + "greeting_evening": "Buonasera", + "pantry_waiting": "{greeting}! La tua Dispensa ti aspetta.", + "expired_one": "Hai 1 prodotto scaduto in dispensa. Controlla!", + "expired_many": "Hai {n} prodotti scaduti in dispensa. Controlla!", + "expired_list": "Prodotti scaduti: {names}", + "expired_list_more": "e altri {n}", + "freezer_expired_ok": "{name} è scaduto, ma essendo in freezer potrebbe essere ancora buono! Controlla.", + "freezer_expired_old": "{name} in freezer è scaduto da troppo tempo. Meglio buttarlo.", + "fridge_expired_one": "Hai 1 prodotto scaduto in frigo!", + "fridge_expired_many": "Hai {n} prodotti scaduti in frigo!", + "expiring_today": "{name} scade oggi! Usalo subito.", + "expiring_tomorrow": "{name} scade domani. Pensaci!", + "expiring_days": "{name} scade tra {days} giorni.", + "expiring_many": "Hai {n} prodotti in scadenza ravvicinata.", + "expiring_this_week": "Questa settimana scadono {n} prodotti. Pianifica i pasti di conseguenza!", + "expiring_item_loc": "{name} ({loc}) scade tra {days} {dayslabel}.", + "expiring_this_month": "In questo mese scadranno {n} prodotti.", + "shopping_add": "Metti in lista: {names} 🛒", + "shopping_more": "e altri {n}", + "shopping_empty": "Lista della spesa vuota. Tutto rifornito! ✅", + "in_fridge": "In frigo c'è: {name}.", + "in_freezer": "Nel freezer c'è: {name}. Non dimenticartelo!", + "top_category": "La categoria più presente è {icon} {cat} con {n} prodotti.", + "cat_meat": "Hai {n} prodotti di carne. 🥩", + "cat_dairy": "Hai {n} latticini in casa. 🥛", + "cat_veggies": "Hai {n} tipi di verdura. Ottimo per la salute! 🥬", + "cat_fruit": "Hai {n} tipi di frutta. 🍎", + "cat_drinks": "Hai {n} bevande disponibili. 🥤", + "cat_frozen": "Hai {n} surgelati nel freezer. ❄️", + "cat_pasta": "Hai {n} tipi di pasta. 🍝 Che ne dici di una carbonara?", + "cat_canned": "Hai {n} conserve in dispensa. 🥫", + "cat_snacks": "Hai {n} snack. Resisti alla tentazione! 🍪", + "cat_condiments": "Hai {n} condimenti a disposizione. 🧂", + "item_random": "Lo sapevi? Hai {name} in {loc}.", + "item_qty": "{name}: ne hai {qty}.", + "no_expiry_count": "{n} prodotti non hanno una data di scadenza impostata.", + "furthest_expiry": "Il prodotto con scadenza più lontana è {name}: {months} mesi.", + "high_qty": "Hai una bella scorta di {name}: {qty}!", + "low_qty_item": "{name} sta per finire. Aggiungilo alla spesa?", + "low_qty_count": "Ci sono {n} prodotti quasi finiti.", + "morning_bread": "Buongiorno! Hai del pane per la colazione. 🍞", + "morning_milk": "C'è del latte in frigo per il cappuccino? ☕🥛", + "morning_fruit": "Buongiorno! Una bella frutta fresca per iniziare bene. 🍎", + "noon_pasta": "Ora di pranzo… Un bel piatto di pasta? 🍝", + "noon_salad": "Un'insalata fresca per pranzo? Hai {n} verdure! 🥗", + "evening_meat": "Per cena potresti usare la carne che hai. 🥩", + "evening_fish": "Che ne dici di pesce per cena? 🐟", + "evening_expiring": "Hai {n} prodotti in scadenza questa settimana — usali stasera!", + "night_reminder": "Buonanotte! Domani ricordati di usare: {names}.", + "weekly_balance": "Bilancio settimana: +{in} aggiunti, −{out} consumati.", + "weekly_added": "Questa settimana hai aggiunto {n} prodotti.", + "weekly_consumed": "Questa settimana hai consumato {n} prodotti. Ottimo!", + "tip_freezer": "💡 I prodotti in freezer durano molto più a lungo della data di scadenza.", + "tip_bread": "💡 Il pane congelato mantiene la fragranza per settimane.", + "tip_fifo": "💡 Per evitare sprechi, usa prima i prodotti con scadenza più vicina (FIFO).", + "tip_meat": "💡 La carne in freezer può durare fino a 6 mesi senza problemi.", + "tip_no_refreeze": "💡 Non ricongelare mai un alimento già scongelato. Cucinalo subito!", + "tip_fridge": "💡 Un frigo ordinato ti fa risparmiare tempo e denaro.", + "tip_canned": "💡 Le conserve aperte vanno in frigo e consumate in pochi giorni.", + "top_brand": "Il marchio più presente nella tua dispensa è {brand} con {n} prodotti.", + "combo_pasta": "Hai pasta e condimenti: sei pronto per un primo piatto! 🍝", + "combo_sandwich": "Pane e carne: un panino veloce è sempre una buona idea! 🥪", + "combo_balanced": "Verdura e carne: hai tutto per un piatto equilibrato! 🥗🥩", + "pantry_empty": "La dispensa è vuota! Fai una bella spesa. 🛒", + "pantry_empty_scan": "Nessun prodotto registrato. Scansiona qualcosa per iniziare!", + "location_distribution": "Distribuzione: {parts}", + "day": "giorno", + "days": "giorni" + }, + "kiosk": { + "check_btn": "🔍 Cerca aggiornamenti", + "checking": "⏳ Controllo…", + "error_check": "Errore durante il controllo", + "error_start_install": "Errore avvio installazione", + "version_installed": "Installata: {v}", + "update_available": "⬆️ Nuova versione disponibile: {latest} (installata: {current})", + "up_to_date": "✅ Sei già aggiornato — versione {v}", + "too_old": "⚠️ Il kiosk installato è troppo vecchio per il controllo automatico.
Premi il pulsante qui sotto per scaricare e installare la nuova versione direttamente.", + "manual_install": "⚠️ Questo kiosk non supporta l'installazione automatica.
Procedura manuale:
1. Esci dal kiosk (tasto ✕ in alto a sinistra)
2. Disinstalla l'app EverShelf Kiosk
3. Scarica e installa la nuova APK da GitHub:", + "starting_download": "⏳ Avvio download…", + "install_btn": "⬇️ Installa aggiornamento", + "exit_title": "Esci dal kiosk", + "refresh_title": "Aggiorna pagina" + }, + "update": { + "new_version": "Nuova versione", + "btn": "Aggiorna" + }, + "gemini": { + "chat_title": "Chat con Gemini", + "not_configured": "🤖 Gemini non configurato — imposta GEMINI_API_KEY nelle impostazioni" + }, + "appliances": { + "empty": "Nessun elettrodomestico aggiunto" + } } \ No newline at end of file