diff --git a/CHANGELOG.md b/CHANGELOG.md index d7aec84..37dd1a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Recipe scraps tips** — During cooking steps, detect "waste" generated (peels, cores, bones, eggshells, coffee grounds, citrus zest, etc.) and surface AI-powered tips on how to reuse them (compost, natural cleaner, broth, candied peel, etc.). Could be shown as an optional collapsible hint card below the step that generates the scrap. +## [1.7.34] - 2026-05-30 + +### Added +- **AI visual barcode fallback** — When the barcode scanner fails to read a barcode within 5 seconds, EverShelf can now automatically capture a camera frame and send it to Gemini Vision to visually identify the product (name, brand, category). On success the product is saved and the inventory form opens just as if a barcode had been scanned. A new toggle in **Settings → Camera** (`AI visual identification (5s fallback)`) lets users enable or disable this feature at any time. Requires Gemini API key configured. Disabled by default. + ## [1.7.33] - 2026-05-29 ### Fixed diff --git a/api/index.php b/api/index.php index f28a7c4..775f626 100644 --- a/api/index.php +++ b/api/index.php @@ -604,7 +604,7 @@ function checkRateLimit(string $action): void { } // Determine limit based on action - $aiActions = ['gemini_readExpiry', 'gemini_chat', 'gemini_identify', 'gemini_suggest_shopping', 'chat_to_recipe', 'recipe_from_ingredient', 'gemini_number_ocr']; + $aiActions = ['gemini_readExpiry', 'gemini_chat', 'gemini_identify', 'gemini_suggest_shopping', 'chat_to_recipe', 'recipe_from_ingredient', 'gemini_number_ocr', 'gemini_barcode_visual']; $loginActions = []; $recipeActions = ['generate_recipe', 'generate_recipe_stream']; $errorActions = ['report_error', 'check_update']; @@ -1109,6 +1109,10 @@ try { geminiNumberOCR(); break; + case 'gemini_barcode_visual': + geminiBarcodeVisual(); + break; + case 'get_shopping_price': getShoppingPrice($db); break; @@ -4353,6 +4357,7 @@ function getServerSettings(): void { 'shopping_forecast' => env('SHOPPING_FORECAST', 'true') === 'true', 'shopping_auto_add_threshold' => (int)env('SHOPPING_AUTO_ADD_THRESHOLD', '0'), 'dark_mode' => env('DARK_MODE', 'auto'), + 'barcode_ai_fallback' => env('BARCODE_AI_FALLBACK', 'false') === 'true', // Home Assistant Integration 'ha_enabled' => env('HA_ENABLED', 'false') === 'true', 'ha_url' => env('HA_URL', ''), @@ -4455,6 +4460,7 @@ function saveSettings(): void { 'shopping_enabled' => 'SHOPPING_ENABLED', 'shopping_smart_suggestions' => 'SHOPPING_SMART_SUGGESTIONS', 'shopping_forecast' => 'SHOPPING_FORECAST', + 'barcode_ai_fallback' => 'BARCODE_AI_FALLBACK', // Home Assistant 'ha_enabled' => 'HA_ENABLED', ]; @@ -10494,6 +10500,101 @@ function geminiNumberOCR(): void { } } +// ============================================================================= +// ===== GEMINI AI: BARCODE VISUAL FALLBACK ==================================== +// ============================================================================= +/** + * POST /api/?action=gemini_barcode_visual + * Body: { image: base64-jpeg, lang: 'it'|'en'|'de'|... } + * Returns: { found, source, product } or { found: false, error } + * Uses Gemini vision to visually identify a product from a camera frame + * when the barcode scanner fails to read the barcode after 5 seconds. + */ +function geminiBarcodeVisual(): void { + EverLog::info('geminiBarcodeVisual'); + $apiKey = env('GEMINI_API_KEY'); + if (empty($apiKey)) { + echo json_encode(['found' => false, 'error' => 'no_api_key']); + return; + } + + $input = json_decode(file_get_contents('php://input'), true) ?? []; + $imageBase64 = $input['image'] ?? ''; + $lang = $input['lang'] ?? 'it'; + if (empty($imageBase64)) { + echo json_encode(['found' => false, 'error' => 'no_image']); + return; + } + + $langNote = match($lang) { + 'de' => 'Use the German product name if known.', + 'fr' => 'Use the French product name if known.', + 'es' => 'Use the Spanish product name if known.', + default => 'Use the Italian product name if known.', + }; + + $payload = [ + 'contents' => [[ + 'parts' => [ + ['text' => "Identify the product shown in this image. {$langNote}\n" . + "Respond with ONLY valid JSON (no markdown, no backticks):\n" . + "{\"name\":\"...\",\"brand\":\"...\",\"category\":\"...\"}\n" . + "- name: the product name (as specific as possible, not just the brand)\n" . + "- brand: the brand/manufacturer, or empty string if not visible\n" . + "- category: one of: latticini, pasta, bevande, snack, carne, pesce, " . + "frutta, verdura, surgelati, condimenti, conserve, cereali, pane, " . + "igiene, pulizia, altro\n" . + "If you cannot identify the product at all, respond with: {\"unknown\":true}"], + ['inline_data' => ['mime_type' => 'image/jpeg', 'data' => $imageBase64]], + ], + ]], + 'generationConfig' => [ + 'temperature' => 0, + 'maxOutputTokens' => 200, + 'responseMimeType' => 'application/json', + 'thinkingConfig' => ['thinkingBudget' => 0], + ], + ]; + + $result = callGeminiWithFallback($apiKey, $payload, 15, 'barcode_visual'); + if ($result['http_code'] !== 200) { + echo json_encode(['found' => false, 'error' => 'gemini_error_' . $result['http_code']]); + return; + } + + $text = trim($result['data']['candidates'][0]['content']['parts'][0]['text'] ?? ''); + // Strip accidental markdown fences + $text = preg_replace('/^```json\s*/i', '', $text); + $text = preg_replace('/\s*```$/i', '', trim($text)); + + $data = json_decode($text, true); + if (!$data || !empty($data['unknown']) || empty($data['name'])) { + echo json_encode(['found' => false]); + return; + } + + echo json_encode([ + 'found' => true, + 'source' => 'gemini_visual', + 'product' => [ + 'name' => $data['name'] ?? '', + 'brand' => $data['brand'] ?? '', + 'category' => $data['category'] ?? '', + 'image_url' => '', + 'quantity_info' => '', + 'nutriscore' => '', + 'ingredients' => '', + 'allergens' => '', + 'conservation' => '', + 'origin' => '', + 'nova_group' => '', + 'ecoscore' => '', + 'labels' => '', + 'stores' => '', + ], + ], JSON_UNESCAPED_UNICODE); +} + // ============================================================================= // ===== GEMINI AI: ANOMALY EXPLANATION ======================================= // ============================================================================= diff --git a/assets/js/app.js b/assets/js/app.js index 34d03d3..9c28947 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1943,6 +1943,7 @@ let quaggaRunning = false; let aiStream = null; let _scanZoomLevel = 2; // always 2x let _torchActive = false; +let _aiFallbackTimer = null; // Apply fixed 2x zoom (hardware if available, CSS fallback) async function _applyFixedZoom() { @@ -2111,6 +2112,85 @@ async function _tryGeminiNumberOCR() { } } +// ===== AI VISUAL PRODUCT IDENTIFICATION (auto-fallback after 5s) ===== +let _aiBarcodeVisualRunning = false; +async function _tryGeminiVisualBarcode() { + if (_aiBarcodeVisualRunning || !_requireGemini()) return; + const video = document.getElementById('scanner-video'); + if (!video || !video.videoWidth) return; + + _aiBarcodeVisualRunning = true; + stopScanner(); // stop scanner loop while AI processes + _setScanStatus(t('scan.ai_fallback_searching'), 'retry', 'Gemini Vision'); + showLoading(true); + + try { + const canvas = document.createElement('canvas'); + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + canvas.getContext('2d').drawImage(video, 0, 0); + const imageBase64 = canvas.toDataURL('image/jpeg', 0.88).split(',')[1]; + + const result = await api('gemini_barcode_visual', {}, 'POST', { + image: imageBase64, + lang: _currentLang || 'it', + }); + + if (result.found && result.product) { + const p = result.product; + scanLog(`AI visual: found "${p.name}" (${p.brand})`); + showToast(t('scan.ai_fallback_found'), 'success'); + // Build a synthetic product (no barcode) and show the inventory form + const saveResult = await api('product_save', {}, 'POST', { + barcode: '', + name: p.name || t('product.not_recognized'), + brand: p.brand || '', + category: p.category || '', + image_url: '', + unit: 'pz', + default_quantity: 1, + package_unit: '', + notes: '', + }); + if (saveResult.id) { + currentProduct = { + id: saveResult.id, + barcode: '', + name: p.name || t('product.not_recognized'), + brand: p.brand || '', + category: p.category || '', + image_url: '', + unit: 'pz', + default_quantity: 1, + package_unit: '', + _confCount: 0, + weight_info: '', + }; + addToScanRecents(currentProduct); + showLoading(false); + setTimeout(() => showProductAction(), 300); + } else { + showLoading(false); + showToast(t('error.connection'), 'error'); + } + } else { + scanLog('AI visual: product not identified'); + showLoading(false); + showToast(t('scan.ai_fallback_not_found'), 'warning'); + _setScanStatus(t('scan.status_ready'), '', ''); + // Restart scanner so user can try again + setTimeout(() => initScanner(), 300); + } + } catch (e) { + scanLog(`AI visual error: ${e.message}`); + showLoading(false); + showToast(t('error.connection'), 'error'); + setTimeout(() => initScanner(), 300); + } finally { + _aiBarcodeVisualRunning = false; + } +} + // ===== CAMERA HELPER ===== function getCameraConstraints(extraVideo = {}) { const s = getSettings(); @@ -2296,6 +2376,7 @@ function _applySyncedSettings(serverSettings) { 'shopping_enabled','shopping_mode','shopping_smart_suggestions', 'shopping_forecast','shopping_auto_add_threshold', 'dark_mode', + 'barcode_ai_fallback', // Home Assistant 'ha_enabled','ha_url','ha_tts_entity','ha_webhook_id','ha_webhook_events', 'ha_notify_service','ha_expiry_days']; @@ -2887,6 +2968,8 @@ async function loadSettingsUI() { const cameraSelect = document.getElementById('setting-camera-facing'); if (cameraSelect) cameraSelect.value = s.camera_facing || 'environment'; loadCameraDevices(); + const baifEl = document.getElementById('setting-barcode-ai-fallback'); + if (baifEl) baifEl.checked = s.barcode_ai_fallback === true; renderAppliances(s.appliances || []); const mealPlanEnabled = s.meal_plan_enabled !== false; const mpEnabledEl = document.getElementById('setting-meal-plan-enabled'); @@ -3465,6 +3548,8 @@ async function saveSettings() { s.dietary = document.getElementById('setting-dietary').value.trim(); // Camera s.camera_facing = document.getElementById('setting-camera-facing').value; + const baifSave = document.getElementById('setting-barcode-ai-fallback'); + if (baifSave) s.barcode_ai_fallback = baifSave.checked; // Screensaver const ssEl = document.getElementById('setting-screensaver-enabled'); if (ssEl) s.screensaver_enabled = ssEl.checked; @@ -3608,6 +3693,7 @@ async function saveSettings() { shopping_forecast: s.shopping_forecast !== false, shopping_auto_add_threshold: s.shopping_auto_add_threshold || 0, dark_mode: s.dark_mode || 'auto', + barcode_ai_fallback: !!s.barcode_ai_fallback, // Home Assistant ha_enabled: !!s.ha_enabled, ha_url: s.ha_url || '', @@ -6527,6 +6613,17 @@ async function initScanner() { } }, 4000); } + + // After 5s without a scan, auto-trigger AI visual identification (if enabled) + if (_geminiAvailable && getSettings().barcode_ai_fallback) { + clearTimeout(_aiFallbackTimer); + _aiFallbackTimer = setTimeout(() => { + if (scannerStream) { // still scanning — no barcode found yet + scanLog('5s elapsed without barcode — triggering AI visual fallback'); + _tryGeminiVisualBarcode(); + } + }, 5000); + } } catch (err) { scanLog(`CAMERA ERROR: ${err.name}: ${err.message}`); @@ -6850,6 +6947,7 @@ function stopScanner() { quaggaRunning = false; _scanZoomLevel = 2; // always 2x on next start _torchActive = false; + clearTimeout(_aiFallbackTimer); _aiFallbackTimer = null; if (scannerStream) { scannerStream.getTracks().forEach(t => t.stop()); scannerStream = null; @@ -6871,6 +6969,7 @@ function stopScanner() { } async function onBarcodeDetected(barcode) { + clearTimeout(_aiFallbackTimer); _aiFallbackTimer = null; showLoading(true); // Vibrate if available diff --git a/index.html b/index.html index 74cb570..f142861 100644 --- a/index.html +++ b/index.html @@ -1183,6 +1183,16 @@

Se hai più fotocamere, puoi selezionarne una specifica dall'elenco sopra dopo aver concesso i permessi.

+
+ +

Se il codice a barre non viene letto entro 5 secondi, un fotogramma viene inviato automaticamente all'AI per identificare visivamente il prodotto. Richiede Gemini configurato.

+
diff --git a/translations/de.json b/translations/de.json index 8570134..13492c0 100644 --- a/translations/de.json +++ b/translations/de.json @@ -1,1458 +1,1463 @@ { - "app": { - "name": "EverShelf", - "loading": "Laden..." - }, - "nav": { - "title": "EverShelf", - "home": "Home", - "inventory": "Vorrat", - "recipes": "Rezepte", - "shopping": "Einkauf", - "log": "Verlauf", - "settings": "Einstellungen" - }, - "btn": { - "back": "← Zurück", - "save": "💾 Speichern", - "cancel": "✕ Abbrechen", - "close": "Schließen", - "add": "✅ Hinzufügen", - "delete": "Löschen", - "edit": "✏️ Bearbeiten", - "use": "Verwenden", - "edit_item": "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", - "yes_short": "Ja", - "no_short": "Nein" - }, - "form": { - "select_placeholder": "-- Auswählen --" - }, - "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", - "millilitres": "Milliliter", - "from": "von" - }, - "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": "Verbrauch zur Prüfung", - "banner_prediction_hint": "Die Verbrauchsschätzung passt sich aktuellen Daten an: bitte nur die aktuelle Menge bestätigen.", - "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_finished": "Habe ich verbraucht!", - "banner_expired_action_throw": "Habe ich weggeworfen", - "banner_expired_action_edit": "Datum korrigieren", - "banner_expired_action_modify": "Bearbeiten", - "banner_expired_action_vacuum": "Vakuumieren", - "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": "frühere Schätzung: {expected} {unit}{time}; aktuelle Menge: {actual} {unit}.", - "banner_prediction_less": "Schätzung: {expected} {unit}{time}; aktuelle Menge: {actual} {unit}. Wenn sich dein Verbrauch geändert hat, aktualisiert sich die Prognose automatisch.", - "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_opened_detail": "{when} in {location} · 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": "Scannen", - "mode_shopping": "🛒 Einkaufsmodus", - "mode_shopping_end": "✅ Einkauf beenden", - "spesa_btn": "🛒 Einkauf", - "zoom": "Zoom", - "tab_barcode": "Barcode", - "tab_name": "Name", - "tab_ai": "KI", - "recents_label": "Zuletzt", - "torch_hint": "Taschenlampe", - "torch_on": "Taschenlampe an", - "torch_off": "Taschenlampe aus", - "torch_unavailable": "Taschenlampe auf diesem Gerät nicht verfügbar", - "flip_hint": "Kamera wechseln", - "flip_front": "Frontkamera", - "flip_back": "Rückkamera", - "num_ocr_btn": "🔢 Zahlen mit KI lesen", - "num_ocr_searching": "Suche Barcode mit KI...", - "num_ocr_found": "Code gefunden: {code}", - "num_ocr_not_found": "Kein Barcode im Bild gefunden", - "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", - "stock_in_pantry": "Bereits im Vorrat:", - "status_ready": "Kamera auf Barcode richten", - "status_scanning": "Scanne...", - "status_partial": "Erkannt: {code} — prüfe...", - "status_invalid": "Ungültig: {code} — versuche erneut", - "status_confirmed": "Bestätigt!", - "status_parallel": "Kombinierter Scan aktiv..." - }, - "action": { - "title": "Was möchtest du tun?", - "add_btn": "📥 HINZUFÜGEN", - "add_sub": "in Vorrat/Kühlschrank", - "use_btn": "VERWENDEN", - "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", - "related_stock_title": "Auch zuhause" - }, - "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", - "vacuum_question": "Vakuumiert?", - "vacuum_saved": "🔒 Als vakuumiert gespeichert" - }, - "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}!", - "expiry_warning_opened": "⚠️ Die{loc} ist seit {when} geöffnet — zuerst verwenden!", - "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", - "unknown_product": "Unbekanntes Produkt", - "edit_name_brand": "Name/Marke bearbeiten", - "weight_label": "Gewicht", - "origin_label": "Herkunft", - "labels_label": "Etiketten", - "select_variant": "Genaue Variante auswählen oder KI-Daten nutzen:" - }, - "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", - "regen_choice_title": "Was möchtest du mit diesem Rezept machen?", - "regen_replace": "🔄 Neues generieren (dieses verwerfen)", - "regen_save_new": "💾 Im Archiv speichern & neues generieren", - "close_btn": "✅ Schließen", - "ingredients_title": "🧾 Zutaten", - "tools_title": "Benötigte Geräte", - "steps_title": "👨‍🍳 Zubereitung", - "no_steps": "Keine Zubereitungsschritte verfügbar", - "generate_error": "Fehler bei der Generierung", - "stream_interrupted": "Generierung unterbrochen (unvollstaendige Antwort vom Server). Protokolle pruefen oder erneut versuchen.", - "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", - "favorite": "Zu Favoriten hinzufügen", - "unfavorite": "Aus Favoriten entfernen", - "adjust_persons": "Personen" - }, - "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", - "pantry_hint": "Bereits zuhause: {qty}" - }, - "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", - "recipe_prefix": "Rezept" - }, - "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", - "quick_recipe_prompt": "Schlage mir ein schnelles Rezept FÜR EINE PERSON vor, das die Produkte mit dem nächsten Ablaufdatum verwendet! Ignoriere Tiefkühlprodukte, konzentriere dich auf Kühlschrank und Vorratsschrank." - }, - "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": "Usa", - "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", - "step_fallback": "Schritt {n}", - "zerowaste_label": "♻️ Abfall", - "zerowaste_tip_title": "Zero-Waste-Tipp" - }, - "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", - "reset_btn": "↺ Standard wiederherstellen" - }, - "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", - "token_title": "🔑 Einstellungs-Token", - "token_label": "Zugriffstoken", - "token_hint": "Falls `SETTINGS_TOKEN` in der Server-`.env` konfiguriert ist, gib hier den Token ein, bevor du die Einstellungen speicherst. Leer lassen, wenn nicht konfiguriert.", - "token_placeholder": "(leer = kein Schutz)", - "token_required_hint": "🔒 Dieser Server benötigt einen Token zum Speichern der Einstellungen.", - "cert_instructions": "Anleitung für Chrome (Android):
1. Zertifikat oben herunterladen
2. Gehe zu Einstellungen → Sicherheit & Datenschutz → Weitere Sicherheitseinstellungen → Vom Gerätespeicher installieren
3. Wähle die heruntergeladene EverShelf_CA.crt Datei
4. Wähle \"CA\" und bestätige
5. Chrome neu starten

Anleitung für Chrome (PC):
1. Zertifikat oben herunterladen
2. Gehe zu chrome://settings/certificates (oder Einstellungen → Datenschutz und Sicherheit → Sicherheit → Zertifikate verwalten)
3. Tab \"Zertifizierungsstellen\" → Importieren → Datei auswählen
4. Häkchen bei \"Dieser Zertifizierungsstelle für die Identifikation von Webseiten vertrauen\"
5. Chrome neu starten" - }, - "tts": { - "title": "🔊 Sprache & TTS", - "hint": "Sprachsynthese über externe REST-API konfigurieren. Rezeptschritte und abgelaufene Timer werden an den Endpunkt gesendet.", - "enabled": "✅ TTS aktivieren", - "engine_label": "⚙️ TTS-Engine", - "engine_browser": "🔇 Browser (offline, keine Konfiguration erforderlich)", - "engine_server": "🌐 Externer Server (Home Assistant, REST API...)", - "voice_label": "🗣️ Stimme", - "rate_label": "⚡ Geschwindigkeit", - "pitch_label": "🎵 Tonhöhe", - "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_sound_btn": "🔔 Klangtest ausführen", - "test_btn": "🔊 Testansage senden", - "voices_loading": "Stimmen werden geladen…", - "voice_not_supported": "Stimme vom Browser nicht unterstützt", - "voices_none": "Keine Stimmen auf diesem Gerät verfügbar", - "voices_hint": "Verfügbare Stimmen hängen vom Betriebssystem und Browser ab. Auf macOS/iOS ist die Stimme Paola (Italienisch) verfügbar. Drücken Sie ↺ wenn die Liste nicht lädt.", - "url_missing": "⚠️ Endpunkt-URL fehlt.", - "test_sending": "⏳ Wird gesendet…", - "test_ok": "✅ Antwort {code} — prüfe ob der Lautsprecher gesprochen hat.", - "heard_question": "Hast du die Stimme gehört?", - "heard_yes": "Ja, ich habe es gehört", - "heard_no": "Nein, ich habe nichts gehört", - "test_ok_kiosk": "TTS funktioniert.", - "test_fail_steps": "Prüfe: 1) Medienvolume ist nicht 0; 2) Google Text-to-Speech installiert und aktualisiert; 3) Deutsches Sprachpaket in den Android TTS-Einstellungen heruntergeladen." - }, - "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.", - "timeout_1": "1 Minute", - "timeout_2": "2 Minuten", - "timeout_5": "5 Minuten", - "timeout_10": "10 Minuten", - "timeout_15": "15 Minuten", - "timeout_30": "30 Minuten", - "timeout_60": "1 Stunde", - "start_after": "⏱️ Starten nach" - }, - "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", - "live_weight": "Echtzeit-Gewicht", - "auto_reconnect": "🔁 Verbindung: automatisch", - "kiosk_title": "📡 BLE-Waage im Kiosk integriert", - "kiosk_hint": "Die Waage wird direkt vom internen BLE-Gateway des Kiosks verwaltet. Um ein neues Gerät zu koppeln, verwende den Konfigurationsassistenten.", - "kiosk_reconfigure": "🔄 BLE-Waage neu konfigurieren", - "ble_protocols": "

🔌 Unterstützte BLE-Protokolle:

" - }, - "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/", - "native_title": "Kiosk-Konfiguration", - "native_hint": "Server-URL, BLE-Waage, Bildschirmschoner und Einrichtungsassistent.", - "native_btn": "Kiosk-Konfiguration öffnen", - "native_tap_hint": "Zahnrad oben rechts antippen", - "native_update_hint": "Kiosk-App aktualisieren, um diese Funktion zu nutzen", - "update_title": "Kiosk-Aktualisierung", - "check_updates_btn": "🔍 Nach Updates suchen", - "needs_update": "⚠️ Das installierte Kiosk unterstützt diese Funktion nicht. Aktualisiere die Kiosk-App, um sie zu aktivieren." - }, - "saved": "✅ Konfiguration gespeichert!", - "saved_local": "✅ Konfiguration lokal gespeichert", - "saved_local_error": "⚠️ Lokal gespeichert, Serverfehler: {error}", - "theme": { - "title": "🌙 Erscheinungsbild", - "hint": "Wähle das Interface-Design.", - "label": "🌙 Design", - "off": "☀️ Hell", - "on": "🌙 Dunkel", - "auto": "🔄 Automatisch (Tageszeit)" - }, - "zerowaste": { - "card_title": "♻️ Zero-Waste-Tipps", - "card_hint": "Zeige während des Kochens Tipps zur Wiederverwendung von Abfällen (Schalen, Kochwasser usw.). Standardmäßig deaktiviert.", - "label": "Tipps beim Kochen anzeigen" - }, - "backup": { - "tab": "Backup", - "local_title": "Lokales Backup", - "local_hint": "Täglicher Datenbank-Snapshot. Konfiguriere, wie viele Tage Backups aufbewahrt werden.", - "enabled": "Tägliches automatisches Backup aktivieren", - "retention_days": "Aufbewahrung (Tage)", - "retention_info": "Backups werden aufbewahrt für", - "backup_now": "Jetzt sichern", - "backing_up": "Sicherung läuft…", - "backed_up": "Sicherung abgeschlossen", - "backup_error": "Sicherungsfehler", - "last_backup": "Letztes Backup", - "no_backup_yet": "Noch kein Backup erstellt", - "list_empty": "Keine Backups verfügbar", - "restore_btn": "Wiederherstellen", - "restore_confirm": "Backup wiederherstellen", - "delete_btn": "Löschen", - "delete_confirm": "Backup löschen", - "gdrive_title": "Google Drive", - "gdrive_hint": "Backups automatisch via OAuth 2.0 auf Google Drive hochladen. Keine externen Bibliotheken erforderlich.", - "gdrive_enabled": "Google Drive Backup aktivieren", - "gdrive_folder_id": "Drive-Ordner-ID", - "gdrive_folder_id_hint": "Kopiere die ID aus der Drive-Ordner-URL: …/folders/ID", - "gdrive_retention_days": "Drive-Aufbewahrung (Tage, 0=alles behalten)", - "gdrive_test": "Verbindung testen", - "gdrive_ok": "Verbindung erfolgreich!", - "gdrive_error": "Verbindung fehlgeschlagen", - "gdrive_push_now": "Jetzt auf Drive hochladen", - "gdrive_pushing": "Wird hochgeladen…", - "gdrive_pushed": "Auf Drive hochgeladen", - "gdrive_wizard_hint": "Optional: täglich automatisch via OAuth 2.0 auf Google Drive sichern.", - "gdrive_skip": "Überspringen — später in Einstellungen konfigurieren", - "gdrive_client_id": "Client-ID", - "gdrive_client_secret": "Client-Secret", - "gdrive_redirect_uri_hint": "Füge http://localhost als autorisierten Weiterleitungs-URI in der Google Cloud Console hinzu. Funktioniert auf jedem Server, auch ohne öffentliche Domain.", - "gdrive_code_title": "Autorisierungs-URL oder Code einfügen", - "gdrive_code_hint": "Nach der Autorisierung öffnet der Browser http://localhost und zeigt möglicherweise einen Verbindungsfehler — das ist normal. Kopiere die URL aus der Adressleiste (z.B. http://localhost/?code=4%2F0A...) und füge sie hier ein.", - "gdrive_code_submit": "Bestätigen", - "gdrive_code_empty": "Bitte zuerst die URL oder den Autorisierungscode einfügen", - "gdrive_redirect_uri_label": "Redirect-URI (in Google Cloud Console eintragen):", - "gdrive_oauth_authorize": "Mit Google autorisieren", - "gdrive_oauth_authorized": "Autorisiert", - "gdrive_oauth_not_authorized": "Noch nicht autorisiert", - "gdrive_oauth_window_opened": "Browserfenster geöffnet — autorisieren und zurückkehren", - "gdrive_oauth_how_to": "OAuth 2.0 einrichten (Schritt für Schritt)", - "gdrive_oauth_steps": "
  • Gehe zu console.cloud.google.com und wähle dein Projekt
  • Aktiviere die Google Drive API: APIs & Dienste → APIs aktivieren → Google Drive API
  • Gehe zu APIs & Dienste → Anmeldedaten → Anmeldedaten erstellen → OAuth-Client-ID
  • Anwendungstyp: Webanwendung; füge die unten angezeigte URL als Autorisierter Weiterleitungs-URI hinzu
  • Kopiere Client-ID und Client-Secret in die Felder oben und speichere
  • Klicke auf Mit Google autorisieren: melde dich an und erteile den Zugriff
  • Das Fenster schließt sich automatisch und Backups sind bereit
  • " - }, - "info": { - "tab": "Info", - "ai_title": "Gemini AI — Token-Nutzung", - "ai_hint": "Monatlicher Verbrauch und geschätzte Kosten für den aktuellen API-Schlüssel.", - "loading": "Laden…", - "total_tokens": "Token gesamt", - "est_cost": "Gesch. Kosten", - "input_tok": "Eingabe-Token", - "output_tok": "Ausgabe-Token", - "ai_calls": "Aufrufe", - "by_action": "Aufschlüsselung nach Funktion", - "by_model": "Aufschlüsselung nach Modell", - "pricing_note": "Gemini Referenzpreise: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.", - "system_title": "System", - "db_size": "Datenbank", - "log_size": "Protokolle", - "log_level": "Log-Level", - "ai_overview": "KI-Nutzungsübersicht, Inventar und Systemstatus", - "calls_unit": "Aufrufe", - "inv_title": "Inventar", - "inv_active": "Aktiv", - "inv_products": "Produkte gesamt", - "inv_expiring": "Ablaufend (7T)", - "inv_expired": "Abgelaufen", - "inv_finished": "Leer", - "act_title": "Monatliche Aktivität", - "act_tx_month": "Bewegungen", - "act_restock": "Einkäufe", - "act_use": "Verbrauch", - "act_new_products": "Neue Produkte", - "act_tx_year": "Jährl. Bewegungen", - "price_cache": "Preiscache", - "cache_entries": "Produkte", - "last_backup": "Letztes Backup", - "bring_days": "Token läuft in {n} Tagen ab", - "bring_expired": "Token abgelaufen", - "year_label": "Jahr {year}", - "currency_title": "Währung", - "currency_hint": "Die Währung, die für alle Kosten und Preise in der App verwendet wird." - }, - "tab_general": "Allgemein", - "shopping": { - "tab": "Einkaufsliste", - "title": "Einkaufsliste", - "hint": "Konfiguriere die integrierte Einkaufsliste oder verbinde Bring!.", - "enable_label": "Einkaufsliste aktivieren", - "mode_label": "Anbieter", - "mode_internal": "Intern (ohne Bring!)", - "mode_bring": "Bring! (externe App)", - "bring_section_title": "Bring!-Konfiguration", - "ai_section_title": "KI-Unterstützung", - "smart_suggestions_label": "KI-Vorschläge", - "forecast_label": "Prognose für bald leere Produkte", - "auto_add_label": "Automatisch hinzufügen wenn", - "auto_add_suffix": "im Lager verbleibend (0 = nur wenn leer)" - }, - "ha": { - "tab": "Home Assistant", - "title": "Home Assistant", - "hint": "Verbinde EverShelf mit Home Assistant für Automationen, Push-Benachrichtigungen und REST-Sensoren.", - "enabled": "Home Assistant-Integration aktivieren", - "connection_title": "Verbindung", - "url_label": "Home Assistant URL", - "url_placeholder": "http://192.168.1.50:8123", - "url_hint": "Basis-URL deiner Home Assistant-Instanz (z.B. http://homeassistant.local:8123).", - "token_label": "Long-Lived Access Token", - "token_hint": "Erstelle unter HA-Profil → Sicherheit → Langlebige Zugangstoken.", - "token_placeholder": "eyJhbGci...", - "token_saved": "Token gespeichert (aus Sicherheitsgründen verborgen)", - "test_btn": "Verbindung testen", - "test_ok": "Verbunden mit {version}", - "test_fail": "Verbindung fehlgeschlagen: {error}", - "test_bad_token": "HA erreichbar, aber Token ist ungültig", - "testing": "Teste…", - "error_no_url": "Bitte zuerst die Home Assistant URL eingeben.", - "tts_title": "TTS auf Smart Speaker", - "tts_hint": "Rezeptschritte auf einem Home Assistant Media Player vorlesen.", - "tts_entity_label": "Media Player Entity ID", - "tts_entity_placeholder": "media_player.wohnzimmer", - "tts_entity_hint": "Entity-ID des HA-Media-Players. Zu finden unter HA: Entwicklertools → Zustände.", - "tts_platform_label": "TTS-Plattform", - "tts_platform_speak": "tts.speak (empfohlen)", - "tts_platform_notify": "notify.* (Benachrichtigungsdienst)", - "tts_apply_btn": "HA-Voreinstellung auf TTS-Tab anwenden", - "tts_apply_hint": "Füllt den TTS-Tab mit der Home Assistant URL und dem Token aus.", - "tts_preset_applied": "HA-Voreinstellung auf TTS-Tab angewendet.", - "webhook_title": "Webhook-Automationen", - "webhook_hint": "Sende Daten an Home Assistant, wenn Ereignisse in der Vorratskammer auftreten.", - "webhook_id_label": "Webhook-ID", - "webhook_id_placeholder": "evershelf_webhook_abc123", - "webhook_id_hint": "ID des in HA erstellten Webhooks. Kopiere aus: HA → Einstellungen → Automationen → Erstellen → Webhook-Auslöser.", - "webhook_events_label": "Benachrichtige bei diesen Ereignissen", - "event_expiry": "Ablaufende Produkte (täglich)", - "event_shopping": "Artikel zur Einkaufsliste hinzugefügt", - "event_stock": "Lagerbestand aktualisiert", - "expiry_days_label": "Ablaufwarnung im Voraus (Tage)", - "expiry_days_hint": "Sende die Ablaufwarnung N Tage vor dem Ablaufdatum.", - "webhook_help": "In HA: Einstellungen → Automationen → Automation erstellen → Auslöser: Webhook → ID kopieren.", - "notify_title": "Push-Benachrichtigungen", - "notify_hint": "Sende Push-Benachrichtigungen über einen Home Assistant notify-Dienst.", - "notify_service_label": "Notify-Dienst", - "notify_service_placeholder": "notify.mobile_app_mein_handy", - "notify_service_hint": "Name des HA-notify-Dienstes (z.B. notify.mobile_app_phone). Leer lassen zum Deaktivieren.", - "sensor_title": "REST-Sensoren", - "sensor_hint": "Zur configuration.yaml hinzufügen, um EverShelf-Sensoren in Home Assistant zu erstellen.", - "sensor_copy_btn": "YAML kopieren", - "sensor_copied": "YAML in die Zwischenablage kopiert!", - "save_btn": "HA-Einstellungen speichern", - "ha_hint": "Wenn du Home Assistant verwendest, nutze den Home Assistant-Tab für TTS, Webhooks und Sensoren." - } - }, - "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)", - "opened_ago_long": "Seit {n} Tagen geöffnet", - "opened_today_long": "Heute geöffnet", - "opened_suffix": "— Zu lange geöffnet!", - "opened_suffix_ok": "— Geöffnet (noch ok)", - "opened_suffix_warning": "— Geöffnet (erst prüfen)", - "days_compact": "{n}T", - "badge_check_soon": "Bald prüfen" - }, - "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", - "finished_all": "📤 {name} aufgebraucht!", - "vacuum_sealed": "{name} als vakuumversiegelt gespeichert", - "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", - "unknown": "Unbekannter Fehler", - "prefix": "Fehler", - "no_inventory_entry": "Kein Inventareintrag gefunden", - "offline_title": "Keine Verbindung", - "offline_subtitle": "Die App kann den Server nicht erreichen. Überprüfe deine WLAN-Verbindung.", - "offline_checking": "Verbindung prüfen…", - "offline_restored": "Verbindung wiederhergestellt!", - "offline_continue": "Im Offline-Modus fortfahren", - "offline_reading_cache": "Lese aus lokalem Cache", - "offline_ops_pending": "{n} Aktionen ausstehend", - "offline_synced": "{n} Aktionen synchronisiert", - "offline_ai_disabled": "Offline nicht verfügbar", - "offline_cache_ready": "Offline — {n} Produkte im Cache" - }, - "confirm_placeholder_search": null, - "confirm": { - "remove_item": "Möchtest du dieses Produkt wirklich aus dem Bestand entfernen?", - "kiosk_exit": "Kioskmodus verlassen?", - "cancel": "Abbrechen", - "proceed": "Bestätigen", - "discard_one": "1 Stück wegwerfen" - }, - "location": { - "dispensa": "Vorratskammer", - "frigo": "Kühlschrank", - "freezer": "Gefrierschrank" - }, - "edit": { - "title": "{name} bearbeiten", - "unknown_hint": "Produktname und Informationen eingeben", - "label_name": "🏷️ Produktname", - "choose_location_title": "Welchen Ort?", - "choose_location_hint": "Wähle den zu bearbeitenden Ort:", - "confirm_large_qty": "Du setzt die Menge auf {qty} {unit}. Das scheint ungewöhnlich hoch zu sein. Bestätigen?" - }, - "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", - "expiry_found": "Datum gefunden", - "expiry_read_fail": "Datum konnte nicht gelesen werden.", - "expiry_raw_label": "Erkannt" - }, - "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", - "vacuum_seal_rest": "Rest vakuumieren" - }, - "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", - "macros_title": "Geschätzte Makronährstoffe", - "macros_proteins": "Proteine", - "macros_carbs": "Kohlenhydrate", - "macros_fat": "Fett", - "macros_fiber": "Ballaststoffe", - "macros_source": "Schätzung basierend auf {n} Vorratsprodukten" - }, - "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" - }, + "app": { + "name": "EverShelf", + "loading": "Laden..." + }, + "nav": { + "title": "EverShelf", + "home": "Home", + "inventory": "Vorrat", + "recipes": "Rezepte", + "shopping": "Einkauf", + "log": "Verlauf", + "settings": "Einstellungen" + }, + "btn": { + "back": "← Zurück", + "save": "💾 Speichern", + "cancel": "✕ Abbrechen", + "close": "Schließen", + "add": "✅ Hinzufügen", + "delete": "Löschen", + "edit": "✏️ Bearbeiten", + "use": "Verwenden", + "edit_item": "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", + "yes_short": "Ja", + "no_short": "Nein" + }, + "form": { + "select_placeholder": "-- Auswählen --" + }, + "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", + "millilitres": "Milliliter", + "from": "von" + }, + "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": "Verbrauch zur Prüfung", + "banner_prediction_hint": "Die Verbrauchsschätzung passt sich aktuellen Daten an: bitte nur die aktuelle Menge bestätigen.", + "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_finished": "Habe ich verbraucht!", + "banner_expired_action_throw": "Habe ich weggeworfen", + "banner_expired_action_edit": "Datum korrigieren", + "banner_expired_action_modify": "Bearbeiten", + "banner_expired_action_vacuum": "Vakuumieren", + "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": "frühere Schätzung: {expected} {unit}{time}; aktuelle Menge: {actual} {unit}.", + "banner_prediction_less": "Schätzung: {expected} {unit}{time}; aktuelle Menge: {actual} {unit}. Wenn sich dein Verbrauch geändert hat, aktualisiert sich die Prognose automatisch.", + "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_opened_detail": "{when} in {location} · 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": "Scannen", + "mode_shopping": "🛒 Einkaufsmodus", + "mode_shopping_end": "✅ Einkauf beenden", + "spesa_btn": "🛒 Einkauf", + "zoom": "Zoom", + "tab_barcode": "Barcode", + "tab_name": "Name", + "tab_ai": "KI", + "recents_label": "Zuletzt", + "torch_hint": "Taschenlampe", + "torch_on": "Taschenlampe an", + "torch_off": "Taschenlampe aus", + "torch_unavailable": "Taschenlampe auf diesem Gerät nicht verfügbar", + "flip_hint": "Kamera wechseln", + "flip_front": "Frontkamera", + "flip_back": "Rückkamera", + "num_ocr_btn": "🔢 Zahlen mit KI lesen", + "num_ocr_searching": "Suche Barcode mit KI...", + "num_ocr_found": "Code gefunden: {code}", + "num_ocr_not_found": "Kein Barcode im Bild gefunden", + "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", + "stock_in_pantry": "Bereits im Vorrat:", + "status_ready": "Kamera auf Barcode richten", + "status_scanning": "Scanne...", + "status_partial": "Erkannt: {code} — prüfe...", + "status_invalid": "Ungültig: {code} — versuche erneut", + "status_confirmed": "Bestätigt!", + "status_parallel": "Kombinierter Scan aktiv...", + "ai_fallback_searching": "KI identifiziert Produkt...", + "ai_fallback_found": "Produkt von KI erkannt", + "ai_fallback_not_found": "KI: Produkt nicht erkannt" + }, + "action": { + "title": "Was möchtest du tun?", + "add_btn": "📥 HINZUFÜGEN", + "add_sub": "in Vorrat/Kühlschrank", + "use_btn": "VERWENDEN", + "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", + "related_stock_title": "Auch zuhause" + }, + "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", + "vacuum_question": "Vakuumiert?", + "vacuum_saved": "🔒 Als vakuumiert gespeichert" + }, + "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}!", + "expiry_warning_opened": "⚠️ Die{loc} ist seit {when} geöffnet — zuerst verwenden!", + "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", + "unknown_product": "Unbekanntes Produkt", + "edit_name_brand": "Name/Marke bearbeiten", + "weight_label": "Gewicht", + "origin_label": "Herkunft", + "labels_label": "Etiketten", + "select_variant": "Genaue Variante auswählen oder KI-Daten nutzen:" + }, + "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", + "regen_choice_title": "Was möchtest du mit diesem Rezept machen?", + "regen_replace": "🔄 Neues generieren (dieses verwerfen)", + "regen_save_new": "💾 Im Archiv speichern & neues generieren", + "close_btn": "✅ Schließen", + "ingredients_title": "🧾 Zutaten", + "tools_title": "Benötigte Geräte", + "steps_title": "👨‍🍳 Zubereitung", + "no_steps": "Keine Zubereitungsschritte verfügbar", + "generate_error": "Fehler bei der Generierung", + "stream_interrupted": "Generierung unterbrochen (unvollstaendige Antwort vom Server). Protokolle pruefen oder erneut versuchen.", + "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", + "favorite": "Zu Favoriten hinzufügen", + "unfavorite": "Aus Favoriten entfernen", + "adjust_persons": "Personen" + }, + "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", + "pantry_hint": "Bereits zuhause: {qty}" + }, + "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", + "recipe_prefix": "Rezept" + }, + "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", + "quick_recipe_prompt": "Schlage mir ein schnelles Rezept FÜR EINE PERSON vor, das die Produkte mit dem nächsten Ablaufdatum verwendet! Ignoriere Tiefkühlprodukte, konzentriere dich auf Kühlschrank und Vorratsschrank." + }, + "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": "Usa", + "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", + "step_fallback": "Schritt {n}", + "zerowaste_label": "♻️ Abfall", + "zerowaste_tip_title": "Zero-Waste-Tipp" + }, + "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": { - "chat_title": "Mit Gemini chatten", - "not_configured": "🤖 Gemini nicht konfiguriert — GEMINI_API_KEY in den Einstellungen setzen" + "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", + "reset_btn": "↺ Standard wiederherstellen" }, "appliances": { - "empty": "Kein Haushaltsgerät hinzugefügt" + "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" }, - "about": { - "title": "Über", - "version": "Version", - "report_bug": "Fehler melden", - "report_bug_hint": "Etwas funktioniert nicht? Sende uns direkt aus der App eine Meldung.", - "report_bug_modal_title": "Fehler melden", - "report_type_bug": "Fehler", - "report_type_feature": "Funktion", - "report_type_question": "Frage", - "report_field_title": "Titel", - "report_field_title_ph": "Kurze Beschreibung des Problems", - "report_field_desc": "Beschreibung", - "report_field_desc_ph": "Problem detailliert beschreiben…", - "report_field_steps": "Schritte zum Reproduzieren (optional)", - "report_field_steps_ph": "1. Gehe zu…\n2. Tippe auf…\n3. Fehler erscheint…", - "report_auto_info": "Automatisch beigefügt: Version {version}, Sprache {lang}.", - "report_send_btn": "Bericht senden", - "report_bug_sending": "Wird gesendet…", - "report_bug_sent": "Bericht gesendet — danke!", - "report_bug_error": "Bericht konnte nicht gesendet werden. Verbindung prüfen.", - "changelog": "Changelog", - "github": "GitHub-Repository" + "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}" }, - "export": { - "title": "Inventar exportieren", - "hint": "Lade das aktuelle Inventar als CSV herunter oder öffne die druckfertige Version (PDF).", - "btn_csv": "CSV herunterladen", - "btn_pdf": "PDF / Drucken", - "btn_title": "Exportieren" + "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", + "ai_fallback_label": "KI-Bilderkennung (5s Fallback)", + "ai_fallback_hint": "Wird kein Barcode innerhalb von 5 Sekunden gelesen, wird automatisch ein Bild an die KI zur visuellen Produktidentifizierung gesendet. Erfordert konfiguriertes Gemini." }, - "startup": { - "connecting": "Serververbindung wird hergestellt...", - "check_php_memory": "PHP-Speicher", - "check_php_timeout": "PHP-Timeout", - "check_php_upload": "PHP-Upload", - "check_data_dir": "Datenverzeichnis", - "check_rate_limits": "Rate-Limits-Verzeichnis", - "check_backups": "Backup-Verzeichnis", - "check_write_test": "Schreibtest", - "check_disk_space": "Speicherplatz", - "check_db_legacy": "Legacy-DB (dispensa.db)", - "check_db_connect": "Datenbankverbindung", - "check_db_tables": "Datenbanktabellen", - "check_db_integrity": "Datenbankintegrität", - "check_db_wal": "WAL-Modus", - "check_db_size": "Datenbankgröße", - "check_db_rows": "Inventardaten", - "check_env": ".env-Datei", - "check_gemini": "Gemini-AI-Schlüssel", - "check_bring_creds": "Bring!-Anmeldedaten", - "check_bring_token": "Bring!-Token", - "check_tts": "Text-to-Speech-URL", - "check_scale": "Waagen-Gateway", - "check_curl_ssl": "cURL-SSL", - "check_internet": "Internetverbindung", - "fresh_install": "Neuinstallation", - "warnings_found": "Warnungen", - "all_ok": "System OK", - "critical_error_short": "Kritischer Fehler", - "critical_error": "Kritischer Fehler: Die App kann nicht gestartet werden. Prüfe die Serverlogs.", - "critical_error_intro": "Die App kann aufgrund folgender Probleme nicht gestartet werden:", - "error_network": "Server nicht erreichbar.", - "error_network_detail": "Der Browser kann den PHP-Server nicht erreichen.\n\nMögliche Ursachen:\n• Apache/PHP-Server läuft nicht\n• Netzwerk- oder Firewall-Problem\n• Falsche App-URL\n\nBitte Server starten und erneut versuchen.", - "retry": "Erneut versuchen", - "syncing_local": "Lokale Daten synchronisieren...", - "sync_done": "Lokale Daten aktualisiert" + "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", + "token_title": "🔑 Einstellungs-Token", + "token_label": "Zugriffstoken", + "token_hint": "Falls `SETTINGS_TOKEN` in der Server-`.env` konfiguriert ist, gib hier den Token ein, bevor du die Einstellungen speicherst. Leer lassen, wenn nicht konfiguriert.", + "token_placeholder": "(leer = kein Schutz)", + "token_required_hint": "🔒 Dieser Server benötigt einen Token zum Speichern der Einstellungen.", + "cert_instructions": "Anleitung für Chrome (Android):
    1. Zertifikat oben herunterladen
    2. Gehe zu Einstellungen → Sicherheit & Datenschutz → Weitere Sicherheitseinstellungen → Vom Gerätespeicher installieren
    3. Wähle die heruntergeladene EverShelf_CA.crt Datei
    4. Wähle \"CA\" und bestätige
    5. Chrome neu starten

    Anleitung für Chrome (PC):
    1. Zertifikat oben herunterladen
    2. Gehe zu chrome://settings/certificates (oder Einstellungen → Datenschutz und Sicherheit → Sicherheit → Zertifikate verwalten)
    3. Tab \"Zertifizierungsstellen\" → Importieren → Datei auswählen
    4. Häkchen bei \"Dieser Zertifizierungsstelle für die Identifikation von Webseiten vertrauen\"
    5. Chrome neu starten" }, - "stats_monthly": { - "title": "Monatsstatistik", - "consumed": "Produkte verbraucht", - "trend_up": "+{pct}% vs. {prev}", - "trend_down": "-{pct}% vs. {prev}", - "trend_same": "gleiches Tempo wie letzten Monat", - "added": "hinzugefügt", - "wasted": "verschwendet", - "top_used": "meistbenutzt", - "top_cats": "Hauptkategorien", - "source": "Transaktionsverlauf · aktueller Monat" + "tts": { + "title": "🔊 Sprache & TTS", + "hint": "Sprachsynthese über externe REST-API konfigurieren. Rezeptschritte und abgelaufene Timer werden an den Endpunkt gesendet.", + "enabled": "✅ TTS aktivieren", + "engine_label": "⚙️ TTS-Engine", + "engine_browser": "🔇 Browser (offline, keine Konfiguration erforderlich)", + "engine_server": "🌐 Externer Server (Home Assistant, REST API...)", + "voice_label": "🗣️ Stimme", + "rate_label": "⚡ Geschwindigkeit", + "pitch_label": "🎵 Tonhöhe", + "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_sound_btn": "🔔 Klangtest ausführen", + "test_btn": "🔊 Testansage senden", + "voices_loading": "Stimmen werden geladen…", + "voice_not_supported": "Stimme vom Browser nicht unterstützt", + "voices_none": "Keine Stimmen auf diesem Gerät verfügbar", + "voices_hint": "Verfügbare Stimmen hängen vom Betriebssystem und Browser ab. Auf macOS/iOS ist die Stimme Paola (Italienisch) verfügbar. Drücken Sie ↺ wenn die Liste nicht lädt.", + "url_missing": "⚠️ Endpunkt-URL fehlt.", + "test_sending": "⏳ Wird gesendet…", + "test_ok": "✅ Antwort {code} — prüfe ob der Lautsprecher gesprochen hat.", + "heard_question": "Hast du die Stimme gehört?", + "heard_yes": "Ja, ich habe es gehört", + "heard_no": "Nein, ich habe nichts gehört", + "test_ok_kiosk": "TTS funktioniert.", + "test_fail_steps": "Prüfe: 1) Medienvolume ist nicht 0; 2) Google Text-to-Speech installiert und aktualisiert; 3) Deutsches Sprachpaket in den Android TTS-Einstellungen heruntergeladen." + }, + "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.", + "timeout_1": "1 Minute", + "timeout_2": "2 Minuten", + "timeout_5": "5 Minuten", + "timeout_10": "10 Minuten", + "timeout_15": "15 Minuten", + "timeout_30": "30 Minuten", + "timeout_60": "1 Stunde", + "start_after": "⏱️ Starten nach" + }, + "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", + "live_weight": "Echtzeit-Gewicht", + "auto_reconnect": "🔁 Verbindung: automatisch", + "kiosk_title": "📡 BLE-Waage im Kiosk integriert", + "kiosk_hint": "Die Waage wird direkt vom internen BLE-Gateway des Kiosks verwaltet. Um ein neues Gerät zu koppeln, verwende den Konfigurationsassistenten.", + "kiosk_reconfigure": "🔄 BLE-Waage neu konfigurieren", + "ble_protocols": "

    🔌 Unterstützte BLE-Protokolle:

    " + }, + "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/", + "native_title": "Kiosk-Konfiguration", + "native_hint": "Server-URL, BLE-Waage, Bildschirmschoner und Einrichtungsassistent.", + "native_btn": "Kiosk-Konfiguration öffnen", + "native_tap_hint": "Zahnrad oben rechts antippen", + "native_update_hint": "Kiosk-App aktualisieren, um diese Funktion zu nutzen", + "update_title": "Kiosk-Aktualisierung", + "check_updates_btn": "🔍 Nach Updates suchen", + "needs_update": "⚠️ Das installierte Kiosk unterstützt diese Funktion nicht. Aktualisiere die Kiosk-App, um sie zu aktivieren." + }, + "saved": "✅ Konfiguration gespeichert!", + "saved_local": "✅ Konfiguration lokal gespeichert", + "saved_local_error": "⚠️ Lokal gespeichert, Serverfehler: {error}", + "theme": { + "title": "🌙 Erscheinungsbild", + "hint": "Wähle das Interface-Design.", + "label": "🌙 Design", + "off": "☀️ Hell", + "on": "🌙 Dunkel", + "auto": "🔄 Automatisch (Tageszeit)" + }, + "zerowaste": { + "card_title": "♻️ Zero-Waste-Tipps", + "card_hint": "Zeige während des Kochens Tipps zur Wiederverwendung von Abfällen (Schalen, Kochwasser usw.). Standardmäßig deaktiviert.", + "label": "Tipps beim Kochen anzeigen" + }, + "backup": { + "tab": "Backup", + "local_title": "Lokales Backup", + "local_hint": "Täglicher Datenbank-Snapshot. Konfiguriere, wie viele Tage Backups aufbewahrt werden.", + "enabled": "Tägliches automatisches Backup aktivieren", + "retention_days": "Aufbewahrung (Tage)", + "retention_info": "Backups werden aufbewahrt für", + "backup_now": "Jetzt sichern", + "backing_up": "Sicherung läuft…", + "backed_up": "Sicherung abgeschlossen", + "backup_error": "Sicherungsfehler", + "last_backup": "Letztes Backup", + "no_backup_yet": "Noch kein Backup erstellt", + "list_empty": "Keine Backups verfügbar", + "restore_btn": "Wiederherstellen", + "restore_confirm": "Backup wiederherstellen", + "delete_btn": "Löschen", + "delete_confirm": "Backup löschen", + "gdrive_title": "Google Drive", + "gdrive_hint": "Backups automatisch via OAuth 2.0 auf Google Drive hochladen. Keine externen Bibliotheken erforderlich.", + "gdrive_enabled": "Google Drive Backup aktivieren", + "gdrive_folder_id": "Drive-Ordner-ID", + "gdrive_folder_id_hint": "Kopiere die ID aus der Drive-Ordner-URL: …/folders/ID", + "gdrive_retention_days": "Drive-Aufbewahrung (Tage, 0=alles behalten)", + "gdrive_test": "Verbindung testen", + "gdrive_ok": "Verbindung erfolgreich!", + "gdrive_error": "Verbindung fehlgeschlagen", + "gdrive_push_now": "Jetzt auf Drive hochladen", + "gdrive_pushing": "Wird hochgeladen…", + "gdrive_pushed": "Auf Drive hochgeladen", + "gdrive_wizard_hint": "Optional: täglich automatisch via OAuth 2.0 auf Google Drive sichern.", + "gdrive_skip": "Überspringen — später in Einstellungen konfigurieren", + "gdrive_client_id": "Client-ID", + "gdrive_client_secret": "Client-Secret", + "gdrive_redirect_uri_hint": "Füge http://localhost als autorisierten Weiterleitungs-URI in der Google Cloud Console hinzu. Funktioniert auf jedem Server, auch ohne öffentliche Domain.", + "gdrive_code_title": "Autorisierungs-URL oder Code einfügen", + "gdrive_code_hint": "Nach der Autorisierung öffnet der Browser http://localhost und zeigt möglicherweise einen Verbindungsfehler — das ist normal. Kopiere die URL aus der Adressleiste (z.B. http://localhost/?code=4%2F0A...) und füge sie hier ein.", + "gdrive_code_submit": "Bestätigen", + "gdrive_code_empty": "Bitte zuerst die URL oder den Autorisierungscode einfügen", + "gdrive_redirect_uri_label": "Redirect-URI (in Google Cloud Console eintragen):", + "gdrive_oauth_authorize": "Mit Google autorisieren", + "gdrive_oauth_authorized": "Autorisiert", + "gdrive_oauth_not_authorized": "Noch nicht autorisiert", + "gdrive_oauth_window_opened": "Browserfenster geöffnet — autorisieren und zurückkehren", + "gdrive_oauth_how_to": "OAuth 2.0 einrichten (Schritt für Schritt)", + "gdrive_oauth_steps": "
  • Gehe zu console.cloud.google.com und wähle dein Projekt
  • Aktiviere die Google Drive API: APIs & Dienste → APIs aktivieren → Google Drive API
  • Gehe zu APIs & Dienste → Anmeldedaten → Anmeldedaten erstellen → OAuth-Client-ID
  • Anwendungstyp: Webanwendung; füge die unten angezeigte URL als Autorisierter Weiterleitungs-URI hinzu
  • Kopiere Client-ID und Client-Secret in die Felder oben und speichere
  • Klicke auf Mit Google autorisieren: melde dich an und erteile den Zugriff
  • Das Fenster schließt sich automatisch und Backups sind bereit
  • " + }, + "info": { + "tab": "Info", + "ai_title": "Gemini AI — Token-Nutzung", + "ai_hint": "Monatlicher Verbrauch und geschätzte Kosten für den aktuellen API-Schlüssel.", + "loading": "Laden…", + "total_tokens": "Token gesamt", + "est_cost": "Gesch. Kosten", + "input_tok": "Eingabe-Token", + "output_tok": "Ausgabe-Token", + "ai_calls": "Aufrufe", + "by_action": "Aufschlüsselung nach Funktion", + "by_model": "Aufschlüsselung nach Modell", + "pricing_note": "Gemini Referenzpreise: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.", + "system_title": "System", + "db_size": "Datenbank", + "log_size": "Protokolle", + "log_level": "Log-Level", + "ai_overview": "KI-Nutzungsübersicht, Inventar und Systemstatus", + "calls_unit": "Aufrufe", + "inv_title": "Inventar", + "inv_active": "Aktiv", + "inv_products": "Produkte gesamt", + "inv_expiring": "Ablaufend (7T)", + "inv_expired": "Abgelaufen", + "inv_finished": "Leer", + "act_title": "Monatliche Aktivität", + "act_tx_month": "Bewegungen", + "act_restock": "Einkäufe", + "act_use": "Verbrauch", + "act_new_products": "Neue Produkte", + "act_tx_year": "Jährl. Bewegungen", + "price_cache": "Preiscache", + "cache_entries": "Produkte", + "last_backup": "Letztes Backup", + "bring_days": "Token läuft in {n} Tagen ab", + "bring_expired": "Token abgelaufen", + "year_label": "Jahr {year}", + "currency_title": "Währung", + "currency_hint": "Die Währung, die für alle Kosten und Preise in der App verwendet wird." + }, + "tab_general": "Allgemein", + "shopping": { + "tab": "Einkaufsliste", + "title": "Einkaufsliste", + "hint": "Konfiguriere die integrierte Einkaufsliste oder verbinde Bring!.", + "enable_label": "Einkaufsliste aktivieren", + "mode_label": "Anbieter", + "mode_internal": "Intern (ohne Bring!)", + "mode_bring": "Bring! (externe App)", + "bring_section_title": "Bring!-Konfiguration", + "ai_section_title": "KI-Unterstützung", + "smart_suggestions_label": "KI-Vorschläge", + "forecast_label": "Prognose für bald leere Produkte", + "auto_add_label": "Automatisch hinzufügen wenn", + "auto_add_suffix": "im Lager verbleibend (0 = nur wenn leer)" + }, + "ha": { + "tab": "Home Assistant", + "title": "Home Assistant", + "hint": "Verbinde EverShelf mit Home Assistant für Automationen, Push-Benachrichtigungen und REST-Sensoren.", + "enabled": "Home Assistant-Integration aktivieren", + "connection_title": "Verbindung", + "url_label": "Home Assistant URL", + "url_placeholder": "http://192.168.1.50:8123", + "url_hint": "Basis-URL deiner Home Assistant-Instanz (z.B. http://homeassistant.local:8123).", + "token_label": "Long-Lived Access Token", + "token_hint": "Erstelle unter HA-Profil → Sicherheit → Langlebige Zugangstoken.", + "token_placeholder": "eyJhbGci...", + "token_saved": "Token gespeichert (aus Sicherheitsgründen verborgen)", + "test_btn": "Verbindung testen", + "test_ok": "Verbunden mit {version}", + "test_fail": "Verbindung fehlgeschlagen: {error}", + "test_bad_token": "HA erreichbar, aber Token ist ungültig", + "testing": "Teste…", + "error_no_url": "Bitte zuerst die Home Assistant URL eingeben.", + "tts_title": "TTS auf Smart Speaker", + "tts_hint": "Rezeptschritte auf einem Home Assistant Media Player vorlesen.", + "tts_entity_label": "Media Player Entity ID", + "tts_entity_placeholder": "media_player.wohnzimmer", + "tts_entity_hint": "Entity-ID des HA-Media-Players. Zu finden unter HA: Entwicklertools → Zustände.", + "tts_platform_label": "TTS-Plattform", + "tts_platform_speak": "tts.speak (empfohlen)", + "tts_platform_notify": "notify.* (Benachrichtigungsdienst)", + "tts_apply_btn": "HA-Voreinstellung auf TTS-Tab anwenden", + "tts_apply_hint": "Füllt den TTS-Tab mit der Home Assistant URL und dem Token aus.", + "tts_preset_applied": "HA-Voreinstellung auf TTS-Tab angewendet.", + "webhook_title": "Webhook-Automationen", + "webhook_hint": "Sende Daten an Home Assistant, wenn Ereignisse in der Vorratskammer auftreten.", + "webhook_id_label": "Webhook-ID", + "webhook_id_placeholder": "evershelf_webhook_abc123", + "webhook_id_hint": "ID des in HA erstellten Webhooks. Kopiere aus: HA → Einstellungen → Automationen → Erstellen → Webhook-Auslöser.", + "webhook_events_label": "Benachrichtige bei diesen Ereignissen", + "event_expiry": "Ablaufende Produkte (täglich)", + "event_shopping": "Artikel zur Einkaufsliste hinzugefügt", + "event_stock": "Lagerbestand aktualisiert", + "expiry_days_label": "Ablaufwarnung im Voraus (Tage)", + "expiry_days_hint": "Sende die Ablaufwarnung N Tage vor dem Ablaufdatum.", + "webhook_help": "In HA: Einstellungen → Automationen → Automation erstellen → Auslöser: Webhook → ID kopieren.", + "notify_title": "Push-Benachrichtigungen", + "notify_hint": "Sende Push-Benachrichtigungen über einen Home Assistant notify-Dienst.", + "notify_service_label": "Notify-Dienst", + "notify_service_placeholder": "notify.mobile_app_mein_handy", + "notify_service_hint": "Name des HA-notify-Dienstes (z.B. notify.mobile_app_phone). Leer lassen zum Deaktivieren.", + "sensor_title": "REST-Sensoren", + "sensor_hint": "Zur configuration.yaml hinzufügen, um EverShelf-Sensoren in Home Assistant zu erstellen.", + "sensor_copy_btn": "YAML kopieren", + "sensor_copied": "YAML in die Zwischenablage kopiert!", + "save_btn": "HA-Einstellungen speichern", + "ha_hint": "Wenn du Home Assistant verwendest, nutze den Home Assistant-Tab für TTS, Webhooks und Sensoren." } + }, + "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)", + "opened_ago_long": "Seit {n} Tagen geöffnet", + "opened_today_long": "Heute geöffnet", + "opened_suffix": "— Zu lange geöffnet!", + "opened_suffix_ok": "— Geöffnet (noch ok)", + "opened_suffix_warning": "— Geöffnet (erst prüfen)", + "days_compact": "{n}T", + "badge_check_soon": "Bald prüfen" + }, + "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", + "finished_all": "📤 {name} aufgebraucht!", + "vacuum_sealed": "{name} als vakuumversiegelt gespeichert", + "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", + "unknown": "Unbekannter Fehler", + "prefix": "Fehler", + "no_inventory_entry": "Kein Inventareintrag gefunden", + "offline_title": "Keine Verbindung", + "offline_subtitle": "Die App kann den Server nicht erreichen. Überprüfe deine WLAN-Verbindung.", + "offline_checking": "Verbindung prüfen…", + "offline_restored": "Verbindung wiederhergestellt!", + "offline_continue": "Im Offline-Modus fortfahren", + "offline_reading_cache": "Lese aus lokalem Cache", + "offline_ops_pending": "{n} Aktionen ausstehend", + "offline_synced": "{n} Aktionen synchronisiert", + "offline_ai_disabled": "Offline nicht verfügbar", + "offline_cache_ready": "Offline — {n} Produkte im Cache" + }, + "confirm_placeholder_search": null, + "confirm": { + "remove_item": "Möchtest du dieses Produkt wirklich aus dem Bestand entfernen?", + "kiosk_exit": "Kioskmodus verlassen?", + "cancel": "Abbrechen", + "proceed": "Bestätigen", + "discard_one": "1 Stück wegwerfen" + }, + "location": { + "dispensa": "Vorratskammer", + "frigo": "Kühlschrank", + "freezer": "Gefrierschrank" + }, + "edit": { + "title": "{name} bearbeiten", + "unknown_hint": "Produktname und Informationen eingeben", + "label_name": "🏷️ Produktname", + "choose_location_title": "Welchen Ort?", + "choose_location_hint": "Wähle den zu bearbeitenden Ort:", + "confirm_large_qty": "Du setzt die Menge auf {qty} {unit}. Das scheint ungewöhnlich hoch zu sein. Bestätigen?" + }, + "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", + "expiry_found": "Datum gefunden", + "expiry_read_fail": "Datum konnte nicht gelesen werden.", + "expiry_raw_label": "Erkannt" + }, + "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", + "vacuum_seal_rest": "Rest vakuumieren" + }, + "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", + "macros_title": "Geschätzte Makronährstoffe", + "macros_proteins": "Proteine", + "macros_carbs": "Kohlenhydrate", + "macros_fat": "Fett", + "macros_fiber": "Ballaststoffe", + "macros_source": "Schätzung basierend auf {n} Vorratsprodukten" + }, + "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" + }, + "about": { + "title": "Über", + "version": "Version", + "report_bug": "Fehler melden", + "report_bug_hint": "Etwas funktioniert nicht? Sende uns direkt aus der App eine Meldung.", + "report_bug_modal_title": "Fehler melden", + "report_type_bug": "Fehler", + "report_type_feature": "Funktion", + "report_type_question": "Frage", + "report_field_title": "Titel", + "report_field_title_ph": "Kurze Beschreibung des Problems", + "report_field_desc": "Beschreibung", + "report_field_desc_ph": "Problem detailliert beschreiben…", + "report_field_steps": "Schritte zum Reproduzieren (optional)", + "report_field_steps_ph": "1. Gehe zu…\n2. Tippe auf…\n3. Fehler erscheint…", + "report_auto_info": "Automatisch beigefügt: Version {version}, Sprache {lang}.", + "report_send_btn": "Bericht senden", + "report_bug_sending": "Wird gesendet…", + "report_bug_sent": "Bericht gesendet — danke!", + "report_bug_error": "Bericht konnte nicht gesendet werden. Verbindung prüfen.", + "changelog": "Changelog", + "github": "GitHub-Repository" + }, + "export": { + "title": "Inventar exportieren", + "hint": "Lade das aktuelle Inventar als CSV herunter oder öffne die druckfertige Version (PDF).", + "btn_csv": "CSV herunterladen", + "btn_pdf": "PDF / Drucken", + "btn_title": "Exportieren" + }, + "startup": { + "connecting": "Serververbindung wird hergestellt...", + "check_php_memory": "PHP-Speicher", + "check_php_timeout": "PHP-Timeout", + "check_php_upload": "PHP-Upload", + "check_data_dir": "Datenverzeichnis", + "check_rate_limits": "Rate-Limits-Verzeichnis", + "check_backups": "Backup-Verzeichnis", + "check_write_test": "Schreibtest", + "check_disk_space": "Speicherplatz", + "check_db_legacy": "Legacy-DB (dispensa.db)", + "check_db_connect": "Datenbankverbindung", + "check_db_tables": "Datenbanktabellen", + "check_db_integrity": "Datenbankintegrität", + "check_db_wal": "WAL-Modus", + "check_db_size": "Datenbankgröße", + "check_db_rows": "Inventardaten", + "check_env": ".env-Datei", + "check_gemini": "Gemini-AI-Schlüssel", + "check_bring_creds": "Bring!-Anmeldedaten", + "check_bring_token": "Bring!-Token", + "check_tts": "Text-to-Speech-URL", + "check_scale": "Waagen-Gateway", + "check_curl_ssl": "cURL-SSL", + "check_internet": "Internetverbindung", + "fresh_install": "Neuinstallation", + "warnings_found": "Warnungen", + "all_ok": "System OK", + "critical_error_short": "Kritischer Fehler", + "critical_error": "Kritischer Fehler: Die App kann nicht gestartet werden. Prüfe die Serverlogs.", + "critical_error_intro": "Die App kann aufgrund folgender Probleme nicht gestartet werden:", + "error_network": "Server nicht erreichbar.", + "error_network_detail": "Der Browser kann den PHP-Server nicht erreichen.\n\nMögliche Ursachen:\n• Apache/PHP-Server läuft nicht\n• Netzwerk- oder Firewall-Problem\n• Falsche App-URL\n\nBitte Server starten und erneut versuchen.", + "retry": "Erneut versuchen", + "syncing_local": "Lokale Daten synchronisieren...", + "sync_done": "Lokale Daten aktualisiert" + }, + "stats_monthly": { + "title": "Monatsstatistik", + "consumed": "Produkte verbraucht", + "trend_up": "+{pct}% vs. {prev}", + "trend_down": "-{pct}% vs. {prev}", + "trend_same": "gleiches Tempo wie letzten Monat", + "added": "hinzugefügt", + "wasted": "verschwendet", + "top_used": "meistbenutzt", + "top_cats": "Hauptkategorien", + "source": "Transaktionsverlauf · aktueller Monat" + } } \ No newline at end of file diff --git a/translations/en.json b/translations/en.json index bd8ebb7..1536460 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1,1458 +1,1463 @@ { - "app": { - "name": "EverShelf", - "loading": "Loading..." - }, - "nav": { - "title": "EverShelf", - "home": "Home", - "inventory": "Pantry", - "recipes": "Recipes", - "shopping": "Shopping", - "log": "Log", - "settings": "Settings" - }, - "btn": { - "back": "← Back", - "save": "💾 Save", - "cancel": "✕ Cancel", - "close": "Close", - "add": "✅ Add", - "delete": "Delete", - "edit": "✏️ Edit", - "use": "Use", - "edit_item": "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", - "yes_short": "Yes", - "no_short": "No" - }, - "form": { - "select_placeholder": "-- Select --" - }, - "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", - "millilitres": "Millilitres", - "from": "of" - }, - "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": "Consumption to review", - "banner_prediction_hint": "The consumption estimate adapts to recent data: confirm only if the current quantity is correct.", - "banner_prediction_action_confirm": "Confirm {qty} {unit}", - "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_finished": "I finished it!", - "banner_expired_action_throw": "I threw it away", - "banner_expired_action_edit": "Fix date", - "banner_expired_action_modify": "Edit", - "banner_expired_action_vacuum": "Put in vacuum seal", - "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": "previous estimate: {expected} {unit}{time}; current quantity: {actual} {unit}.", - "banner_prediction_less": "estimate: {expected} {unit}{time}; current quantity: {actual} {unit}. If your usage pace changed, the forecast updates automatically.", - "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_opened_detail": "{when} in {location} · 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", - "mode_shopping": "🛒 Shopping Mode", - "mode_shopping_end": "✅ End shopping", - "spesa_btn": "🛒 Shopping", - "zoom": "Zoom", - "tab_barcode": "Barcode", - "tab_name": "Name", - "tab_ai": "AI", - "recents_label": "Recent", - "torch_hint": "Torch", - "torch_on": "Torch on", - "torch_off": "Torch off", - "torch_unavailable": "Torch not available on this device", - "flip_hint": "Flip camera", - "flip_front": "Front camera", - "flip_back": "Rear camera", - "num_ocr_btn": "🔢 Read numbers with AI", - "num_ocr_searching": "Looking for barcode with AI...", - "num_ocr_found": "Code found: {code}", - "num_ocr_not_found": "No barcode found in image", - "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", - "stock_in_pantry": "Already in pantry:", - "status_ready": "Point camera at barcode", - "status_scanning": "Scanning...", - "status_partial": "Detected: {code} — verifying...", - "status_invalid": "Invalid: {code} — retrying", - "status_confirmed": "Confirmed!", - "status_parallel": "Using combined scan methods..." - }, - "action": { - "title": "What do you want to do?", - "add_btn": "📥 ADD", - "add_sub": "to pantry/fridge", - "use_btn": "USE", - "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": "Recipe", - "related_stock_title": "Also at home" - }, - "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", - "vacuum_question": "Vacuum sealed?", - "vacuum_saved": "🔒 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}!", - "expiry_warning_opened": "⚠️ The one{loc} has been open for {when} — use it first!", - "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", - "unknown_product": "Unrecognized product", - "edit_name_brand": "Edit name/brand", - "weight_label": "Weight", - "origin_label": "Origin", - "labels_label": "Labels", - "select_variant": "Select the exact variant or use AI data:" - }, - "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", - "regen_choice_title": "What do you want to do with this recipe?", - "regen_replace": "🔄 Generate another (discard this one)", - "regen_save_new": "💾 Save to archive & generate a new one", - "close_btn": "✅ Close", - "ingredients_title": "🧾 Ingredients", - "tools_title": "Equipment needed", - "steps_title": "👨‍🍳 Steps", - "no_steps": "No steps available", - "generate_error": "Generation error", - "stream_interrupted": "Generation interrupted (incomplete server response). Check logs or try again.", - "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", - "favorite": "Add to favourites", - "unfavorite": "Remove from favourites", - "adjust_persons": "Persons" - }, - "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", - "pantry_hint": "Already at home: {qty}" - }, - "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", - "recipe_prefix": "Recipe" - }, - "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", - "quick_recipe_prompt": "Suggest a quick recipe FOR ONE PERSON using the products that expire first! Ignore freezer items, focus on fridge and pantry." - }, - "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", - "step_fallback": "Step {n}", - "zerowaste_label": "♻️ Scrap", - "zerowaste_tip_title": "Zero-waste tip" - }, - "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", - "reset_btn": "↺ Restore defaults" - }, - "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", - "token_title": "🔑 Settings Token", - "token_label": "Access token", - "token_hint": "If `SETTINGS_TOKEN` is configured in the server's `.env`, enter the token here before saving settings. Leave empty if not configured.", - "token_placeholder": "(empty = no protection)", - "token_required_hint": "🔒 This server requires a token to save settings.", - "cert_instructions": "Instructions for Chrome (Android):
    1. Download the certificate above
    2. Go to Settings → Security & Privacy → More security settings → Install from device storage
    3. Select the downloaded EverShelf_CA.crt file
    4. Choose \"CA\" and confirm
    5. Restart Chrome

    Instructions for Chrome (PC):
    1. Download the certificate above
    2. Go to chrome://settings/certificates (or Settings → Privacy and security → Security → Manage certificates)
    3. Tab \"Authorities\" → Import → select the file
    4. Check \"Trust this certificate for identifying websites\"
    5. Restart Chrome" - }, - "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", - "engine_label": "⚙️ TTS Engine", - "engine_browser": "🔇 Browser (offline, no configuration required)", - "engine_server": "🌐 External server (Home Assistant, REST API...)", - "voice_label": "🗣️ Voice", - "rate_label": "⚡ Speed", - "pitch_label": "🎵 Pitch", - "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_sound_btn": "🔔 Run Sound Test", - "test_btn": "🔊 Send Test Voice", - "voices_loading": "Loading voices…", - "voice_not_supported": "Voice not supported by this browser", - "voices_none": "No voices available on this device", - "voices_hint": "Available voices depend on the OS and browser. On macOS/iOS the Paola (Italian) voice is available. Press ↺ if the list does not load.", - "url_missing": "⚠️ Endpoint URL missing.", - "test_sending": "⏳ Sending…", - "test_ok": "✅ Response {code} — check that the speaker has spoken.", - "heard_question": "Did you hear the voice?", - "heard_yes": "Yes, I heard it", - "heard_no": "No, I didn't hear it", - "test_ok_kiosk": "TTS is working.", - "test_fail_steps": "Check: 1) media volume is not 0; 2) Google Text-to-Speech is installed and updated; 3) Italian voice package is downloaded in Android TTS settings." - }, - "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.", - "timeout_1": "1 minute", - "timeout_2": "2 minutes", - "timeout_5": "5 minutes", - "timeout_10": "10 minutes", - "timeout_15": "15 minutes", - "timeout_30": "30 minutes", - "timeout_60": "1 hour", - "start_after": "⏱️ Start after" - }, - "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", - "live_weight": "real-time weight", - "auto_reconnect": "🔁 Reconnect: automatic", - "kiosk_title": "📡 BLE Scale integrated in Kiosk", - "kiosk_hint": "The scale is directly managed by the internal BLE Gateway of the kiosk. To pair a new device, use the configuration wizard.", - "kiosk_reconfigure": "🔄 Reconfigure BLE Scale", - "ble_protocols": "

    🔌 Supported BLE protocols:

    " - }, - "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/", - "native_title": "Kiosk Configuration", - "native_hint": "Server URL, BLE scale, screensaver and setup wizard.", - "native_btn": "Open kiosk configuration", - "native_tap_hint": "Tap the gear button at the top right", - "native_update_hint": "Update the kiosk app to use this feature", - "update_title": "Kiosk Update", - "check_updates_btn": "🔍 Check for updates", - "needs_update": "⚠️ The installed kiosk does not support this feature. Update the kiosk app to enable it." - }, - "saved": "✅ Configuration saved!", - "saved_local": "✅ Configuration saved locally", - "saved_local_error": "⚠️ Saved locally, server error: {error}", - "theme": { - "title": "🌙 Appearance", - "hint": "Choose the interface theme.", - "label": "🌙 Theme", - "off": "☀️ Light", - "on": "🌙 Dark", - "auto": "🔄 Automatic (time of day)" - }, - "zerowaste": { - "card_title": "♻️ Zero-waste tips", - "card_hint": "During cooking, show tips on how to reuse scraps generated in each step (peels, cooking water, etc.). Disabled by default.", - "label": "Show tips during cooking" - }, - "backup": { - "tab": "Backup", - "local_title": "Local Backup", - "local_hint": "Daily database snapshot. Configure how many days of backups to keep.", - "enabled": "Enable daily automatic backup", - "retention_days": "Retention (days)", - "retention_info": "Backups are kept for", - "backup_now": "Backup Now", - "backing_up": "Backing up…", - "backed_up": "Backup complete", - "backup_error": "Backup error", - "last_backup": "Last backup", - "no_backup_yet": "No backup has been created yet", - "list_empty": "No backups available", - "restore_btn": "Restore", - "restore_confirm": "Restore backup", - "delete_btn": "Delete", - "delete_confirm": "Delete backup", - "gdrive_title": "Google Drive", - "gdrive_hint": "Automatically back up to Google Drive via OAuth 2.0. No external libraries required.", - "gdrive_enabled": "Enable Google Drive backup", - "gdrive_folder_id": "Drive Folder ID", - "gdrive_folder_id_hint": "Copy the ID from the Drive folder URL: …/folders/ID", - "gdrive_retention_days": "Drive retention (days, 0=keep all)", - "gdrive_test": "Test Connection", - "gdrive_ok": "Connection successful!", - "gdrive_error": "Connection failed", - "gdrive_push_now": "Upload to Drive Now", - "gdrive_pushing": "Uploading…", - "gdrive_pushed": "Uploaded to Drive", - "gdrive_wizard_hint": "Optional: automatically back up to Google Drive daily via OAuth 2.0.", - "gdrive_skip": "Skip — configure later in Settings", - "gdrive_client_id": "Client ID", - "gdrive_client_secret": "Client Secret", - "gdrive_redirect_uri_hint": "Add http://localhost as an authorized redirect URI in Google Cloud Console. This works on any server, even without a public domain.", - "gdrive_code_title": "Paste the authorization URL or code", - "gdrive_code_hint": "After authorizing, the browser will open http://localhost and may show a connection error — that is expected. Copy the URL from the address bar (e.g. http://localhost/?code=4%2F0A...) and paste it here.", - "gdrive_code_submit": "Submit", - "gdrive_code_empty": "Paste the URL or authorization code first", - "gdrive_redirect_uri_label": "Redirect URI (add this in Google Cloud Console):", - "gdrive_oauth_authorize": "Authorize with Google", - "gdrive_oauth_authorized": "Authorized", - "gdrive_oauth_not_authorized": "Not authorized yet", - "gdrive_oauth_window_opened": "Browser window opened — authorize and come back", - "gdrive_oauth_how_to": "How to set up OAuth 2.0 (step by step)", - "gdrive_oauth_steps": "
  • Go to console.cloud.google.com and select your project
  • Enable the Google Drive API: APIs & Services → Enable APIs → Google Drive API
  • Go to APIs & Services → Credentials → Create Credentials → OAuth client ID
  • Application type: Web application; add http://localhost as an Authorized redirect URI
  • Copy the Client ID and Client Secret into the fields above and save
  • Click Authorize with Google, sign in and grant access
  • The browser will open http://localhost (a connection error is expected): copy the URL from the address bar and paste it in the field that appears below
  • " - }, - "info": { - "tab": "Info", - "ai_title": "Gemini AI — Token Usage", - "ai_hint": "Monthly consumption and estimated cost for the current API key.", - "loading": "Loading…", - "total_tokens": "Total tokens", - "est_cost": "Est. cost", - "input_tok": "Input tokens", - "output_tok": "Output tokens", - "ai_calls": "Calls", - "by_action": "Breakdown by function", - "by_model": "Breakdown by model", - "pricing_note": "Gemini reference pricing: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.", - "system_title": "System", - "db_size": "Database", - "log_size": "Logs", - "log_level": "Log level", - "ai_overview": "AI usage overview, inventory and system status", - "calls_unit": "calls", - "inv_title": "Inventory", - "inv_active": "Active", - "inv_products": "Total products", - "inv_expiring": "Expiring (7d)", - "inv_expired": "Expired", - "inv_finished": "Finished", - "act_title": "Monthly activity", - "act_tx_month": "Movements", - "act_restock": "Restocks", - "act_use": "Usages", - "act_new_products": "New products", - "act_tx_year": "Yearly movements", - "price_cache": "Price cache", - "cache_entries": "products", - "last_backup": "Last backup", - "bring_days": "token expires in {n} days", - "bring_expired": "token expired", - "year_label": "Year {year}", - "currency_title": "Currency", - "currency_hint": "The currency used for all costs and prices in the app." - }, - "tab_general": "General", - "shopping": { - "tab": "Shopping list", - "title": "Shopping list", - "hint": "Configure the built-in shopping list or connect Bring!.", - "enable_label": "Enable shopping list", - "mode_label": "Provider", - "mode_internal": "Built-in (no Bring!)", - "mode_bring": "Bring! (external app)", - "bring_section_title": "Bring! configuration", - "ai_section_title": "AI assistance", - "smart_suggestions_label": "AI suggestions", - "forecast_label": "Forecast low-stock products", - "auto_add_label": "Auto-add to list when", - "auto_add_suffix": "remaining in stock (0 = only when empty)" - }, - "ha": { - "tab": "Home Assistant", - "title": "Home Assistant", - "hint": "Connect EverShelf to Home Assistant for automations, push notifications and REST sensors.", - "enabled": "Enable Home Assistant integration", - "connection_title": "Connection", - "url_label": "Home Assistant URL", - "url_placeholder": "http://192.168.1.50:8123", - "url_hint": "Base URL of your Home Assistant instance (e.g. http://homeassistant.local:8123).", - "token_label": "Long-Lived Access Token", - "token_hint": "Generate from HA Profile → Security → Long-Lived Access Tokens.", - "token_placeholder": "eyJhbGci...", - "token_saved": "Token saved (hidden for security)", - "test_btn": "Test connection", - "test_ok": "Connected to {version}", - "test_fail": "Connection failed: {error}", - "test_bad_token": "HA reachable but token is invalid", - "testing": "Testing…", - "error_no_url": "Please enter the Home Assistant URL first.", - "tts_title": "TTS on Smart Speaker", - "tts_hint": "Read recipe steps aloud on a Home Assistant media player.", - "tts_entity_label": "Media player entity ID", - "tts_entity_placeholder": "media_player.living_room", - "tts_entity_hint": "Entity ID of the HA media player. Find it in HA: Developer Tools → States.", - "tts_platform_label": "TTS platform", - "tts_platform_speak": "tts.speak (recommended)", - "tts_platform_notify": "notify.* (notification service)", - "tts_apply_btn": "Apply HA preset to TTS tab", - "tts_apply_hint": "Pre-fills the TTS tab with the Home Assistant URL and token.", - "tts_preset_applied": "HA preset applied to TTS tab.", - "webhook_title": "Webhook Automations", - "webhook_hint": "Send data to Home Assistant when pantry events occur. Create an HA automation with a Webhook trigger and paste the generated ID here.", - "webhook_id_label": "Webhook ID", - "webhook_id_placeholder": "evershelf_webhook_abc123", - "webhook_id_hint": "ID of the webhook created in HA. Copy from: HA → Settings → Automations → Create → Webhook Trigger.", - "webhook_events_label": "Notify on these events", - "event_expiry": "Expiring products (daily)", - "event_shopping": "Item added to shopping list", - "event_stock": "Stock level updated", - "expiry_days_label": "Expiry lead time (days)", - "expiry_days_hint": "Send the expiry alert N days before the expiry date.", - "webhook_help": "In HA: Settings → Automations → Create automation → Trigger: Webhook → copy the generated ID above.", - "notify_title": "Push Notifications", - "notify_hint": "Send push notifications to your phone via a Home Assistant notify service.", - "notify_service_label": "Notify service", - "notify_service_placeholder": "notify.mobile_app_my_phone", - "notify_service_hint": "HA notify service name (e.g. notify.mobile_app_phone). Leave empty to disable.", - "sensor_title": "REST Sensors", - "sensor_hint": "Add to configuration.yaml to create EverShelf sensors in Home Assistant.", - "sensor_copy_btn": "Copy YAML", - "sensor_copied": "YAML copied to clipboard!", - "save_btn": "Save HA settings", - "ha_hint": "If you use Home Assistant, use the Home Assistant tab to configure TTS, webhooks and sensors." - } - }, - "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)", - "opened_ago_long": "Opened {n} days ago", - "opened_today_long": "Opened today", - "opened_suffix": "— Opened too long!", - "opened_suffix_ok": "— Opened (still ok)", - "opened_suffix_warning": "— Opened (check first)", - "days_compact": "{n}d", - "badge_check_soon": "Check soon" - }, - "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}", - "finished_all": "📤 {name} finished!", - "vacuum_sealed": "{name} saved as vacuum sealed", - "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", - "unknown": "Unknown error", - "prefix": "Error", - "no_inventory_entry": "No inventory entry found", - "offline_title": "No connection", - "offline_subtitle": "The app cannot reach the server. Check your Wi-Fi connection.", - "offline_checking": "Checking connection…", - "offline_restored": "Connection restored!", - "offline_continue": "Continue in offline mode", - "offline_reading_cache": "Reading from local cache", - "offline_ops_pending": "{n} operations pending", - "offline_synced": "{n} operations synced", - "offline_ai_disabled": "Not available offline", - "offline_cache_ready": "Offline — {n} items cached" - }, - "confirm_placeholder_search": null, - "confirm": { - "remove_item": "Do you really want to remove this product from inventory?", - "kiosk_exit": "Exit kiosk mode?", - "cancel": "Cancel", - "proceed": "Confirm", - "discard_one": "Discard 1 piece" - }, - "location": { - "dispensa": "Pantry", - "frigo": "Fridge", - "freezer": "Freezer" - }, - "edit": { - "title": "Edit {name}", - "unknown_hint": "Enter the product name and information", - "label_name": "🏷️ Product name", - "choose_location_title": "Which location?", - "choose_location_hint": "Choose the location to edit:", - "confirm_large_qty": "You are setting the quantity to {qty} {unit}. This seems unusually high. Confirm?" - }, - "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", - "expiry_found": "Date found", - "expiry_read_fail": "Cannot read the date.", - "expiry_raw_label": "Read" - }, - "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", - "vacuum_seal_rest": "Vacuum seal the rest" - }, - "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", - "macros_title": "Estimated Macronutrients", - "macros_proteins": "Proteins", - "macros_carbs": "Carbohydrates", - "macros_fat": "Fat", - "macros_fiber": "Fibre", - "macros_source": "Estimate based on {n} pantry 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" - }, + "app": { + "name": "EverShelf", + "loading": "Loading..." + }, + "nav": { + "title": "EverShelf", + "home": "Home", + "inventory": "Pantry", + "recipes": "Recipes", + "shopping": "Shopping", + "log": "Log", + "settings": "Settings" + }, + "btn": { + "back": "← Back", + "save": "💾 Save", + "cancel": "✕ Cancel", + "close": "Close", + "add": "✅ Add", + "delete": "Delete", + "edit": "✏️ Edit", + "use": "Use", + "edit_item": "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", + "yes_short": "Yes", + "no_short": "No" + }, + "form": { + "select_placeholder": "-- Select --" + }, + "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", + "millilitres": "Millilitres", + "from": "of" + }, + "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": "Consumption to review", + "banner_prediction_hint": "The consumption estimate adapts to recent data: confirm only if the current quantity is correct.", + "banner_prediction_action_confirm": "Confirm {qty} {unit}", + "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_finished": "I finished it!", + "banner_expired_action_throw": "I threw it away", + "banner_expired_action_edit": "Fix date", + "banner_expired_action_modify": "Edit", + "banner_expired_action_vacuum": "Put in vacuum seal", + "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": "previous estimate: {expected} {unit}{time}; current quantity: {actual} {unit}.", + "banner_prediction_less": "estimate: {expected} {unit}{time}; current quantity: {actual} {unit}. If your usage pace changed, the forecast updates automatically.", + "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_opened_detail": "{when} in {location} · 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", + "mode_shopping": "🛒 Shopping Mode", + "mode_shopping_end": "✅ End shopping", + "spesa_btn": "🛒 Shopping", + "zoom": "Zoom", + "tab_barcode": "Barcode", + "tab_name": "Name", + "tab_ai": "AI", + "recents_label": "Recent", + "torch_hint": "Torch", + "torch_on": "Torch on", + "torch_off": "Torch off", + "torch_unavailable": "Torch not available on this device", + "flip_hint": "Flip camera", + "flip_front": "Front camera", + "flip_back": "Rear camera", + "num_ocr_btn": "🔢 Read numbers with AI", + "num_ocr_searching": "Looking for barcode with AI...", + "num_ocr_found": "Code found: {code}", + "num_ocr_not_found": "No barcode found in image", + "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", + "stock_in_pantry": "Already in pantry:", + "status_ready": "Point camera at barcode", + "status_scanning": "Scanning...", + "status_partial": "Detected: {code} — verifying...", + "status_invalid": "Invalid: {code} — retrying", + "status_confirmed": "Confirmed!", + "status_parallel": "Using combined scan methods...", + "ai_fallback_searching": "AI identifying product...", + "ai_fallback_found": "Product identified by AI", + "ai_fallback_not_found": "AI: product not recognized" + }, + "action": { + "title": "What do you want to do?", + "add_btn": "📥 ADD", + "add_sub": "to pantry/fridge", + "use_btn": "USE", + "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": "Recipe", + "related_stock_title": "Also at home" + }, + "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", + "vacuum_question": "Vacuum sealed?", + "vacuum_saved": "🔒 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}!", + "expiry_warning_opened": "⚠️ The one{loc} has been open for {when} — use it first!", + "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", + "unknown_product": "Unrecognized product", + "edit_name_brand": "Edit name/brand", + "weight_label": "Weight", + "origin_label": "Origin", + "labels_label": "Labels", + "select_variant": "Select the exact variant or use AI data:" + }, + "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", + "regen_choice_title": "What do you want to do with this recipe?", + "regen_replace": "🔄 Generate another (discard this one)", + "regen_save_new": "💾 Save to archive & generate a new one", + "close_btn": "✅ Close", + "ingredients_title": "🧾 Ingredients", + "tools_title": "Equipment needed", + "steps_title": "👨‍🍳 Steps", + "no_steps": "No steps available", + "generate_error": "Generation error", + "stream_interrupted": "Generation interrupted (incomplete server response). Check logs or try again.", + "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", + "favorite": "Add to favourites", + "unfavorite": "Remove from favourites", + "adjust_persons": "Persons" + }, + "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", + "pantry_hint": "Already at home: {qty}" + }, + "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", + "recipe_prefix": "Recipe" + }, + "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", + "quick_recipe_prompt": "Suggest a quick recipe FOR ONE PERSON using the products that expire first! Ignore freezer items, focus on fridge and pantry." + }, + "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", + "step_fallback": "Step {n}", + "zerowaste_label": "♻️ Scrap", + "zerowaste_tip_title": "Zero-waste tip" + }, + "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": { - "chat_title": "Chat with Gemini", - "not_configured": "🤖 Gemini not configured — set GEMINI_API_KEY in settings" + "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", + "reset_btn": "↺ Restore defaults" }, "appliances": { - "empty": "No appliances added" + "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" }, - "about": { - "title": "About", - "version": "Version", - "report_bug": "Report a Bug", - "report_bug_hint": "Something not working? Send us a report directly from the app.", - "report_bug_modal_title": "Report a Bug", - "report_type_bug": "Bug", - "report_type_feature": "Feature", - "report_type_question": "Question", - "report_field_title": "Title", - "report_field_title_ph": "Brief description of the issue", - "report_field_desc": "Description", - "report_field_desc_ph": "Describe the issue in detail…", - "report_field_steps": "Steps to reproduce (optional)", - "report_field_steps_ph": "1. Go to…\n2. Tap…\n3. See the error…", - "report_auto_info": "Automatically attached: version {version}, language {lang}.", - "report_send_btn": "Send report", - "report_bug_sending": "Sending…", - "report_bug_sent": "Report sent — thank you!", - "report_bug_error": "Could not send the report. Check your connection.", - "changelog": "Changelog", - "github": "GitHub Repository" + "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}" }, - "export": { - "title": "Export inventory", - "hint": "Download the current inventory as CSV or open a print-ready version (PDF).", - "btn_csv": "Download CSV", - "btn_pdf": "PDF / Print", - "btn_title": "Export" + "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", + "ai_fallback_label": "AI visual identification (5s fallback)", + "ai_fallback_hint": "If no barcode is read within 5 seconds, a frame is automatically sent to AI to visually identify the product. Requires Gemini configured." }, - "startup": { - "connecting": "Connecting to server...", - "check_php_memory": "PHP memory", - "check_php_timeout": "PHP timeout", - "check_php_upload": "PHP upload", - "check_data_dir": "Data directory", - "check_rate_limits": "Rate limits dir", - "check_backups": "Backup dir", - "check_write_test": "Disk write test", - "check_disk_space": "Disk space", - "check_db_legacy": "Legacy DB (dispensa.db)", - "check_db_connect": "Database connection", - "check_db_tables": "Database tables", - "check_db_integrity": "Database integrity", - "check_db_wal": "WAL mode", - "check_db_size": "Database size", - "check_db_rows": "Inventory data", - "check_env": ".env file", - "check_gemini": "Gemini AI key", - "check_bring_creds": "Bring! credentials", - "check_bring_token": "Bring! token", - "check_tts": "Text-to-Speech URL", - "check_scale": "Scale gateway", - "check_curl_ssl": "cURL SSL", - "check_internet": "Internet connection", - "fresh_install": "fresh install", - "warnings_found": "warnings found", - "all_ok": "System OK", - "critical_error_short": "Critical error", - "critical_error": "Critical error: the app cannot start. Check your server logs.", - "critical_error_intro": "The app cannot start due to the following issues:", - "error_network": "Cannot reach the server.", - "error_network_detail": "The browser cannot reach the PHP server.\n\nPossible causes:\n• Apache/PHP server is not running\n• Network or firewall issue\n• Incorrect app URL\n\nMake sure the server is started and try again.", - "retry": "Retry", - "syncing_local": "Syncing local data...", - "sync_done": "Local data synced" + "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", + "token_title": "🔑 Settings Token", + "token_label": "Access token", + "token_hint": "If `SETTINGS_TOKEN` is configured in the server's `.env`, enter the token here before saving settings. Leave empty if not configured.", + "token_placeholder": "(empty = no protection)", + "token_required_hint": "🔒 This server requires a token to save settings.", + "cert_instructions": "Instructions for Chrome (Android):
    1. Download the certificate above
    2. Go to Settings → Security & Privacy → More security settings → Install from device storage
    3. Select the downloaded EverShelf_CA.crt file
    4. Choose \"CA\" and confirm
    5. Restart Chrome

    Instructions for Chrome (PC):
    1. Download the certificate above
    2. Go to chrome://settings/certificates (or Settings → Privacy and security → Security → Manage certificates)
    3. Tab \"Authorities\" → Import → select the file
    4. Check \"Trust this certificate for identifying websites\"
    5. Restart Chrome" }, - "stats_monthly": { - "title": "Monthly Stats", - "consumed": "products used", - "trend_up": "+{pct}% vs {prev}", - "trend_down": "-{pct}% vs {prev}", - "trend_same": "same pace as last month", - "added": "added", - "wasted": "wasted", - "top_used": "top used", - "top_cats": "Top categories", - "source": "Transaction history · current month" + "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", + "engine_label": "⚙️ TTS Engine", + "engine_browser": "🔇 Browser (offline, no configuration required)", + "engine_server": "🌐 External server (Home Assistant, REST API...)", + "voice_label": "🗣️ Voice", + "rate_label": "⚡ Speed", + "pitch_label": "🎵 Pitch", + "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_sound_btn": "🔔 Run Sound Test", + "test_btn": "🔊 Send Test Voice", + "voices_loading": "Loading voices…", + "voice_not_supported": "Voice not supported by this browser", + "voices_none": "No voices available on this device", + "voices_hint": "Available voices depend on the OS and browser. On macOS/iOS the Paola (Italian) voice is available. Press ↺ if the list does not load.", + "url_missing": "⚠️ Endpoint URL missing.", + "test_sending": "⏳ Sending…", + "test_ok": "✅ Response {code} — check that the speaker has spoken.", + "heard_question": "Did you hear the voice?", + "heard_yes": "Yes, I heard it", + "heard_no": "No, I didn't hear it", + "test_ok_kiosk": "TTS is working.", + "test_fail_steps": "Check: 1) media volume is not 0; 2) Google Text-to-Speech is installed and updated; 3) Italian voice package is downloaded in Android TTS settings." + }, + "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.", + "timeout_1": "1 minute", + "timeout_2": "2 minutes", + "timeout_5": "5 minutes", + "timeout_10": "10 minutes", + "timeout_15": "15 minutes", + "timeout_30": "30 minutes", + "timeout_60": "1 hour", + "start_after": "⏱️ Start after" + }, + "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", + "live_weight": "real-time weight", + "auto_reconnect": "🔁 Reconnect: automatic", + "kiosk_title": "📡 BLE Scale integrated in Kiosk", + "kiosk_hint": "The scale is directly managed by the internal BLE Gateway of the kiosk. To pair a new device, use the configuration wizard.", + "kiosk_reconfigure": "🔄 Reconfigure BLE Scale", + "ble_protocols": "

    🔌 Supported BLE protocols:

    " + }, + "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/", + "native_title": "Kiosk Configuration", + "native_hint": "Server URL, BLE scale, screensaver and setup wizard.", + "native_btn": "Open kiosk configuration", + "native_tap_hint": "Tap the gear button at the top right", + "native_update_hint": "Update the kiosk app to use this feature", + "update_title": "Kiosk Update", + "check_updates_btn": "🔍 Check for updates", + "needs_update": "⚠️ The installed kiosk does not support this feature. Update the kiosk app to enable it." + }, + "saved": "✅ Configuration saved!", + "saved_local": "✅ Configuration saved locally", + "saved_local_error": "⚠️ Saved locally, server error: {error}", + "theme": { + "title": "🌙 Appearance", + "hint": "Choose the interface theme.", + "label": "🌙 Theme", + "off": "☀️ Light", + "on": "🌙 Dark", + "auto": "🔄 Automatic (time of day)" + }, + "zerowaste": { + "card_title": "♻️ Zero-waste tips", + "card_hint": "During cooking, show tips on how to reuse scraps generated in each step (peels, cooking water, etc.). Disabled by default.", + "label": "Show tips during cooking" + }, + "backup": { + "tab": "Backup", + "local_title": "Local Backup", + "local_hint": "Daily database snapshot. Configure how many days of backups to keep.", + "enabled": "Enable daily automatic backup", + "retention_days": "Retention (days)", + "retention_info": "Backups are kept for", + "backup_now": "Backup Now", + "backing_up": "Backing up…", + "backed_up": "Backup complete", + "backup_error": "Backup error", + "last_backup": "Last backup", + "no_backup_yet": "No backup has been created yet", + "list_empty": "No backups available", + "restore_btn": "Restore", + "restore_confirm": "Restore backup", + "delete_btn": "Delete", + "delete_confirm": "Delete backup", + "gdrive_title": "Google Drive", + "gdrive_hint": "Automatically back up to Google Drive via OAuth 2.0. No external libraries required.", + "gdrive_enabled": "Enable Google Drive backup", + "gdrive_folder_id": "Drive Folder ID", + "gdrive_folder_id_hint": "Copy the ID from the Drive folder URL: …/folders/ID", + "gdrive_retention_days": "Drive retention (days, 0=keep all)", + "gdrive_test": "Test Connection", + "gdrive_ok": "Connection successful!", + "gdrive_error": "Connection failed", + "gdrive_push_now": "Upload to Drive Now", + "gdrive_pushing": "Uploading…", + "gdrive_pushed": "Uploaded to Drive", + "gdrive_wizard_hint": "Optional: automatically back up to Google Drive daily via OAuth 2.0.", + "gdrive_skip": "Skip — configure later in Settings", + "gdrive_client_id": "Client ID", + "gdrive_client_secret": "Client Secret", + "gdrive_redirect_uri_hint": "Add http://localhost as an authorized redirect URI in Google Cloud Console. This works on any server, even without a public domain.", + "gdrive_code_title": "Paste the authorization URL or code", + "gdrive_code_hint": "After authorizing, the browser will open http://localhost and may show a connection error — that is expected. Copy the URL from the address bar (e.g. http://localhost/?code=4%2F0A...) and paste it here.", + "gdrive_code_submit": "Submit", + "gdrive_code_empty": "Paste the URL or authorization code first", + "gdrive_redirect_uri_label": "Redirect URI (add this in Google Cloud Console):", + "gdrive_oauth_authorize": "Authorize with Google", + "gdrive_oauth_authorized": "Authorized", + "gdrive_oauth_not_authorized": "Not authorized yet", + "gdrive_oauth_window_opened": "Browser window opened — authorize and come back", + "gdrive_oauth_how_to": "How to set up OAuth 2.0 (step by step)", + "gdrive_oauth_steps": "
  • Go to console.cloud.google.com and select your project
  • Enable the Google Drive API: APIs & Services → Enable APIs → Google Drive API
  • Go to APIs & Services → Credentials → Create Credentials → OAuth client ID
  • Application type: Web application; add http://localhost as an Authorized redirect URI
  • Copy the Client ID and Client Secret into the fields above and save
  • Click Authorize with Google, sign in and grant access
  • The browser will open http://localhost (a connection error is expected): copy the URL from the address bar and paste it in the field that appears below
  • " + }, + "info": { + "tab": "Info", + "ai_title": "Gemini AI — Token Usage", + "ai_hint": "Monthly consumption and estimated cost for the current API key.", + "loading": "Loading…", + "total_tokens": "Total tokens", + "est_cost": "Est. cost", + "input_tok": "Input tokens", + "output_tok": "Output tokens", + "ai_calls": "Calls", + "by_action": "Breakdown by function", + "by_model": "Breakdown by model", + "pricing_note": "Gemini reference pricing: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.", + "system_title": "System", + "db_size": "Database", + "log_size": "Logs", + "log_level": "Log level", + "ai_overview": "AI usage overview, inventory and system status", + "calls_unit": "calls", + "inv_title": "Inventory", + "inv_active": "Active", + "inv_products": "Total products", + "inv_expiring": "Expiring (7d)", + "inv_expired": "Expired", + "inv_finished": "Finished", + "act_title": "Monthly activity", + "act_tx_month": "Movements", + "act_restock": "Restocks", + "act_use": "Usages", + "act_new_products": "New products", + "act_tx_year": "Yearly movements", + "price_cache": "Price cache", + "cache_entries": "products", + "last_backup": "Last backup", + "bring_days": "token expires in {n} days", + "bring_expired": "token expired", + "year_label": "Year {year}", + "currency_title": "Currency", + "currency_hint": "The currency used for all costs and prices in the app." + }, + "tab_general": "General", + "shopping": { + "tab": "Shopping list", + "title": "Shopping list", + "hint": "Configure the built-in shopping list or connect Bring!.", + "enable_label": "Enable shopping list", + "mode_label": "Provider", + "mode_internal": "Built-in (no Bring!)", + "mode_bring": "Bring! (external app)", + "bring_section_title": "Bring! configuration", + "ai_section_title": "AI assistance", + "smart_suggestions_label": "AI suggestions", + "forecast_label": "Forecast low-stock products", + "auto_add_label": "Auto-add to list when", + "auto_add_suffix": "remaining in stock (0 = only when empty)" + }, + "ha": { + "tab": "Home Assistant", + "title": "Home Assistant", + "hint": "Connect EverShelf to Home Assistant for automations, push notifications and REST sensors.", + "enabled": "Enable Home Assistant integration", + "connection_title": "Connection", + "url_label": "Home Assistant URL", + "url_placeholder": "http://192.168.1.50:8123", + "url_hint": "Base URL of your Home Assistant instance (e.g. http://homeassistant.local:8123).", + "token_label": "Long-Lived Access Token", + "token_hint": "Generate from HA Profile → Security → Long-Lived Access Tokens.", + "token_placeholder": "eyJhbGci...", + "token_saved": "Token saved (hidden for security)", + "test_btn": "Test connection", + "test_ok": "Connected to {version}", + "test_fail": "Connection failed: {error}", + "test_bad_token": "HA reachable but token is invalid", + "testing": "Testing…", + "error_no_url": "Please enter the Home Assistant URL first.", + "tts_title": "TTS on Smart Speaker", + "tts_hint": "Read recipe steps aloud on a Home Assistant media player.", + "tts_entity_label": "Media player entity ID", + "tts_entity_placeholder": "media_player.living_room", + "tts_entity_hint": "Entity ID of the HA media player. Find it in HA: Developer Tools → States.", + "tts_platform_label": "TTS platform", + "tts_platform_speak": "tts.speak (recommended)", + "tts_platform_notify": "notify.* (notification service)", + "tts_apply_btn": "Apply HA preset to TTS tab", + "tts_apply_hint": "Pre-fills the TTS tab with the Home Assistant URL and token.", + "tts_preset_applied": "HA preset applied to TTS tab.", + "webhook_title": "Webhook Automations", + "webhook_hint": "Send data to Home Assistant when pantry events occur. Create an HA automation with a Webhook trigger and paste the generated ID here.", + "webhook_id_label": "Webhook ID", + "webhook_id_placeholder": "evershelf_webhook_abc123", + "webhook_id_hint": "ID of the webhook created in HA. Copy from: HA → Settings → Automations → Create → Webhook Trigger.", + "webhook_events_label": "Notify on these events", + "event_expiry": "Expiring products (daily)", + "event_shopping": "Item added to shopping list", + "event_stock": "Stock level updated", + "expiry_days_label": "Expiry lead time (days)", + "expiry_days_hint": "Send the expiry alert N days before the expiry date.", + "webhook_help": "In HA: Settings → Automations → Create automation → Trigger: Webhook → copy the generated ID above.", + "notify_title": "Push Notifications", + "notify_hint": "Send push notifications to your phone via a Home Assistant notify service.", + "notify_service_label": "Notify service", + "notify_service_placeholder": "notify.mobile_app_my_phone", + "notify_service_hint": "HA notify service name (e.g. notify.mobile_app_phone). Leave empty to disable.", + "sensor_title": "REST Sensors", + "sensor_hint": "Add to configuration.yaml to create EverShelf sensors in Home Assistant.", + "sensor_copy_btn": "Copy YAML", + "sensor_copied": "YAML copied to clipboard!", + "save_btn": "Save HA settings", + "ha_hint": "If you use Home Assistant, use the Home Assistant tab to configure TTS, webhooks and sensors." } + }, + "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)", + "opened_ago_long": "Opened {n} days ago", + "opened_today_long": "Opened today", + "opened_suffix": "— Opened too long!", + "opened_suffix_ok": "— Opened (still ok)", + "opened_suffix_warning": "— Opened (check first)", + "days_compact": "{n}d", + "badge_check_soon": "Check soon" + }, + "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}", + "finished_all": "📤 {name} finished!", + "vacuum_sealed": "{name} saved as vacuum sealed", + "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", + "unknown": "Unknown error", + "prefix": "Error", + "no_inventory_entry": "No inventory entry found", + "offline_title": "No connection", + "offline_subtitle": "The app cannot reach the server. Check your Wi-Fi connection.", + "offline_checking": "Checking connection…", + "offline_restored": "Connection restored!", + "offline_continue": "Continue in offline mode", + "offline_reading_cache": "Reading from local cache", + "offline_ops_pending": "{n} operations pending", + "offline_synced": "{n} operations synced", + "offline_ai_disabled": "Not available offline", + "offline_cache_ready": "Offline — {n} items cached" + }, + "confirm_placeholder_search": null, + "confirm": { + "remove_item": "Do you really want to remove this product from inventory?", + "kiosk_exit": "Exit kiosk mode?", + "cancel": "Cancel", + "proceed": "Confirm", + "discard_one": "Discard 1 piece" + }, + "location": { + "dispensa": "Pantry", + "frigo": "Fridge", + "freezer": "Freezer" + }, + "edit": { + "title": "Edit {name}", + "unknown_hint": "Enter the product name and information", + "label_name": "🏷️ Product name", + "choose_location_title": "Which location?", + "choose_location_hint": "Choose the location to edit:", + "confirm_large_qty": "You are setting the quantity to {qty} {unit}. This seems unusually high. Confirm?" + }, + "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", + "expiry_found": "Date found", + "expiry_read_fail": "Cannot read the date.", + "expiry_raw_label": "Read" + }, + "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", + "vacuum_seal_rest": "Vacuum seal the rest" + }, + "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", + "macros_title": "Estimated Macronutrients", + "macros_proteins": "Proteins", + "macros_carbs": "Carbohydrates", + "macros_fat": "Fat", + "macros_fiber": "Fibre", + "macros_source": "Estimate based on {n} pantry 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" + }, + "about": { + "title": "About", + "version": "Version", + "report_bug": "Report a Bug", + "report_bug_hint": "Something not working? Send us a report directly from the app.", + "report_bug_modal_title": "Report a Bug", + "report_type_bug": "Bug", + "report_type_feature": "Feature", + "report_type_question": "Question", + "report_field_title": "Title", + "report_field_title_ph": "Brief description of the issue", + "report_field_desc": "Description", + "report_field_desc_ph": "Describe the issue in detail…", + "report_field_steps": "Steps to reproduce (optional)", + "report_field_steps_ph": "1. Go to…\n2. Tap…\n3. See the error…", + "report_auto_info": "Automatically attached: version {version}, language {lang}.", + "report_send_btn": "Send report", + "report_bug_sending": "Sending…", + "report_bug_sent": "Report sent — thank you!", + "report_bug_error": "Could not send the report. Check your connection.", + "changelog": "Changelog", + "github": "GitHub Repository" + }, + "export": { + "title": "Export inventory", + "hint": "Download the current inventory as CSV or open a print-ready version (PDF).", + "btn_csv": "Download CSV", + "btn_pdf": "PDF / Print", + "btn_title": "Export" + }, + "startup": { + "connecting": "Connecting to server...", + "check_php_memory": "PHP memory", + "check_php_timeout": "PHP timeout", + "check_php_upload": "PHP upload", + "check_data_dir": "Data directory", + "check_rate_limits": "Rate limits dir", + "check_backups": "Backup dir", + "check_write_test": "Disk write test", + "check_disk_space": "Disk space", + "check_db_legacy": "Legacy DB (dispensa.db)", + "check_db_connect": "Database connection", + "check_db_tables": "Database tables", + "check_db_integrity": "Database integrity", + "check_db_wal": "WAL mode", + "check_db_size": "Database size", + "check_db_rows": "Inventory data", + "check_env": ".env file", + "check_gemini": "Gemini AI key", + "check_bring_creds": "Bring! credentials", + "check_bring_token": "Bring! token", + "check_tts": "Text-to-Speech URL", + "check_scale": "Scale gateway", + "check_curl_ssl": "cURL SSL", + "check_internet": "Internet connection", + "fresh_install": "fresh install", + "warnings_found": "warnings found", + "all_ok": "System OK", + "critical_error_short": "Critical error", + "critical_error": "Critical error: the app cannot start. Check your server logs.", + "critical_error_intro": "The app cannot start due to the following issues:", + "error_network": "Cannot reach the server.", + "error_network_detail": "The browser cannot reach the PHP server.\n\nPossible causes:\n• Apache/PHP server is not running\n• Network or firewall issue\n• Incorrect app URL\n\nMake sure the server is started and try again.", + "retry": "Retry", + "syncing_local": "Syncing local data...", + "sync_done": "Local data synced" + }, + "stats_monthly": { + "title": "Monthly Stats", + "consumed": "products used", + "trend_up": "+{pct}% vs {prev}", + "trend_down": "-{pct}% vs {prev}", + "trend_same": "same pace as last month", + "added": "added", + "wasted": "wasted", + "top_used": "top used", + "top_cats": "Top categories", + "source": "Transaction history · current month" + } } \ No newline at end of file diff --git a/translations/es.json b/translations/es.json index dd7cc40..f7714bb 100644 --- a/translations/es.json +++ b/translations/es.json @@ -1,1401 +1,1406 @@ { - "app": { - "name": "EverShelf", - "loading": "Cargando..." - }, - "nav": { - "title": "EverShelf", - "home": "Inicio", - "inventory": "Despensa", - "recipes": "Recetas", - "shopping": "Compras", - "log": "Registro", - "settings": "Ajustes" - }, - "btn": { - "back": "← Volver", - "save": "💾 Guardar", - "cancel": "✕ Cancelar", - "close": "Cerrar", - "add": "✅ Añadir", - "delete": "Eliminar", - "edit": "✏️ Editar", - "use": "Usar", - "edit_item": "Editar", - "search": "🔍 Buscar", - "go": "✅ Ir", - "toggle_password": "👁️ Mostrar/Ocultar", - "load_more": "Cargar más...", - "save_config": "💾 Guardar configuración", - "save_product": "💾 Guardar producto", - "restart": "↺ Reiniciar", - "reset_default": "↺ Restablecer valores por defecto", - "save_info": "💾 Guardar información", - "retry": "🔄 Reintentar", - "yes_short": "Sí", - "no_short": "No" - }, - "form": { - "select_placeholder": "-- Seleccionar --" - }, - "locations": { - "dispensa": "Despensa", - "frigo": "Nevera", - "freezer": "Congelador", - "altro": "Otro" - }, - "categories": { - "latticini": "Lácteos", - "carne": "Carne", - "pesce": "Pescado", - "frutta": "Fruta", - "verdura": "Verduras", - "pasta": "Pasta y Arroz", - "pane": "Pan y Panadería", - "surgelati": "Congelados", - "bevande": "Bebidas", - "condimenti": "Condimentos", - "snack": "Snacks y Dulces", - "conserve": "Conservas", - "cereali": "Cereales y Legumbres", - "igiene": "Higiene", - "pulizia": "Limpieza", - "altro": "Otro", - "select": "-- Seleccionar --" - }, - "units": { - "pz": "uds", - "conf": "paq", - "g": "g", - "ml": "ml", - "pieces": "Unidades", - "grams": "Gramos", - "box": "Paquete", - "boxes": "Paquetes", - "millilitres": "Mililitros", - "from": "de" - }, - "shopping_sections": { - "frutta_verdura": "Frutas y Verduras", - "carne_pesce": "Carne y Pescado", - "latticini": "Lácteos y Frescos", - "pane_dolci": "Pan y Dulces", - "pasta": "Pasta y Cereales", - "conserve": "Conservas y Salsas", - "surgelati": "Congelados", - "bevande": "Bebidas", - "pulizia_igiene": "Limpieza e Higiene", - "altro": "Otro" - }, - "dashboard": { - "expired_title": "🚫 Caducado", - "expiring_title": "⏰ Caduca pronto", - "stats_period": "📊 Últimos 30 días", - "opened_title": "📦 Productos abiertos", - "review_title": "🔍 Por revisar", - "review_hint": "Cantidades inusuales. Confirma si son correctas o modifícalas.", - "quick_recipe": "Receta rápida con productos que caducan", - "banner_review_title": "Cantidad anormal", - "banner_review_action_ok": "Es correcto", - "banner_review_action_finish": "🗑️ Todo terminado", - "banner_review_action_edit": "Corregir", - "banner_review_action_weigh": "Pesar", - "banner_review_dismiss": "Ignorar", - "banner_prediction_title": "Consumo por revisar", - "banner_prediction_hint": "La estimación de consumo se adapta a los datos recientes: confirma solo si la cantidad actual es correcta.", - "banner_prediction_action_confirm": "Confirmar {qty} {unit}", - "banner_prediction_action_weigh": "Pesar ahora", - "banner_prediction_action_edit": "Actualizar cantidad", - "banner_expired_title": "Producto caducado", - "banner_expired_today": "Caducado hoy", - "banner_expired_days": "Caducado hace {days} días", - "banner_expired_action_use": "Usar de todas formas", - "banner_expired_action_finished": "¡Ya lo terminé!", - "banner_expired_action_throw": "Lo tiré", - "banner_expired_action_edit": "Corregir fecha", - "banner_anomaly_action_edit": "Corregir inventario", - "banner_anomaly_action_dismiss": "La cantidad es correcta", - "banner_no_expiry_title": "Caducidad faltante: {name}", - "banner_no_expiry_detail": "Este producto no tiene fecha de caducidad. ¿Quieres añadir una o confirmar que no caduca?", - "banner_no_expiry_action_set": "Establecer fecha de caducidad", - "banner_no_expiry_action_dismiss": "No caduca ✓", - "banner_no_expiry_toast_dismissed": "Marcado como «sin fecha de caducidad»", - "banner_expiring_title": "Caduca pronto", - "banner_expiring_today": "¡Caduca hoy!", - "banner_expiring_tomorrow": "Caduca mañana", - "banner_expiring_days": "Caduca en {days} días", - "banner_expiring_action_use": "Usar ahora", - "banner_finished_title": "¿terminado?", - "banner_finished_detail": "Registré que {name} llegó a cero de stock. ¿Realmente se acabó o todavía tienes algo?", - "banner_finished_action_yes": "Sí, se acabó", - "banner_finished_action_no": "No, todavía tengo", - "banner_review_unusual_pkg_title": "Tamaño de paquete inusual", - "banner_review_unusual_pkg_detail": "Configuraste un paquete de {qty} {unit} — el tamaño parece muy grande. Comprueba si es correcto o edita.", - "banner_review_low_qty_title": "Cantidad muy baja", - "banner_review_low_qty_detail": "Solo tienes {qty} en stock — parece muy poco, puede ser un error de escritura. Confirma si es correcto.", - "banner_review_high_qty_title": "Cantidad inusualmente alta", - "banner_review_high_qty_detail": "Tienes {qty} en stock — la cifra parece muy alta. Confirma si es correcto o edita.", - "banner_prediction_rate_day": "Media ~{n} {unit}/día", - "banner_prediction_rate_week": "Media ~{n} {unit}/semana", - "banner_prediction_days_ago": "Hace {n} días reabasteciste", - "banner_prediction_more": "estimación anterior: {expected} {unit}{time}; cantidad actual: {actual} {unit}.", - "banner_prediction_less": "estimación: {expected} {unit}{time}; cantidad actual: {actual} {unit}. Si tu ritmo de uso cambió, la previsión se actualiza automáticamente.", - "banner_finished_zero": "El inventario muestra cero, pero los movimientos registrados sugieren que no debería estar vacío.", - "banner_finished_expected": "Según los registros deberías tener todavía {qty} {unit}.", - "banner_finished_check": "¿Puedes comprobarlo?", - "banner_anomaly_phantom_title": "tienes más stock del esperado", - "banner_anomaly_phantom_detail": "El inventario indica {inv_qty} {unit}, pero según los registros solo deberías tener {expected_qty} {unit}. ¿Añadiste stock sin registrarlo?", - "banner_anomaly_untracked_title": "stock no registrado como entrada", - "banner_anomaly_untracked_detail": "Tienes {inv_qty} {unit} en inventario, pero las salidas registradas superan las entradas — el stock inicial probablemente nunca se añadió como transacción «entrada». Puedes corregir la cantidad o registrar las entradas faltantes.", - "banner_anomaly_ghost_title": "tienes menos stock del esperado", - "banner_anomaly_ghost_detail": "Según las operaciones registradas deberías tener {expected_qty} {unit} de {name}, pero el inventario solo muestra {inv_qty} {unit}. ¿Tomaste stock sin registrarlo?", - "consumed": "Consumido: {n} ({pct}%)", - "wasted": "Desperdiciado: {n} ({pct}%)", - "more_opened": "y {n} más abiertos...", - "banner_expired_detail": "{when} · aún tienes {qty}.", - "banner_opened_detail": "{when} en {location} · aún tienes {qty}.", - "banner_explain_title": "Pedir explicación a Gemini", - "banner_explain_btn": "Explicar", - "banner_analyzing": "🤖 Analizando…" - }, - "inventory": { - "title": "Despensa", - "filter_all": "Todo", - "search_placeholder": "🔍 Buscar producto...", - "recent_title": "🕐 Usados recientemente", - "popular_title": "⭐ Más usados", - "empty": "No hay productos aquí.\n¡Escanea un producto para añadirlo!", - "no_items_found": "No se encontraron artículos", - "qty_remainder_suffix": "restante", - "vacuum_badge": "🫙 Al vacío", - "opened_badge": "📭 Abierto", - "label_expiry": "📅 Caducidad", - "label_storage": "🫙 Almacenamiento", - "label_status": "📭 Estado", - "opened_since": "Abierto desde el {date}", - "label_position": "📍 Ubicación", - "label_quantity": "📦 Cantidad", - "label_added": "📅 Añadido", - "empty_text": "No hay productos aquí.
    ¡Escanea un producto para añadirlo!", - "empty_db": "No hay productos en la base de datos.
    ¡Escanea un producto para empezar!", - "qty_trace": "< 1" - }, - "scan": { - "title": "Escáner", - "mode_shopping": "🛒 Modo compras", - "mode_shopping_end": "✅ Finalizar compras", - "spesa_btn": "🛒 Compras", - "zoom": "Zoom", - "tab_barcode": "Código de barras", - "tab_name": "Nombre", - "tab_ai": "IA", - "recents_label": "Recientes", - "torch_hint": "Linterna", - "torch_on": "Linterna encendida", - "torch_off": "Linterna apagada", - "torch_unavailable": "Linterna no disponible en este dispositivo", - "flip_hint": "Cambiar cámara", - "flip_front": "Cámara frontal", - "flip_back": "Cámara trasera", - "num_ocr_btn": "🔢 Leer números con IA", - "num_ocr_searching": "Buscando código de barras con IA...", - "num_ocr_found": "Código encontrado: {code}", - "num_ocr_not_found": "No se encontró código de barras en la imagen", - "barcode_placeholder": "Introduce el código de barras...", - "quick_name_divider": "o escribe el nombre", - "quick_name_placeholder": "Ej.: Manzanas, Calabacín, Pan...", - "manual_entry": "✏️ Entrada manual", - "ai_identify": "🤖 Identificar con IA", - "hint": "Escanea el código de barras, escribe el nombre del producto o usa la IA para identificarlo", - "debug_toggle": "🐛 Registro de depuración", - "barcode_acquired": "🔖 Código de barras escaneado: {code}", - "scan_barcode": "🔖 Escanear código de barras", - "create_named": "Crear {name}", - "new_without_barcode": "Nuevo producto sin código de barras", - "status_ready": "Apunta la cámara al código de barras", - "status_scanning": "Escaneando...", - "status_partial": "Detectado: {code} — verificando...", - "status_invalid": "Inválido: {code} — reintentando", - "status_confirmed": "Confirmado!", - "status_parallel": "Escaneo combinado activo..." - }, - "action": { - "title": "¿Qué quieres hacer?", - "add_btn": "📥 AÑADIR", - "add_sub": "a la despensa/nevera", - "use_btn": "📤 USAR / CONSUMIR", - "use_sub": "de la despensa/nevera", - "have_title": "📦 ¡Ya en stock!", - "add_more_sub": "añadir más", - "use_qty_sub": "cuánto usaste", - "throw_btn": "🗑️ DESECHAR", - "throw_sub": "tirar", - "edit_sub": "caducidad, ubicación…", - "create_recipe_btn": "Receta" - }, - "add": { - "title": "Añadir a la despensa", - "location_label": "📍 ¿Dónde lo guardas?", - "quantity_label": "📦 Cantidad", - "conf_size_label": "📦 Cada paquete contiene:", - "conf_size_placeholder": "ej. 300", - "vacuum_label": "🫙 Al vacío", - "vacuum_hint": "La fecha de caducidad se ampliará automáticamente", - "submit": "✅ Añadir", - "purchase_type_label": "🛒 Este producto es...", - "new_btn": "🆕 Recién comprado", - "existing_btn": "📦 Ya lo tenía", - "remaining_label": "📦 Cantidad restante", - "remaining_hint": "¿Aproximadamente cuánto queda?", - "remaining_full": "🟢 Lleno", - "remaining_half": "🟠 Por la mitad", - "estimated_expiry": "Caducidad estimada:", - "suffix_freezer": "(congelador)", - "suffix_vacuum": "(al vacío)", - "hint_modify": "📝 Puedes cambiar la fecha o escanearla con la cámara", - "scan_expiry_title": "📷 Escanear fecha de caducidad", - "product_added": "✅ ¡{name} añadido!{qty}", - "suffix_freezer_vacuum": "(congelador + al vacío)", - "history_badge_tip": "Media de {n} entradas anteriores", - "vacuum_question": "¿Al vacío?", - "vacuum_saved": "🔒 ¡Al vacío!" - }, - "use": { - "title": "Usar / Consumir", - "location_label": "📍 ¿De dónde?", - "quantity_label": "¿Cuánto usaste?", - "change": "cambiar", - "partial_hint": "O especifica la cantidad usada:", - "partial_piece_hint": "¿Usaste solo una parte?", - "piece": "unidad", - "one_whole": "1 entero", - "use_all": "🗑️ Todo usado / Terminado", - "submit": "📤 Usar esta cantidad", - "available": "📦 Disponible:", - "opened_badge": "ABIERTO", - "not_in_inventory": "⚠️ Producto no en inventario.", - "expiry_warning": "⚠️ ¡Usa primero el{loc} que caduca el {date} — {when}!", - "expiry_warning_opened": "⚠️ El{loc} lleva abierto {when} — ¡úsalo primero!", - "throw_title": "🗑️ Desechar producto", - "throw_all": "🗑️ Desechar TODO ({qty})", - "throw_qty_label": "¿Cuánto desechar?", - "throw_qty_hint": "o introduce una cantidad:", - "throw_partial_btn": "🗑️ Desechar esta cantidad", - "when_expired": "caducado hace {n} días", - "when_today": "caduca hoy", - "when_tomorrow": "caduca mañana", - "when_days": "caduca en {n} días", - "toast_used": "📤 {qty} de {name} usado", - "toast_bring": "🛒 Producto terminado → añadido a Bring!", - "toast_opened_finished": "🔓 ¡Paquete abierto de {name} terminado!", - "disambiguation_hint": "¿Qué quieres decir con «todo terminado»?", - "disambiguation_all": "🗑️ Terminar TODO ({qty})", - "error_exceeds_stock": "⚠️ ¡No puedes usar más de lo que tienes disponible!", - "use_all_confirm_title": "✅ Terminar todo", - "use_all_confirm_msg": "Confirma que has terminado el producto:", - "use_all_confirm_btn": "✅ Sí, terminado", - "throw_all_confirm_title": "🗑️ Desechar todo", - "throw_all_confirm_msg": "¿Realmente quieres tirar todo el producto?", - "throw_all_confirm_btn": "🗑️ Sí, desechar" - }, - "product": { - "title_new": "Nuevo producto", - "title_edit": "Editar producto", - "ai_fill": "📷 Sacar foto e identificar con IA", - "ai_fill_hint": "La IA rellenará automáticamente los campos del producto", - "name_label": "🏷️ Nombre del producto *", - "name_placeholder": "Ej.: Leche entera, Pasta penne...", - "brand_label": "🏢 Marca", - "brand_placeholder": "Ej.: Barilla, Danone, Heinz...", - "category_label": "📂 Categoría", - "unit_label": "📏 Unidad de medida", - "default_qty_label": "🔢 Cantidad por defecto", - "conf_size_label": "📦 Cada paquete contiene:", - "conf_size_placeholder": "ej. 300", - "notes_label": "📝 Notas", - "notes_placeholder": "Ej.: sin lactosa, ecológico, guardar en nevera tras abrir...", - "barcode_label": "🔖 Código de barras", - "barcode_placeholder": "Código de barras (si disponible)", - "barcode_hint": "⚠️ ¡Añade el código de barras para la próxima vez solo tendrás que escanear!", - "submit": "💾 Guardar producto", - "name_required": "Introduce el nombre del producto", - "conf_size_required": "Especifica el contenido del paquete", - "expiry_estimated": "Caducidad estimada:", - "scan_expiry": "Escanear fecha de caducidad", - "expiry_hint": "📝 Puedes editar la fecha o escanearla con la cámara", - "add_batch": "📦 + Lote con fecha diferente", - "package_info": "📦 Paquete: {info}", - "edit_catalog": "⚙️ Editar información del producto (nombre, marca, categoría…)", - "not_recognized": "⚠️ Producto no reconocido", - "edit_info": "✏️ Editar información", - "modify_details": "EDITAR\ncaducidad, ubicación…", - "already_in_pantry": "📋 Ya en la despensa", - "no_barcode": "Sin código de barras", - "unknown_product": "Producto no reconocido", - "edit_name_brand": "Editar nombre/marca", - "weight_label": "Peso", - "origin_label": "Origen", - "labels_label": "Etiquetas", - "select_variant": "Selecciona la variante exacta o usa los datos de IA:" - }, - "products": { - "title": "📦 Todos los productos", - "search_placeholder": "🔍 Buscar producto...", - "empty": "No hay productos en la base de datos.\n¡Escanea un producto para empezar!", - "no_category": "No hay productos en esta categoría" - }, - "recipes": { - "title": "🍳 Recetas", - "generate": "✨ Generar nueva receta", - "archive_empty": "No hay recetas guardadas. ¡Genera tu primera receta!", - "dialog_title": "🍳 Receta", - "dialog_desc": "Generaré una receta saludable usando ingredientes de la despensa, priorizando los productos que caducan.", - "meal_label": "🕐 ¿Qué comida?", - "persons_label": "👥 ¿Para cuántas personas?", - "meal_type_label": "🎯 Tipo de comida", - "opt_fast": "⚡ Comida rápida", - "opt_light": "🥗 Apetito ligero", - "opt_expiry": "⏰ Priorizar productos que caducan", - "opt_healthy": "💚 Extra saludable", - "opt_opened": "📦 Priorizar productos abiertos", - "opt_zero_waste": "♻️ Cero desperdicio", - "generate_btn": "✨ Generar receta", - "loading_msg": "Preparando tu receta...", - "start_cooking": "👨‍🍳 Modo cocina", - "regenerate": "🔄 Generar otra", - "regen_choice_title": "¿Qué quieres hacer con esta receta?", - "regen_replace": "🔄 Generar otra (descartar esta)", - "regen_save_new": "💾 Guardar en el archivo y generar una nueva", - "close_btn": "✅ Cerrar", - "ingredients_title": "🧾 Ingredientes", - "tools_title": "Equipo necesario", - "steps_title": "👨‍🍳 Pasos", - "no_steps": "No hay pasos disponibles", - "generate_error": "Error de generación", - "persons_short": "com.", - "use_ingredient_title": "Usar ingrediente", - "recipe_qty_label": "Receta", - "from_where_label": "¿De dónde?", - "amount_label": "Cuánto", - "use_amount_btn": "Usar esta cantidad", - "use_all_btn": "Usar TODO / Terminado", - "packs_label": "Paquetes", - "quantity_in_total": "Cantidad en {unit} (total: {total})", - "packs_of_have": "Paquetes de {size} (tienes {count} paquetes)", - "scale_wait_stable": "Espera 10s de peso estable para el relleno automático…", - "ingredient_scaled_toast": "📦 ¡Ingrediente deducido de la despensa!", - "finished_added_bring_toast": "🛒 Producto terminado → ¡añadido a Bring!", - "load_error": "Error de carga", - "favorite": "Añadir a favoritos", - "unfavorite": "Quitar de favoritos", - "adjust_persons": "Personas" - }, - "shopping": { - "title": "🛒 Lista de la compra", - "bring_loading": "Conectando a Bring!...", - "bring_not_configured": "Bring! no está configurado. Añade tu email y contraseña en los ajustes.", - "tab_to_buy": "🛍️ Por comprar", - "tab_forecast": "🧠 Previsión", - "total_label": "💰 Total estimado", - "section_to_buy": "🛍️ Por comprar", - "suggestions_title": "💡 Sugerencias IA", - "suggestions_add": "✅ Añadir selección a Bring!", - "search_prices": "🔍 Buscar todos los precios", - "suggest_btn": "Sugerir qué comprar", - "smart_title": "🧠 Predicciones inteligentes", - "smart_empty": "No hay predicciones disponibles.
    Añade productos a tu despensa para recibir predicciones inteligentes.", - "smart_filter_all": "Todo", - "smart_filter_critical": "🔴 Urgente", - "smart_filter_high": "🟠 Pronto", - "smart_filter_medium": "🟡 Planificar", - "smart_filter_low": "🟢 Previsión", - "smart_add": "🛒 Añadir selección a Bring!", - "empty": "¡Lista de la compra vacía!\nUsa el botón de abajo para generar sugerencias.", - "already_in_list": "🛒 \"{name}\" ya está en la lista de la compra", - "already_in_list_short": "ℹ️ Ya en la lista de la compra", - "add_prompt": "¿Quieres añadirlo a la lista de la compra?", - "smart_already": "📊 Las predicciones de compra ya predicen {name}", - "all_searched": "Todos los productos ya han sido buscados. Usa 🔄 para buscar individualmente.", - "search_complete": "Búsqueda completada: {count} productos", - "removed_sufficient": "🧹 {removed} producto(s) con stock suficiente eliminado(s) de la lista", - "suggest_buy": "🛒 Comprar: {qty} {unit}", - "suggest_buy_approx": "🛒 Al menos: {qty} {unit}", - "suggest_buy_tip": "Cantidad sugerida basada en tus últimos 14 días de consumo", - "suggest_buy_approx_tip": "Estimación mínima basada en el consumo (compra el tamaño de paquete más cercano)", - "bring_badge": "🛒 Ya en Bring!", - "add_urgent_toast": "🔴 {n} producto(s) urgente(s) añadido(s) automáticamente a Bring!", - "migration_done": "✅ {migrated} actualizado(s), {skipped} ya estaban ok", - "added_to_bring": "🛒 {n} productos añadidos a Bring!", - "added_to_bring_skip": "{n} ya presentes", - "all_on_bring": "¡Todos los productos ya estaban en Bring!", - "freq_high": "📈 Frecuente", - "freq_regular": "📊 Regular", - "freq_occasional": "📉 Ocasional", - "out_of_stock": "Sin stock", - "scan_toast": "📷 Escanear: {name}", - "empty_category": "No hay productos en esta categoría", - "session_empty": "🛒 Sin productos aún", - "urgency_critical": "Urgente", - "urgency_high": "Pronto", - "urgency_medium": "Planificar", - "urgency_low": "Previsión", - "urgency_medium_short": "Medio", - "urgency_low_short": "Ok", - "tag_urgent": "🔴 Urgente", - "tag_priority": "⭐ Prioridad", - "tag_check": "✅ Comprobar", - "smart_already_predicted": "📊 Las predicciones inteligentes ya predicen {name}{urgency}.", - "item_removed": "✅ ¡{name} eliminado de la lista!", - "urgency_spec_critical": "⚡ Urgente", - "urgency_spec_high": "🟠 Pronto", - "bring_add_n": "Añadir {n} a Bring!", - "bring_add_selected": "Añadir selección a Bring!", - "bring_adding": "Añadiendo...", - "bring_added_one": "1 producto añadido a Bring!", - "bring_added_many": "{n} productos añadidos a Bring!", - "bring_skipped": "({n} ya en la lista)", - "force_sync": "Forzar sincronización de Bring!", - "scan_target_label": "Estás buscando", - "scan_target_found": "¡Encontrado! Eliminar de la lista", - "bring_add_one": "Añadir 1 producto a Bring!", - "bring_add_many": "Añadir {n} productos a Bring!", - "syncing": "Sincronizando…", - "sync_done": "Sincronización completada", - "price_searching": "Buscando...", - "search_action": "Buscar", - "open_action": "Abrir", - "not_found": "No encontrado", - "search_price": "Buscar precio", - "tap_to_scan": "Toca para escanear", - "tag_title": "Etiqueta", - "remove_title": "Eliminar", - "found_count": "{found}/{total} productos encontrados", - "savings_offers": "· 🏷️ Ahorras {amount}€ con ofertas", - "searching_progress": "Buscando {current}/{total}...", - "remove_error": "Error al eliminar", - "btn_fetch_prices": "Buscar precios", - "price_total_label": "💰 Total estimado:", - "price_loading": "Buscando precios…", - "price_not_found": "precio n/d", - "suggest_loading": "Analizando...", - "suggest_error": "Error al generar sugerencias", - "priority_high": "Alta", - "priority_medium": "Media", - "priority_low": "Baja", - "smart_last_update": "Actualizado {time}", - "names_already_updated": "Todos los nombres ya están actualizados" - }, - "ai": { - "title": "🤖 Identificación IA", - "capture": "📸 Sacar foto", - "retake": "🔄 Repetir", - "hint": "Saca una foto del producto y la IA intentará identificarlo", - "identifying": "🤖 Identificando producto...", - "no_api_key": "⚠️ Clave API de Gemini no configurada.\nAñade GEMINI_API_KEY al archivo .env en el servidor.", - "fields_filled": "✅ Campos rellenados por IA", - "use_data": "✅ Usar datos de IA", - "use_data_no_barcode": "✅ Usar datos de IA (sin código de barras)" - }, - "log": { - "title": "📒 Registro de operaciones", - "type_added": "Añadido", - "type_waste": "Desechado", - "type_used": "Usado", - "type_bring": "Añadido a Bring!", - "undone_badge": "Deshecho", - "undo_title": "Deshacer esta operación", - "load_error": "Error al cargar el registro", - "empty": "No hay operaciones registradas.", - "undo_action_remove": "eliminación de", - "undo_action_restore": "reabastecimiento de", - "undo_confirm": "¿Deshacer esta operación?\n→ {action} {name}", - "undo_success": "↩ Operación deshecha para {name}", - "already_undone": "Operación ya deshecha", - "too_old": "No se pueden deshacer operaciones de más de 24 horas", - "undo_error": "Error al deshacer", - "recipe_prefix": "Receta" - }, - "chat": { - "title": "Chef Gemini", - "welcome": "¡Hola! Soy tu asistente de cocina", - "welcome_desc": "¡Pídeme que te prepare un zumo, un snack, un plato rápido… Conozco tu despensa, tus electrodomésticos y tus preferencias!", - "suggestion_snack": "🍿 Snack rápido", - "suggestion_juice": "🥤 Zumo/Batido", - "suggestion_light": "🥗 Algo ligero", - "suggestion_expiry": "⏰ Usar productos que caducan", - "clear": "Nueva conversación", - "placeholder": "Pregunta algo...", - "cleared": "Chat borrado", - "suggestion_snack_text": "¿Qué puedo preparar como snack rápido?", - "suggestion_juice_text": "Hazme un zumo o batido con lo que tengo", - "suggestion_light_text": "Tengo hambre pero quiero algo ligero", - "suggestion_expiry_text": "¿Qué está a punto de caducar y cómo puedo usarlo?", - "transfer_to_recipes": "Transferir a recetas", - "transferring": "Transfiriendo...", - "transferred": "¡Añadido a recetas!", - "open_recipe": "Abrir receta", - "quick_recipe_prompt": "¡Sugiere una receta rápida PARA UNA PERSONA usando los productos que caducan primero! Ignora el congelador, céntrate en la nevera y la despensa." - }, - "cooking": { - "close": "Cerrar", - "tts_btn": "Leer en voz alta", - "restart": "↺ Reiniciar", - "replay": "🔊 Repetir", - "timer": "⏱️ {time} · Temporizador", - "prev": "◀ Anterior", - "next": "Siguiente ▶", - "ingredient_used": "✔️ Deducido", - "ingredient_use_btn": "📦 Usar", - "ingredient_deduct_title": "Deducir de la despensa", - "timer_expired_tts": "¡Temporizador {label} finalizado!", - "timer_warning_tts": "¡Atención! {label}: ¡10 segundos restantes!", - "recipe_done_tts": "¡Receta completada! ¡Buen provecho!", - "expires_chip": "cad. {date}", - "finish": "✅ Finalizar", - "step_fallback": "Paso {n}", - "zerowaste_label": "♻️ Desperdicio", - "zerowaste_tip_title": "Consejo sin desperdicios" - }, - "settings": { - "title": "⚙️ Ajustes", - "tab_api": "Claves API", - "tab_bring": "Bring!", - "tab_recipe": "Recetas", - "tab_mealplan": "Plan semanal", - "tab_appliances": "Electrodomésticos", - "tab_spesa": "Compra online", - "tab_camera": "Cámara", - "tab_security": "Seguridad", - "tab_tts": "Voz (TTS)", - "tab_language": "Idioma", - "tab_scale": "Báscula inteligente", - "gemini": { - "title": "🤖 Google Gemini IA", - "hint": "Clave API para identificación de productos, fechas de caducidad y recetas.", - "key_label": "Clave API Gemini" - }, - "bring": { - "title": "🛒 Lista de la compra Bring!", - "hint": "Credenciales para la integración con la lista de la compra Bring!", - "email_label": "📧 Email Bring!", - "password_label": "🔒 Contraseña Bring!" - }, - "price": { - "title": "💰 Estimación de precios (IA)", - "hint": "Mostrar el coste estimado por producto en la lista de la compra usando IA.", - "enabled_label": "Activar estimación de precios", - "country_label": "🌍 País de referencia", - "currency_label": "💱 Moneda", - "update_label": "🔄 Actualizar precios cada", - "update_suffix": "meses" - }, - "recipe": { - "title": "🍳 Preferencias de recetas", - "hint": "Configura las opciones predeterminadas para la generación de recetas.", - "persons_label": "👥 Raciones por defecto", - "options_label": "🎯 Opciones de receta por defecto", - "fast": "⚡ Comida rápida", - "light": "🥗 Comida ligera", - "expiry": "⏰ Prioridad caducidad", - "healthy": "💚 Extra saludable", - "opened": "📦 Prioridad productos abiertos", - "zerowaste": "♻️ Cero desperdicio", - "dietary_label": "🚫 Intolerancias / Restricciones", - "dietary_placeholder": "Ej.: sin gluten, sin lactosa, vegetariano..." - }, - "mealplan": { - "title": "📅 Plan de comidas semanal", - "hint": "Establece el tipo de comida para cada día. Se usará como guía en la generación de recetas.", - "enabled": "✅ Activar plan semanal", - "legend": "🌤️ = Almuerzo  ·  🌙 = Cena  ·  Toca un badge para cambiarlo.", - "types_title": "📋 Tipos disponibles", - "reset_btn": "↺ Restaurar valores por defecto" - }, - "appliances": { - "title": "🔌 Electrodomésticos disponibles", - "hint": "Indica los electrodomésticos que tienes. Se tendrán en cuenta en la generación de recetas.", - "new_placeholder": "Ej.: Panificadora, Thermomix, Freidora de aire...", - "quick_title": "Añadir rápido:", - "oven": "🔥 Horno", - "microwave": "📡 Microondas", - "air_fryer": "🍟 Freidora de aire", - "bread_maker": "🍞 Panificadora", - "bimby": "🤖 Thermomix/Cookeo", - "mixer": "🌀 Robot de cocina", - "steamer": "♨️ Vaporera", - "pressure_cooker": "🫕 Olla a presión", - "toaster": "🍞 Tostadora", - "blender": "🍹 Batidora", - "empty": "No hay electrodomésticos añadidos" - }, - "spesa": { - "title": "🛍️ Compra online", - "hint": "Configura el proveedor de compra online.", - "provider_label": "🏪 Proveedor", - "email_label": "📧 Email", - "password_label": "🔒 Contraseña", - "login_btn": "🔐 Iniciar sesión", - "ai_prompt_label": "🤖 Prompt IA de selección de producto", - "ai_prompt_placeholder": "Instrucciones para la IA al elegir entre varios productos...", - "ai_prompt_hint": "La IA usa este prompt para elegir el producto más apropiado de los resultados. Deja vacío para el comportamiento por defecto.", - "configure_first": "Configura primero la compra online en ajustes", - "missing_credentials": "Introduce email y contraseña", - "login_in_progress": "Iniciando sesión...", - "login_error_prefix": "Error:", - "login_network_error_prefix": "Error de red:", - "login_success_default": "¡Inicio de sesión exitoso!", - "result_name_label": "Nombre", - "result_card_label": "Tarjeta", - "result_pickup_label": "Punto de recogida", - "result_points_label": "Puntos de fidelidad", - "connected_relogin": "✅ Conectado — Iniciar sesión de nuevo", - "connected_as": "Conectado como {name}" - }, - "camera": { - "title": "📷 Cámara", - "hint": "Elige qué cámara usar para el escaneo de códigos de barras e identificación IA.", - "device_label": "📸 Cámara por defecto", - "back": "📱 Trasera (por defecto)", - "front": "🤳 Frontal", - "devices_hint": "Si tienes varias cámaras, puedes seleccionar una específica de la lista de arriba tras conceder los permisos.", - "detect_btn": "🔄 Detectar cámaras" - }, - "security": { - "title": "🔒 Certificado HTTPS", - "hint": "Si el navegador muestra el error «Tu conexión no es privada» (ERR_CERT_AUTHORITY_INVALID), necesitas instalar el certificado CA en el dispositivo.", - "download_btn": "📥 Descargar certificado CA", - "token_title": "🔑 Token de ajustes", - "token_label": "Token de acceso", - "token_hint": "Si `SETTINGS_TOKEN` está configurado en el `.env` del servidor, introduce el token aquí antes de guardar los ajustes. Deja vacío si no está configurado.", - "token_placeholder": "(vacío = sin protección)", - "token_required_hint": "🔒 Este servidor requiere un token para guardar los ajustes.", - "cert_instructions": "Instrucciones para Chrome (Android):
    1. Descarga el certificado de arriba
    2. Ve a Ajustes → Seguridad y privacidad → Más ajustes de seguridad → Instalar desde almacenamiento
    3. Selecciona el archivo EverShelf_CA.crt descargado
    4. Elige «CA» y confirma
    5. Reinicia Chrome

    Instrucciones para Chrome (PC):
    1. Descarga el certificado de arriba
    2. Ve a chrome://settings/certificates
    3. Pestaña «Autoridades» → Importar → selecciona el archivo
    4. Marca «Confiar en este certificado para identificar sitios web»
    5. Reinicia Chrome" - }, - "tts": { - "title": "🔊 Voz & TTS", - "hint": "Configura la síntesis de voz mediante cualquier API REST externa. Los pasos de la receta y los temporizadores expirados se enviarán al endpoint configurado.", - "enabled": "✅ Activar TTS", - "engine_label": "⚙️ Motor TTS", - "engine_browser": "🔇 Navegador (sin conexión, sin configuración requerida)", - "engine_server": "🌐 Servidor externo (Home Assistant, API REST...)", - "voice_label": "🗣️ Voz", - "rate_label": "⚡ Velocidad", - "pitch_label": "🎵 Tono", - "url_label": "🌐 URL del endpoint", - "method_label": "📡 Método HTTP", - "auth_label": "🔐 Autenticación", - "auth_bearer": "Bearer Token", - "auth_custom": "Cabecera personalizada", - "auth_none": "Ninguna", - "token_label": "🔑 Bearer Token", - "custom_header_name": "📋 Nombre de cabecera", - "custom_header_value": "📋 Valor de cabecera", - "content_type_label": "📄 Content-Type", - "payload_key_label": "🗝️ Campo de texto en el payload", - "payload_key_hint": "Nombre del campo JSON que contendrá el texto a leer (ej.: message, text).", - "extra_fields_label": "➕ Campos adicionales (JSON)", - "extra_fields_placeholder": "{\"entity_id\": \"media_player.salon\"}", - "extra_fields_hint": "Campos adicionales a incluir en el payload, en formato JSON. Deja vacío si no es necesario.", - "test_btn": "🔊 Enviar voz de prueba", - "voices_loading": "Cargando voces…", - "voice_not_supported": "Voz no compatible con este navegador", - "voices_none": "No hay voces disponibles en este dispositivo", - "voices_hint": "Las voces disponibles dependen del SO y el navegador. Pulsa ↺ si la lista no carga.", - "url_missing": "⚠️ URL del endpoint faltante.", - "test_sending": "⏳ Enviando…", - "test_ok": "✅ Respuesta {code} — comprueba que el altavoz haya hablado.", - "heard_question": "¿Has escuchado la voz?", - "heard_yes": "Sí, la escuché", - "heard_no": "No, no escuché nada", - "test_ok_kiosk": "TTS funcionando.", - "test_fail_steps": "Comprueba: 1) el volumen del multimedia no es 0; 2) Google Text-to-Speech está instalado y actualizado; 3) el paquete de voz español está descargado en la configuración TTS de Android." - }, - "language": { - "title": "🌐 Idioma", - "hint": "Selecciona el idioma de la interfaz.", - "label": "🌐 Idioma", - "restart_notice": "La página se recargará para aplicar el nuevo idioma." - }, - "screensaver": { - "label": "Activar protector de pantalla", - "card_title": "🌙 Protector de pantalla", - "card_hint": "Muestra un reloj con información útil después de 5 minutos de inactividad. Desactivado por defecto.", - "timeout_1": "1 minuto", - "timeout_2": "2 minutos", - "timeout_5": "5 minutos", - "timeout_10": "10 minutos", - "timeout_15": "15 minutos", - "timeout_30": "30 minutos", - "timeout_60": "1 hora", - "start_after": "⏱️ Iniciar tras" - }, - "scale": { - "title": "⚖️ Báscula inteligente", - "hint": "Conecta una báscula Bluetooth mediante la pasarela Android para leer el peso automáticamente.", - "tab": "Báscula inteligente", - "enabled": "✅ Activar báscula inteligente", - "url_label": "🌐 URL de la pasarela WebSocket", - "url_placeholder": "ws://192.168.1.x:8765", - "url_hint": "URL mostrada por la app Android (misma red Wi-Fi). Ej.:", - "test_btn": "🔗 Probar conexión", - "download_btn": "📥 Descargar pasarela Android (APK)", - "download_hint": "App Android que conecta tu báscula BLE con EverShelf.", - "download_sub": "Fuente: evershelf-scale-gateway/ en la raíz del proyecto", - "live_weight": "peso en tiempo real", - "auto_reconnect": "🔁 Reconexión: automática", - "kiosk_title": "📡 Báscula BLE integrada en el kiosco", - "kiosk_hint": "La báscula está gestionada directamente por la pasarela BLE interna del kiosco. Para vincular un nuevo dispositivo, usa el asistente de configuración.", - "kiosk_reconfigure": "🔄 Reconfigurar báscula BLE", - "ble_protocols": "

    🔌 Protocolos BLE soportados:

    " - }, - "kiosk": { - "hint": "Convierte una tableta Android en un panel EverShelf permanente con pasarela BLE integrada.", - "download_btn": "📥 Descargar EverShelf Kiosk (APK)", - "download_sub": "Modo kiosco a pantalla completa + pasarela de báscula integrada. Fuente: evershelf-kiosk/", - "native_title": "Configuración del kiosco", - "native_hint": "URL del servidor, báscula BLE, protector de pantalla y asistente de configuración.", - "native_btn": "Abrir configuración del kiosco", - "native_tap_hint": "Toca el botón de engranaje en la parte superior derecha", - "native_update_hint": "Actualiza la app del kiosco para usar esta función", - "update_title": "Actualización del kiosco", - "check_updates_btn": "🔍 Buscar actualizaciones", - "needs_update": "⚠️ El kiosco instalado no admite esta función. Actualiza la app del kiosco para activarla." - }, - "saved": "✅ ¡Configuración guardada!", - "saved_local": "✅ Configuración guardada localmente", - "saved_local_error": "⚠️ Guardado localmente, error del servidor: {error}", - "theme": { - "title": "🌙 Apariencia", - "hint": "Elige el tema de la interfaz.", - "label": "🌙 Tema", - "off": "☀️ Claro", - "on": "🌙 Oscuro", - "auto": "🔄 Automático (sistema)" - }, - "zerowaste": { - "card_title": "♻️ Consejos sin desperdicios", - "card_hint": "Durante la cocción, muestra consejos sobre cómo reutilizar los restos generados en cada paso (peladuras, agua de cocción, etc.). Desactivado por defecto.", - "label": "Mostrar consejos durante la cocción" - }, - "backup": { - "tab": "Copia de seguridad", - "local_title": "Copia local", - "local_hint": "Instantánea diaria de la base de datos. Configura cuántos días de copias de seguridad conservar.", - "enabled": "Activar copia de seguridad diaria automática", - "retention_days": "Retención (días)", - "retention_info": "Las copias se conservan durante", - "backup_now": "Hacer copia ahora", - "backing_up": "Haciendo copia…", - "backed_up": "Copia completada", - "backup_error": "Error en la copia", - "last_backup": "Última copia", - "no_backup_yet": "Aún no se ha creado ninguna copia", - "list_empty": "No hay copias disponibles", - "restore_btn": "Restaurar", - "restore_confirm": "Restaurar la copia", - "delete_btn": "Eliminar", - "delete_confirm": "Eliminar la copia", - "gdrive_title": "Google Drive", - "gdrive_hint": "Copias de seguridad automáticas en Google Drive via OAuth 2.0. No se requieren bibliotecas externas.", - "gdrive_enabled": "Activar copia en Google Drive", - "gdrive_folder_id": "ID de carpeta de Drive", - "gdrive_folder_id_hint": "Copia el ID desde la URL de la carpeta de Drive: …/folders/ID", - "gdrive_retention_days": "Retención en Drive (días, 0=mantener todo)", - "gdrive_test": "Probar conexión", - "gdrive_ok": "Conexión exitosa!", - "gdrive_error": "Conexión fallida", - "gdrive_push_now": "Subir a Drive ahora", - "gdrive_pushing": "Subiendo…", - "gdrive_pushed": "Subido a Drive", - "gdrive_wizard_hint": "Opcional: copia de seguridad diaria automática en Google Drive via OAuth 2.0.", - "gdrive_skip": "Omitir — configurar después en Ajustes", - "gdrive_client_id": "Client ID", - "gdrive_client_secret": "Client Secret", - "gdrive_redirect_uri_hint": "Agrega http://localhost como URI de redireccionamiento autorizado en Google Cloud Console. Funciona en cualquier servidor, incluso sin dominio público.", - "gdrive_code_title": "Pegar la URL o el código de autorización", - "gdrive_code_hint": "Tras autorizar, el navegador abrirá http://localhost y puede mostrar un error de conexión — es normal. Copia la URL de la barra de direcciones (ej. http://localhost/?code=4%2F0A...) y pégala aquí.", - "gdrive_code_submit": "Confirmar", - "gdrive_code_empty": "Pega primero la URL o el código de autorización", - "gdrive_redirect_uri_label": "URI de redirección (agregar en Google Cloud Console):", - "gdrive_oauth_authorize": "Autorizar con Google", - "gdrive_oauth_authorized": "Autorizado", - "gdrive_oauth_not_authorized": "Aún no autorizado", - "gdrive_oauth_window_opened": "Ventana abierta — autoriza y regresa aquí", - "gdrive_oauth_how_to": "Cómo configurar OAuth 2.0 (paso a paso)", - "gdrive_oauth_steps": "
  • Ve a console.cloud.google.com y selecciona tu proyecto
  • Habilita la API de Google Drive: API y servicios → Habilitar API → Google Drive API
  • Ve a API y servicios → Credenciales → Crear credenciales → ID de cliente OAuth
  • Tipo de aplicación: Aplicación web; agrega la URL mostrada abajo como URI de redirección autorizado
  • Copia el Client ID y el Client Secret en los campos de arriba y guarda
  • Haz clic en Autorizar con Google: inicia sesión en tu cuenta de Google y concede acceso
  • La ventana se cierra automáticamente al finalizar y las copias de seguridad están listas
  • " - }, - "shopping": { - "tab": "Lista de la compra", - "title": "Lista de la compra", - "hint": "Configura la lista de la compra integrada o conecta Bring!.", - "enable_label": "Activar lista de la compra", - "mode_label": "Proveedor", - "mode_internal": "Integrado (sin Bring!)", - "mode_bring": "Bring! (app externa)", - "bring_section_title": "Configuración de Bring!", - "ai_section_title": "Asistencia IA", - "smart_suggestions_label": "Sugerencias IA", - "forecast_label": "Previsión de productos por agotar", - "auto_add_label": "Añadir automáticamente cuando", - "auto_add_suffix": "restante en stock (0 = solo cuando se agota)" - }, - "ha": { - "tab": "Home Assistant", - "title": "Home Assistant", - "hint": "Conecta EverShelf a Home Assistant para automatizaciones, notificaciones push y sensores REST.", - "enabled": "Activar integración con Home Assistant", - "connection_title": "Conexión", - "url_label": "URL de Home Assistant", - "url_placeholder": "http://192.168.1.50:8123", - "url_hint": "URL base de tu instancia de Home Assistant.", - "token_label": "Token de acceso de larga duración", - "token_hint": "Genera desde Perfil HA → Seguridad → Tokens de acceso de larga duración.", - "token_placeholder": "eyJhbGci...", - "token_saved": "Token guardado (oculto por seguridad)", - "test_btn": "Probar conexión", - "test_ok": "Conectado a {version}", - "test_fail": "Conexión fallida: {error}", - "test_bad_token": "HA accesible pero el token no es válido", - "testing": "Probando…", - "error_no_url": "Por favor, introduce primero la URL de Home Assistant.", - "tts_title": "TTS en altavoz inteligente", - "tts_hint": "Lee los pasos de la receta en un reproductor de medios de Home Assistant.", - "tts_entity_label": "Entity ID del reproductor multimedia", - "tts_entity_placeholder": "media_player.salon", - "tts_entity_hint": "ID de entidad del reproductor multimedia HA. Encuéntralo en HA: Herramientas para desarrolladores → Estados.", - "tts_platform_label": "Plataforma TTS", - "tts_platform_speak": "tts.speak (recomendado)", - "tts_platform_notify": "notify.* (servicio de notificaciones)", - "tts_apply_btn": "Aplicar preset HA a la pestaña TTS", - "tts_apply_hint": "Pre-rellena la pestaña TTS con la URL y el token de Home Assistant.", - "tts_preset_applied": "Preset HA aplicado a la pestaña TTS.", - "webhook_title": "Automatizaciones Webhook", - "webhook_hint": "Envía datos a Home Assistant cuando ocurren eventos en la despensa.", - "webhook_id_label": "ID de Webhook", - "webhook_id_placeholder": "evershelf_webhook_abc123", - "webhook_id_hint": "ID del webhook creado en HA. Copia desde: HA → Ajustes → Automatizaciones → Crear → Disparador Webhook.", - "webhook_events_label": "Notificar en estos eventos", - "event_expiry": "Productos próximos a caducar (diario)", - "event_shopping": "Artículo añadido a la lista de compras", - "event_stock": "Nivel de stock actualizado", - "expiry_days_label": "Antelación de caducidad (días)", - "expiry_days_hint": "Enviar alerta de caducidad N días antes de la fecha.", - "webhook_help": "En HA: Ajustes → Automatizaciones → Crear automatización → Disparador: Webhook → copia el ID generado.", - "notify_title": "Notificaciones push", - "notify_hint": "Envía notificaciones push a tu teléfono mediante un servicio notify de Home Assistant.", - "notify_service_label": "Servicio notify", - "notify_service_placeholder": "notify.mobile_app_mi_telefono", - "notify_service_hint": "Nombre del servicio notify de HA. Déjalo vacío para desactivar.", - "sensor_title": "Sensores REST", - "sensor_hint": "Añade a configuration.yaml para crear sensores de EverShelf en Home Assistant.", - "sensor_copy_btn": "Copiar YAML", - "sensor_copied": "¡YAML copiado al portapapeles!", - "save_btn": "Guardar ajustes HA", - "ha_hint": "Si usas Home Assistant, utiliza la pestaña Home Assistant para configurar TTS, webhooks y sensores." - } - }, - "expiry": { - "today": "HOY", - "tomorrow": "Mañana", - "days": "{days} días", - "expired_days": "hace {days}d", - "expired_yesterday": "Ayer", - "expired_today": "Hoy", - "badge_today": "⚠️ ¡Caduca hoy!", - "badge_tomorrow": "⏰ Mañana", - "badge_tomorrow_long": "⏰ Caduca mañana", - "badge_days": "⏰ {n} días", - "badge_expired_ago": "⚠️ Caducado hace {n}d", - "badge_expired": "⛔ ¡Caducado!", - "badge_stable": "✅ Estable", - "badge_expiring_short": "⏰ Cad. en {n}d", - "badge_ok_still": "✅ Aún {n}d", - "badge_expires_red": "🔴 Cad. en {n}d", - "badge_expires_yellow": "🟡 Cad. en {n}d", - "badge_expired_bare": "⚠️ Caducado", - "badge_expires_warn": "⚠️ Cad. en {n}d", - "badge_days_left": "⏳ ~{n}d restantes", - "days_approx": "~{n} días", - "weeks_approx": "~{n} semanas", - "months_approx": "~{n} meses", - "years_approx": "~{n} años", - "expired_today_long": "Caducado hoy", - "expired_ago_long": "Caducado hace {n} días", - "expired_suffix": "— ¡Caducado!", - "expired_suffix_ok": "— Caducado (aún ok)", - "expired_suffix_warning": "— Caducado (comprobar primero)", - "opened_ago_long": "Abierto hace {n} días", - "opened_today_long": "Abierto hoy", - "opened_suffix": "— ¡Abierto demasiado tiempo!", - "opened_suffix_ok": "— Abierto (aún ok)", - "opened_suffix_warning": "— Abierto (comprobar primero)", - "days_compact": "{n}d", - "badge_check_soon": "Comprobar pronto" - }, - "status": { - "ok": "OK", - "check": "Comprobar", - "discard": "Desechar", - "tip_freezer_ok": "En congelador: aún seguro (~{n}d de margen)", - "tip_freezer_check": "En el congelador mucho tiempo, puede haber perdido calidad. Consumir pronto", - "tip_freezer_danger": "En el congelador demasiado tiempo, riesgo de quemadura por congelación y degradación", - "tip_highRisk_check": "Caducado recientemente, comprueba el olor y el aspecto antes de consumir", - "tip_highRisk_danger": "Producto perecedero caducado: desechar por seguridad", - "tip_medRisk_check1": "Comprueba el aspecto y el olor antes de consumir", - "tip_medRisk_check2": "Caducado hace tiempo, comprueba cuidadosamente antes de usar", - "tip_medRisk_danger": "Demasiado tiempo desde la caducidad, mejor desechar", - "tip_lowRisk_ok": "Producto de larga duración, aún seguro para consumir", - "tip_lowRisk_check": "Caducado hace más de un mes, comprueba la integridad del envase", - "tip_lowRisk_danger": "Caducado hace demasiado tiempo, mejor no arriesgarse" - }, - "toast": { - "product_saved": "¡Producto guardado!", - "product_created": "¡Producto creado!", - "product_updated": "✅ ¡Producto actualizado!", - "product_removed": "Producto eliminado", - "updated": "¡Actualizado!", - "quantity_confirmed": "✓ Cantidad confirmada", - "added_to_inventory": "✅ ¡{name} añadido!", - "removed_from_list": "✅ ¡{name} eliminado de la lista!", - "removed_from_list_short": "Eliminado de la lista", - "added_to_shopping": "🛒 ¡Añadido a la lista de la compra!", - "removed_from_shopping": "🛒 Eliminado de la lista de la compra", - "finished_to_bring": "🛒 Producto terminado → ¡añadido a Bring!", - "thrown_away": "🗑️ ¡{name} tirado!", - "thrown_away_partial": "🗑️ {qty} {unit} de {name} tirado(s)", - "finished_all": "📤 ¡{name} terminado!", - "product_finished_confirmed": "✅ Eliminado — añádelo de nuevo cuando reabastezcas", - "appliance_added": "Electrodoméstico añadido", - "item_added": "{name} añadido" - }, - "antiwaste": { - "title": "🌱 Informe anti-desperdicio", - "grade_label": "Nota", - "you": "Tú", - "avg_label": "Media", - "better": "🎉 ¡Desperdicias un {diff}% menos que la media {country}!", - "worse": "⚠️ Desperdicias más que la media {country}. ¡Hay margen de mejora!", - "on_par": "→ Estás en la media {country}. ¡Puedes hacerlo mejor!", - "saved_money": "~{amount}/mes ahorrado", - "saved_meals": "~{n} comidas salvadas", - "saved_co2": "{n} kg CO₂ evitados", - "trend_title": "Tendencia (últimos 3 meses)", - "months_ago_2": "-60 días", - "months_ago_1": "-30 días", - "this_month": "Ahora", - "country_it": "Media italiana", - "country_de": "Media alemana", - "country_en": "Media estadounidense", - "source": "Fuentes: REDUCE, Eurostat, USDA 2021", - "live_on": "Datos en vivo", - "live_off": "Sin conexión", - "meals": "comidas", - "annual_info": "📅 Tú ~{you} kg/año · media ~{avg} kg/año", - "badge_rate": "tasa de pérdida", - "badge_saved_money": "ahorrado vs media", - "badge_wasted": "artículos perdidos", - "badge_better": "menos que la media" - }, - "error": { - "generic": "Error", - "network": "Error de red", - "no_api_key": "Configura la clave API en los ajustes", - "loading": "Error al cargar el producto", - "not_found": "Producto no encontrado", - "not_found_manual": "Producto no encontrado. Introdúcelo manualmente.", - "search": "Error de búsqueda. Inténtalo de nuevo.", - "search_short": "Error de búsqueda", - "save": "Error al guardar", - "connection": "Error de conexión", - "camera": "No se puede acceder a la cámara", - "bring_add": "Error al añadir a Bring!", - "bring_connection": "Error de conexión con Bring!", - "identification": "Error de identificación", - "ai_quota": "Cuota de IA agotada. Inténtalo de nuevo en unos minutos.", - "barcode_empty": "Introduce un código de barras", - "barcode_format": "El código de barras solo puede contener números (4-14 dígitos)", - "min_chars": "Escribe al menos 2 caracteres", - "not_in_inventory": "Producto no en inventario", - "appliance_exists": "El electrodoméstico ya existe", - "already_exists": "Ya existe", - "network_retry": "Error de conexión. Inténtalo de nuevo.", - "select_items": "Selecciona al menos un producto", - "server_offline": "Conexión con el servidor perdida", - "server_restored": "Conexión con el servidor restaurada", - "server_retry": "Reintentar", - "unknown": "Error desconocido", - "prefix": "Error", - "no_inventory_entry": "No se encontró ninguna entrada de inventario", - "offline_title": "Sin conexión", - "offline_subtitle": "La app no puede conectar con el servidor. Verifica tu conexión Wi-Fi.", - "offline_checking": "Verificando conexión…", - "offline_restored": "¡Conexión restaurada!", - "offline_continue": "Continuar en modo sin conexión", - "offline_reading_cache": "Leyendo desde caché local", - "offline_ops_pending": "{n} operaciones pendientes", - "offline_synced": "{n} operaciones sincronizadas", - "offline_ai_disabled": "No disponible sin conexión", - "offline_cache_ready": "Offline — {n} productos en caché" - }, - "confirm_placeholder_search": null, - "confirm": { - "remove_item": "¿Realmente quieres eliminar este producto del inventario?", - "kiosk_exit": "¿Salir del modo kiosco?", - "cancel": "Cancelar", - "proceed": "Confirmar", - "discard_one": "Tirar 1 unidad" - }, - "location": { - "dispensa": "Despensa", - "frigo": "Nevera", - "freezer": "Congelador" - }, - "edit": { - "title": "Editar {name}", - "unknown_hint": "Introduce el nombre del producto y la información", - "label_name": "🏷️ Nombre del producto", - "choose_location_title": "¿Qué ubicación?", - "choose_location_hint": "Elige la ubicación a editar:", - "confirm_large_qty": "Estás configurando la cantidad a {qty} {unit}. Esto parece inusualmente alto. ¿Confirmar?" - }, - "screensaver": { - "recipe_btn": "Recetas", - "scan_btn": "Escanear producto" - }, - "days": { - "mon": "Lunes", - "tue": "Martes", - "wed": "Miércoles", - "thu": "Jueves", - "fri": "Viernes", - "sat": "Sábado", - "sun": "Domingo", - "mon_short": "Lun", - "tue_short": "Mar", - "wed_short": "Mié", - "thu_short": "Jue", - "fri_short": "Vie", - "sat_short": "Sáb", - "sun_short": "Dom" - }, - "meal_types": { - "lunch": "Almuerzo", - "dinner": "Cena", - "colazione": "Desayuno", - "merenda": "Merienda", - "dolce": "Postre", - "succo": "Zumo de fruta", - "pranzo": "Almuerzo", - "cena": "Cena" - }, - "scale": { - "status_connected": "Báscula conectada", - "status_searching": "Pasarela conectada, esperando báscula…", - "status_disconnected": "Pasarela de báscula inaccesible", - "status_error": "Error de conexión con la pasarela", - "not_connected": "Pasarela de báscula no conectada", - "read_btn": "⚖️ Leer desde báscula", - "reading_title": "Lectura de báscula", - "place_on_scale": "Coloca el producto en la báscula…", - "waiting_stable": "El peso se capturará automáticamente cuando la lectura sea estable.", - "no_url": "Introduce la URL de la pasarela", - "testing": "⏳ Probando conexión…", - "connected_ok": "¡Conexión con la pasarela exitosa!", - "timeout": "Tiempo de espera agotado: sin respuesta de la pasarela", - "error_connect": "No se puede conectar a la pasarela", - "tab": "Báscula inteligente", - "low_weight": "Peso < 10 g · introduce manualmente\n(la lectura automática requiere al menos 10 g)", - "density_hint": "(densidad {density} g/ml)", - "ml_hint": "(se convertirá a ml)", - "weight_detected": "Peso detectado — espera 10s de estabilidad…", - "weight_too_low": "Peso demasiado bajo — esperando…", - "stable": "✓ Estable", - "auto_confirm": "✅ {val} {unit} — confirmación automática en 5s (toca para cancelar)", - "cancelled_replace": "Cancelado — vuelve a colocar el ingrediente en la báscula para continuar" - }, - "prediction": { - "expected_qty": "Esperado: {expected} {unit}", - "actual_qty": "Actual: {actual} {unit}", - "check_suggestion": "Comprueba o pesa la cantidad restante" - }, - "date": { - "today": "📅 Hoy", - "yesterday": "📅 Ayer" - }, - "scanner": { - "title_barcode": "🔖 Escanear código de barras", - "barcode_hint": "Encuadra el código de barras del producto", - "barcode_manual_placeholder": "O introduce manualmente...", - "barcode_use_btn": "✅ Usar este código", - "ai_identifying": "🤖 Identificando producto...", - "ai_analyzing": "🤖 Análisis IA en curso...", - "product_label_hint": "Encuadra la etiqueta del producto", - "expiry_label_hint": "Encuadra la fecha de caducidad impresa en el producto", - "capture_btn": "📸 Capturar", - "capture_photo_btn": "📸 Sacar foto", - "retake_btn": "🔄 Repetir", - "camera_error_hint": "Asegúrate de usar HTTPS y haber concedido los permisos de cámara.
    Puedes introducir el código de barras manualmente o usar la identificación IA.", - "no_barcode": "Sin código de barras", - "save_new_btn": "🆕 Ninguno de estos — guardar como nuevo" - }, - "lowstock": { - "title": "⚠️ ¡Stock bajo!", - "message": "{name} se está agotando — solo quedan {qty}.", - "question": "¿Quieres añadirlo a la lista de la compra?", - "yes": "🛒 Sí, añadir a Bring!", - "no": "No, por ahora estoy bien" - }, - "move": { - "title": "📦 ¿Mover el resto?", - "question": "¿Quieres mover {thing} de {name} a otra ubicación?", - "question_short": "¿Quieres mover {thing} a otra ubicación?", - "thing_opened": "el paquete abierto", - "thing_rest": "el resto", - "stay_btn": "No, quedarse en {location}", - "moved_toast": "📦 Paquete abierto movido a {location}", - "vacuum_restore": "🫙 Restaurar al vacío", - "vacuum_seal_rest": "🔒 Sellar el resto al vacío" - }, - "nova": { - "1": "Sin procesar", - "2": "Ingrediente culinario", - "3": "Procesado", - "4": "Ultraprocesado" - }, - "meal_plan_types": { - "pasta": "Pasta", - "riso": "Arroz", - "carne": "Carne", - "pesce": "Pescado", - "legumi": "Legumbres", - "uova": "Huevos", - "formaggio": "Queso", - "pizza": "Pizza", - "affettati": "Fiambres", - "verdure": "Verduras", - "zuppa": "Sopa", - "insalata": "Ensalada", - "pane": "Pan/Bocadillo", - "dolce": "Postre", - "libero": "Libre" - }, - "meal_sub": { - "dolce_torta": "Tarta", - "dolce_crema": "Crema / Pudín", - "dolce_crumble": "Crumble / Tarta", - "dolce_biscotti": "Galletas / Pastelería", - "dolce_frutta": "Postre de fruta", - "succo_dolce": "Dulce / Afrutado", - "succo_energizzante": "Energizante", - "succo_detox": "Detox / Verde", - "succo_rinfrescante": "Refrescante", - "succo_vitaminico": "Vitamínico / Cítricos" - }, - "meal_plan": { - "reset_success": "Plan semanal restablecido", - "not_available": "no disponible en la despensa", - "suggested_by": "sugerido por el plan semanal" - }, - "nutrition": { - "title": "🥗 Análisis alimentario", - "score_excellent": "😄 Excelente", - "score_good": "🙂 Bien", - "score_improve": "😬 Mejorable", - "label_health": "🌿 Salud", - "label_variety": "🎨 Variedad", - "label_fresh": "❄️ Fresco", - "source": "Basado en {n} productos en tu despensa · EverShelf", - "products_count": "productos", - "today_title": "🥗 Tu despensa hoy", - "products_n": "{n} productos", - "macros_title": "Macronutrientes estimados", - "macros_proteins": "Proteínas", - "macros_carbs": "Carbohidratos", - "macros_fat": "Grasas", - "macros_fiber": "Fibra", - "macros_source": "Estimación basada en {n} productos en despensa" - }, - "facts": { - "greeting_morning": "Buenos días", - "greeting_afternoon": "Buenas tardes", - "greeting_evening": "Buenas noches", - "pantry_waiting": "¡{greeting}! Tu despensa te espera.", - "expired_one": "Tienes 1 producto caducado en tu despensa. ¡Compruébalo!", - "expired_many": "Tienes {n} productos caducados en tu despensa. ¡Compruébalos!", - "expired_list": "Productos caducados: {names}", - "expired_list_more": "y {n} más", - "freezer_expired_ok": "{name} está caducado, pero al estar en el congelador puede estar bien. ¡Compruébalo!", - "freezer_expired_old": "{name} en el congelador lleva demasiado tiempo caducado. Mejor tirarlo.", - "fridge_expired_one": "¡Tienes 1 producto caducado en la nevera!", - "fridge_expired_many": "¡Tienes {n} productos caducados en la nevera!", - "expiring_today": "¡{name} caduca hoy! Úsalo enseguida.", - "expiring_tomorrow": "{name} caduca mañana. ¡Planifica!", - "expiring_days": "{name} caduca en {days} días.", - "expiring_many": "Tienes {n} productos que caducan pronto.", - "expiring_this_week": "¡{n} productos caducan esta semana. Planifica tus comidas en consecuencia!", - "expiring_item_loc": "{name} ({loc}) caduca en {days} {dayslabel}.", - "expiring_this_month": "{n} productos caducarán este mes.", - "shopping_add": "Añadir a la lista: {names} 🛒", - "shopping_more": "y {n} más", - "shopping_empty": "¡Lista de la compra vacía. Todo en stock! ✅", - "in_fridge": "En la nevera: {name}.", - "in_freezer": "En el congelador: {name}. ¡No lo olvides!", - "top_category": "La categoría principal es {icon} {cat} con {n} productos.", - "cat_meat": "Tienes {n} productos cárnicos. 🥩", - "cat_dairy": "Tienes {n} productos lácteos en casa. 🥛", - "cat_veggies": "Tienes {n} tipos de verduras. ¡Genial para la salud! 🥬", - "cat_fruit": "Tienes {n} tipos de fruta. 🍎", - "cat_drinks": "Tienes {n} bebidas disponibles. 🥤", - "cat_frozen": "Tienes {n} artículos congelados. ❄️", - "cat_pasta": "Tienes {n} tipos de pasta. 🍝 ¿Y si hacemos una carbonara?", - "cat_canned": "Tienes {n} conservas en la despensa. 🥫", - "cat_snacks": "Tienes {n} snacks. ¡Resiste la tentación! 🍪", - "cat_condiments": "Tienes {n} condimentos disponibles. 🧂", - "item_random": "¿Sabías que tienes {name} en {loc}?", - "item_qty": "{name}: tienes {qty}.", - "no_expiry_count": "{n} productos no tienen fecha de caducidad.", - "furthest_expiry": "El producto con la fecha de caducidad más lejana es {name}: {months} meses.", - "high_qty": "¡Tienes un buen stock de {name}: {qty}!", - "low_qty_item": "{name} se está agotando. ¿Añadirlo a tu lista de la compra?", - "low_qty_count": "{n} productos están casi agotados.", - "morning_bread": "¡Buenos días! Tienes pan para el desayuno. 🍞", - "morning_milk": "¿Hay leche en la nevera para un café con leche? ☕🥛", - "morning_fruit": "¡Buenos días! Algo de fruta fresca es un gran comienzo. 🍎", - "noon_pasta": "Es hora de comer… ¿Y si preparamos un buen plato de pasta? 🍝", - "noon_salad": "¿Una ensalada fresca para comer? ¡Tienes {n} verduras! 🥗", - "evening_meat": "Para cenar podrías usar la carne que tienes. 🥩", - "evening_fish": "¿Qué tal pescado para cenar? 🐟", - "evening_expiring": "Tienes {n} productos que caducan esta semana — ¡úsalos esta noche!", - "night_reminder": "¡Buenas noches! Recuerda usar mañana: {names}.", - "weekly_balance": "Balance semanal: +{in} añadidos, −{out} consumidos.", - "weekly_added": "Has añadido {n} productos esta semana.", - "weekly_consumed": "Has consumido {n} productos esta semana. ¡Bien hecho!", - "tip_freezer": "💡 Los productos congelados duran mucho más que la fecha de caducidad.", - "tip_bread": "💡 El pan congelado conserva su frescura durante semanas.", - "tip_fifo": "💡 Para evitar desperdicios, usa primero los productos más cercanos a la caducidad (FIFO).", - "tip_meat": "💡 La carne en el congelador puede durar hasta 6 meses con seguridad.", - "tip_no_refreeze": "💡 Nunca vuelvas a congelar un producto descongelado. ¡Cocínalo enseguida!", - "tip_fridge": "💡 Una nevera ordenada te ahorra tiempo y dinero.", - "tip_canned": "💡 Las conservas abiertas deben ir a la nevera y consumirse en pocos días.", - "top_brand": "La marca más común en tu despensa es {brand} con {n} productos.", - "combo_pasta": "Tienes pasta y condimentos: ¡listo para un primer plato! 🍝", - "combo_sandwich": "Pan y carne: ¡un sándwich rápido siempre es buena idea! 🥪", - "combo_balanced": "Verduras y carne: ¡tienes todo para una comida equilibrada! 🥗🥩", - "pantry_empty": "¡La despensa está vacía! Es hora de ir al supermercado. 🛒", - "pantry_empty_scan": "No hay productos registrados. ¡Escanea algo para empezar!", - "location_distribution": "Distribución: {parts}", - "day": "día", - "days": "días" - }, - "kiosk_session": { - "first_item": "¡Primer artículo: {name}!", - "items_two_four": "{n} artículos — arrancando 🚀", - "items_five_nine": "{n} artículos — ¡buen ritmo! 💪", - "items_ten_twenty": "{n} artículos — casi un récord 🏆", - "items_twenty_plus": "{n} artículos — ¡compra épica! 🛒🔥", - "duplicates_one": "1 duplicado (mismo artículo dos veces)", - "duplicates_many": "{n} duplicados (cogido varias veces)", - "top_category": "Categoría principal: {cat} ({count}×)", - "items_fallback": "{n} artículo{plural} añadido{plural}" - }, - "kiosk": { - "check_btn": "🔍 Buscar actualizaciones", - "checking": "⏳ Comprobando…", - "error_check": "Error durante la comprobación de actualizaciones", - "error_start_install": "Error al iniciar la instalación", - "version_installed": "Instalado: {v}", - "update_available": "⬆️ Nueva versión disponible: {latest} (instalada: {current})", - "up_to_date": "✅ Estás actualizado — versión {v}", - "too_old": "⚠️ El kiosco instalado es demasiado antiguo para la comprobación automática de actualizaciones.
    Pulsa el botón de abajo para descargar e instalar la nueva versión directamente.", - "manual_install": "⚠️ Este kiosco no admite instalación automática.
    Procedimiento manual:
    1. Sal del kiosco (botón ✕ arriba a la izquierda)
    2. Desinstala la app EverShelf Kiosk
    3. Descarga e instala el nuevo APK desde GitHub:", - "starting_download": "⏳ Iniciando descarga…", - "install_btn": "⬇️ Instalar actualización", - "exit_title": "Salir del kiosco", - "refresh_title": "Actualizar página" - }, - "update": { - "new_version": "Nueva versión", - "btn": "Actualizar" - }, + "app": { + "name": "EverShelf", + "loading": "Cargando..." + }, + "nav": { + "title": "EverShelf", + "home": "Inicio", + "inventory": "Despensa", + "recipes": "Recetas", + "shopping": "Compras", + "log": "Registro", + "settings": "Ajustes" + }, + "btn": { + "back": "← Volver", + "save": "💾 Guardar", + "cancel": "✕ Cancelar", + "close": "Cerrar", + "add": "✅ Añadir", + "delete": "Eliminar", + "edit": "✏️ Editar", + "use": "Usar", + "edit_item": "Editar", + "search": "🔍 Buscar", + "go": "✅ Ir", + "toggle_password": "👁️ Mostrar/Ocultar", + "load_more": "Cargar más...", + "save_config": "💾 Guardar configuración", + "save_product": "💾 Guardar producto", + "restart": "↺ Reiniciar", + "reset_default": "↺ Restablecer valores por defecto", + "save_info": "💾 Guardar información", + "retry": "🔄 Reintentar", + "yes_short": "Sí", + "no_short": "No" + }, + "form": { + "select_placeholder": "-- Seleccionar --" + }, + "locations": { + "dispensa": "Despensa", + "frigo": "Nevera", + "freezer": "Congelador", + "altro": "Otro" + }, + "categories": { + "latticini": "Lácteos", + "carne": "Carne", + "pesce": "Pescado", + "frutta": "Fruta", + "verdura": "Verduras", + "pasta": "Pasta y Arroz", + "pane": "Pan y Panadería", + "surgelati": "Congelados", + "bevande": "Bebidas", + "condimenti": "Condimentos", + "snack": "Snacks y Dulces", + "conserve": "Conservas", + "cereali": "Cereales y Legumbres", + "igiene": "Higiene", + "pulizia": "Limpieza", + "altro": "Otro", + "select": "-- Seleccionar --" + }, + "units": { + "pz": "uds", + "conf": "paq", + "g": "g", + "ml": "ml", + "pieces": "Unidades", + "grams": "Gramos", + "box": "Paquete", + "boxes": "Paquetes", + "millilitres": "Mililitros", + "from": "de" + }, + "shopping_sections": { + "frutta_verdura": "Frutas y Verduras", + "carne_pesce": "Carne y Pescado", + "latticini": "Lácteos y Frescos", + "pane_dolci": "Pan y Dulces", + "pasta": "Pasta y Cereales", + "conserve": "Conservas y Salsas", + "surgelati": "Congelados", + "bevande": "Bebidas", + "pulizia_igiene": "Limpieza e Higiene", + "altro": "Otro" + }, + "dashboard": { + "expired_title": "🚫 Caducado", + "expiring_title": "⏰ Caduca pronto", + "stats_period": "📊 Últimos 30 días", + "opened_title": "📦 Productos abiertos", + "review_title": "🔍 Por revisar", + "review_hint": "Cantidades inusuales. Confirma si son correctas o modifícalas.", + "quick_recipe": "Receta rápida con productos que caducan", + "banner_review_title": "Cantidad anormal", + "banner_review_action_ok": "Es correcto", + "banner_review_action_finish": "🗑️ Todo terminado", + "banner_review_action_edit": "Corregir", + "banner_review_action_weigh": "Pesar", + "banner_review_dismiss": "Ignorar", + "banner_prediction_title": "Consumo por revisar", + "banner_prediction_hint": "La estimación de consumo se adapta a los datos recientes: confirma solo si la cantidad actual es correcta.", + "banner_prediction_action_confirm": "Confirmar {qty} {unit}", + "banner_prediction_action_weigh": "Pesar ahora", + "banner_prediction_action_edit": "Actualizar cantidad", + "banner_expired_title": "Producto caducado", + "banner_expired_today": "Caducado hoy", + "banner_expired_days": "Caducado hace {days} días", + "banner_expired_action_use": "Usar de todas formas", + "banner_expired_action_finished": "¡Ya lo terminé!", + "banner_expired_action_throw": "Lo tiré", + "banner_expired_action_edit": "Corregir fecha", + "banner_anomaly_action_edit": "Corregir inventario", + "banner_anomaly_action_dismiss": "La cantidad es correcta", + "banner_no_expiry_title": "Caducidad faltante: {name}", + "banner_no_expiry_detail": "Este producto no tiene fecha de caducidad. ¿Quieres añadir una o confirmar que no caduca?", + "banner_no_expiry_action_set": "Establecer fecha de caducidad", + "banner_no_expiry_action_dismiss": "No caduca ✓", + "banner_no_expiry_toast_dismissed": "Marcado como «sin fecha de caducidad»", + "banner_expiring_title": "Caduca pronto", + "banner_expiring_today": "¡Caduca hoy!", + "banner_expiring_tomorrow": "Caduca mañana", + "banner_expiring_days": "Caduca en {days} días", + "banner_expiring_action_use": "Usar ahora", + "banner_finished_title": "¿terminado?", + "banner_finished_detail": "Registré que {name} llegó a cero de stock. ¿Realmente se acabó o todavía tienes algo?", + "banner_finished_action_yes": "Sí, se acabó", + "banner_finished_action_no": "No, todavía tengo", + "banner_review_unusual_pkg_title": "Tamaño de paquete inusual", + "banner_review_unusual_pkg_detail": "Configuraste un paquete de {qty} {unit} — el tamaño parece muy grande. Comprueba si es correcto o edita.", + "banner_review_low_qty_title": "Cantidad muy baja", + "banner_review_low_qty_detail": "Solo tienes {qty} en stock — parece muy poco, puede ser un error de escritura. Confirma si es correcto.", + "banner_review_high_qty_title": "Cantidad inusualmente alta", + "banner_review_high_qty_detail": "Tienes {qty} en stock — la cifra parece muy alta. Confirma si es correcto o edita.", + "banner_prediction_rate_day": "Media ~{n} {unit}/día", + "banner_prediction_rate_week": "Media ~{n} {unit}/semana", + "banner_prediction_days_ago": "Hace {n} días reabasteciste", + "banner_prediction_more": "estimación anterior: {expected} {unit}{time}; cantidad actual: {actual} {unit}.", + "banner_prediction_less": "estimación: {expected} {unit}{time}; cantidad actual: {actual} {unit}. Si tu ritmo de uso cambió, la previsión se actualiza automáticamente.", + "banner_finished_zero": "El inventario muestra cero, pero los movimientos registrados sugieren que no debería estar vacío.", + "banner_finished_expected": "Según los registros deberías tener todavía {qty} {unit}.", + "banner_finished_check": "¿Puedes comprobarlo?", + "banner_anomaly_phantom_title": "tienes más stock del esperado", + "banner_anomaly_phantom_detail": "El inventario indica {inv_qty} {unit}, pero según los registros solo deberías tener {expected_qty} {unit}. ¿Añadiste stock sin registrarlo?", + "banner_anomaly_untracked_title": "stock no registrado como entrada", + "banner_anomaly_untracked_detail": "Tienes {inv_qty} {unit} en inventario, pero las salidas registradas superan las entradas — el stock inicial probablemente nunca se añadió como transacción «entrada». Puedes corregir la cantidad o registrar las entradas faltantes.", + "banner_anomaly_ghost_title": "tienes menos stock del esperado", + "banner_anomaly_ghost_detail": "Según las operaciones registradas deberías tener {expected_qty} {unit} de {name}, pero el inventario solo muestra {inv_qty} {unit}. ¿Tomaste stock sin registrarlo?", + "consumed": "Consumido: {n} ({pct}%)", + "wasted": "Desperdiciado: {n} ({pct}%)", + "more_opened": "y {n} más abiertos...", + "banner_expired_detail": "{when} · aún tienes {qty}.", + "banner_opened_detail": "{when} en {location} · aún tienes {qty}.", + "banner_explain_title": "Pedir explicación a Gemini", + "banner_explain_btn": "Explicar", + "banner_analyzing": "🤖 Analizando…" + }, + "inventory": { + "title": "Despensa", + "filter_all": "Todo", + "search_placeholder": "🔍 Buscar producto...", + "recent_title": "🕐 Usados recientemente", + "popular_title": "⭐ Más usados", + "empty": "No hay productos aquí.\n¡Escanea un producto para añadirlo!", + "no_items_found": "No se encontraron artículos", + "qty_remainder_suffix": "restante", + "vacuum_badge": "🫙 Al vacío", + "opened_badge": "📭 Abierto", + "label_expiry": "📅 Caducidad", + "label_storage": "🫙 Almacenamiento", + "label_status": "📭 Estado", + "opened_since": "Abierto desde el {date}", + "label_position": "📍 Ubicación", + "label_quantity": "📦 Cantidad", + "label_added": "📅 Añadido", + "empty_text": "No hay productos aquí.
    ¡Escanea un producto para añadirlo!", + "empty_db": "No hay productos en la base de datos.
    ¡Escanea un producto para empezar!", + "qty_trace": "< 1" + }, + "scan": { + "title": "Escáner", + "mode_shopping": "🛒 Modo compras", + "mode_shopping_end": "✅ Finalizar compras", + "spesa_btn": "🛒 Compras", + "zoom": "Zoom", + "tab_barcode": "Código de barras", + "tab_name": "Nombre", + "tab_ai": "IA", + "recents_label": "Recientes", + "torch_hint": "Linterna", + "torch_on": "Linterna encendida", + "torch_off": "Linterna apagada", + "torch_unavailable": "Linterna no disponible en este dispositivo", + "flip_hint": "Cambiar cámara", + "flip_front": "Cámara frontal", + "flip_back": "Cámara trasera", + "num_ocr_btn": "🔢 Leer números con IA", + "num_ocr_searching": "Buscando código de barras con IA...", + "num_ocr_found": "Código encontrado: {code}", + "num_ocr_not_found": "No se encontró código de barras en la imagen", + "barcode_placeholder": "Introduce el código de barras...", + "quick_name_divider": "o escribe el nombre", + "quick_name_placeholder": "Ej.: Manzanas, Calabacín, Pan...", + "manual_entry": "✏️ Entrada manual", + "ai_identify": "🤖 Identificar con IA", + "hint": "Escanea el código de barras, escribe el nombre del producto o usa la IA para identificarlo", + "debug_toggle": "🐛 Registro de depuración", + "barcode_acquired": "🔖 Código de barras escaneado: {code}", + "scan_barcode": "🔖 Escanear código de barras", + "create_named": "Crear {name}", + "new_without_barcode": "Nuevo producto sin código de barras", + "status_ready": "Apunta la cámara al código de barras", + "status_scanning": "Escaneando...", + "status_partial": "Detectado: {code} — verificando...", + "status_invalid": "Inválido: {code} — reintentando", + "status_confirmed": "Confirmado!", + "status_parallel": "Escaneo combinado activo...", + "ai_fallback_searching": "Identificación de IA en curso...", + "ai_fallback_found": "Producto identificado por IA", + "ai_fallback_not_found": "IA: producto no reconocido" + }, + "action": { + "title": "¿Qué quieres hacer?", + "add_btn": "📥 AÑADIR", + "add_sub": "a la despensa/nevera", + "use_btn": "📤 USAR / CONSUMIR", + "use_sub": "de la despensa/nevera", + "have_title": "📦 ¡Ya en stock!", + "add_more_sub": "añadir más", + "use_qty_sub": "cuánto usaste", + "throw_btn": "🗑️ DESECHAR", + "throw_sub": "tirar", + "edit_sub": "caducidad, ubicación…", + "create_recipe_btn": "Receta" + }, + "add": { + "title": "Añadir a la despensa", + "location_label": "📍 ¿Dónde lo guardas?", + "quantity_label": "📦 Cantidad", + "conf_size_label": "📦 Cada paquete contiene:", + "conf_size_placeholder": "ej. 300", + "vacuum_label": "🫙 Al vacío", + "vacuum_hint": "La fecha de caducidad se ampliará automáticamente", + "submit": "✅ Añadir", + "purchase_type_label": "🛒 Este producto es...", + "new_btn": "🆕 Recién comprado", + "existing_btn": "📦 Ya lo tenía", + "remaining_label": "📦 Cantidad restante", + "remaining_hint": "¿Aproximadamente cuánto queda?", + "remaining_full": "🟢 Lleno", + "remaining_half": "🟠 Por la mitad", + "estimated_expiry": "Caducidad estimada:", + "suffix_freezer": "(congelador)", + "suffix_vacuum": "(al vacío)", + "hint_modify": "📝 Puedes cambiar la fecha o escanearla con la cámara", + "scan_expiry_title": "📷 Escanear fecha de caducidad", + "product_added": "✅ ¡{name} añadido!{qty}", + "suffix_freezer_vacuum": "(congelador + al vacío)", + "history_badge_tip": "Media de {n} entradas anteriores", + "vacuum_question": "¿Al vacío?", + "vacuum_saved": "🔒 ¡Al vacío!" + }, + "use": { + "title": "Usar / Consumir", + "location_label": "📍 ¿De dónde?", + "quantity_label": "¿Cuánto usaste?", + "change": "cambiar", + "partial_hint": "O especifica la cantidad usada:", + "partial_piece_hint": "¿Usaste solo una parte?", + "piece": "unidad", + "one_whole": "1 entero", + "use_all": "🗑️ Todo usado / Terminado", + "submit": "📤 Usar esta cantidad", + "available": "📦 Disponible:", + "opened_badge": "ABIERTO", + "not_in_inventory": "⚠️ Producto no en inventario.", + "expiry_warning": "⚠️ ¡Usa primero el{loc} que caduca el {date} — {when}!", + "expiry_warning_opened": "⚠️ El{loc} lleva abierto {when} — ¡úsalo primero!", + "throw_title": "🗑️ Desechar producto", + "throw_all": "🗑️ Desechar TODO ({qty})", + "throw_qty_label": "¿Cuánto desechar?", + "throw_qty_hint": "o introduce una cantidad:", + "throw_partial_btn": "🗑️ Desechar esta cantidad", + "when_expired": "caducado hace {n} días", + "when_today": "caduca hoy", + "when_tomorrow": "caduca mañana", + "when_days": "caduca en {n} días", + "toast_used": "📤 {qty} de {name} usado", + "toast_bring": "🛒 Producto terminado → añadido a Bring!", + "toast_opened_finished": "🔓 ¡Paquete abierto de {name} terminado!", + "disambiguation_hint": "¿Qué quieres decir con «todo terminado»?", + "disambiguation_all": "🗑️ Terminar TODO ({qty})", + "error_exceeds_stock": "⚠️ ¡No puedes usar más de lo que tienes disponible!", + "use_all_confirm_title": "✅ Terminar todo", + "use_all_confirm_msg": "Confirma que has terminado el producto:", + "use_all_confirm_btn": "✅ Sí, terminado", + "throw_all_confirm_title": "🗑️ Desechar todo", + "throw_all_confirm_msg": "¿Realmente quieres tirar todo el producto?", + "throw_all_confirm_btn": "🗑️ Sí, desechar" + }, + "product": { + "title_new": "Nuevo producto", + "title_edit": "Editar producto", + "ai_fill": "📷 Sacar foto e identificar con IA", + "ai_fill_hint": "La IA rellenará automáticamente los campos del producto", + "name_label": "🏷️ Nombre del producto *", + "name_placeholder": "Ej.: Leche entera, Pasta penne...", + "brand_label": "🏢 Marca", + "brand_placeholder": "Ej.: Barilla, Danone, Heinz...", + "category_label": "📂 Categoría", + "unit_label": "📏 Unidad de medida", + "default_qty_label": "🔢 Cantidad por defecto", + "conf_size_label": "📦 Cada paquete contiene:", + "conf_size_placeholder": "ej. 300", + "notes_label": "📝 Notas", + "notes_placeholder": "Ej.: sin lactosa, ecológico, guardar en nevera tras abrir...", + "barcode_label": "🔖 Código de barras", + "barcode_placeholder": "Código de barras (si disponible)", + "barcode_hint": "⚠️ ¡Añade el código de barras para la próxima vez solo tendrás que escanear!", + "submit": "💾 Guardar producto", + "name_required": "Introduce el nombre del producto", + "conf_size_required": "Especifica el contenido del paquete", + "expiry_estimated": "Caducidad estimada:", + "scan_expiry": "Escanear fecha de caducidad", + "expiry_hint": "📝 Puedes editar la fecha o escanearla con la cámara", + "add_batch": "📦 + Lote con fecha diferente", + "package_info": "📦 Paquete: {info}", + "edit_catalog": "⚙️ Editar información del producto (nombre, marca, categoría…)", + "not_recognized": "⚠️ Producto no reconocido", + "edit_info": "✏️ Editar información", + "modify_details": "EDITAR\ncaducidad, ubicación…", + "already_in_pantry": "📋 Ya en la despensa", + "no_barcode": "Sin código de barras", + "unknown_product": "Producto no reconocido", + "edit_name_brand": "Editar nombre/marca", + "weight_label": "Peso", + "origin_label": "Origen", + "labels_label": "Etiquetas", + "select_variant": "Selecciona la variante exacta o usa los datos de IA:" + }, + "products": { + "title": "📦 Todos los productos", + "search_placeholder": "🔍 Buscar producto...", + "empty": "No hay productos en la base de datos.\n¡Escanea un producto para empezar!", + "no_category": "No hay productos en esta categoría" + }, + "recipes": { + "title": "🍳 Recetas", + "generate": "✨ Generar nueva receta", + "archive_empty": "No hay recetas guardadas. ¡Genera tu primera receta!", + "dialog_title": "🍳 Receta", + "dialog_desc": "Generaré una receta saludable usando ingredientes de la despensa, priorizando los productos que caducan.", + "meal_label": "🕐 ¿Qué comida?", + "persons_label": "👥 ¿Para cuántas personas?", + "meal_type_label": "🎯 Tipo de comida", + "opt_fast": "⚡ Comida rápida", + "opt_light": "🥗 Apetito ligero", + "opt_expiry": "⏰ Priorizar productos que caducan", + "opt_healthy": "💚 Extra saludable", + "opt_opened": "📦 Priorizar productos abiertos", + "opt_zero_waste": "♻️ Cero desperdicio", + "generate_btn": "✨ Generar receta", + "loading_msg": "Preparando tu receta...", + "start_cooking": "👨‍🍳 Modo cocina", + "regenerate": "🔄 Generar otra", + "regen_choice_title": "¿Qué quieres hacer con esta receta?", + "regen_replace": "🔄 Generar otra (descartar esta)", + "regen_save_new": "💾 Guardar en el archivo y generar una nueva", + "close_btn": "✅ Cerrar", + "ingredients_title": "🧾 Ingredientes", + "tools_title": "Equipo necesario", + "steps_title": "👨‍🍳 Pasos", + "no_steps": "No hay pasos disponibles", + "generate_error": "Error de generación", + "persons_short": "com.", + "use_ingredient_title": "Usar ingrediente", + "recipe_qty_label": "Receta", + "from_where_label": "¿De dónde?", + "amount_label": "Cuánto", + "use_amount_btn": "Usar esta cantidad", + "use_all_btn": "Usar TODO / Terminado", + "packs_label": "Paquetes", + "quantity_in_total": "Cantidad en {unit} (total: {total})", + "packs_of_have": "Paquetes de {size} (tienes {count} paquetes)", + "scale_wait_stable": "Espera 10s de peso estable para el relleno automático…", + "ingredient_scaled_toast": "📦 ¡Ingrediente deducido de la despensa!", + "finished_added_bring_toast": "🛒 Producto terminado → ¡añadido a Bring!", + "load_error": "Error de carga", + "favorite": "Añadir a favoritos", + "unfavorite": "Quitar de favoritos", + "adjust_persons": "Personas" + }, + "shopping": { + "title": "🛒 Lista de la compra", + "bring_loading": "Conectando a Bring!...", + "bring_not_configured": "Bring! no está configurado. Añade tu email y contraseña en los ajustes.", + "tab_to_buy": "🛍️ Por comprar", + "tab_forecast": "🧠 Previsión", + "total_label": "💰 Total estimado", + "section_to_buy": "🛍️ Por comprar", + "suggestions_title": "💡 Sugerencias IA", + "suggestions_add": "✅ Añadir selección a Bring!", + "search_prices": "🔍 Buscar todos los precios", + "suggest_btn": "Sugerir qué comprar", + "smart_title": "🧠 Predicciones inteligentes", + "smart_empty": "No hay predicciones disponibles.
    Añade productos a tu despensa para recibir predicciones inteligentes.", + "smart_filter_all": "Todo", + "smart_filter_critical": "🔴 Urgente", + "smart_filter_high": "🟠 Pronto", + "smart_filter_medium": "🟡 Planificar", + "smart_filter_low": "🟢 Previsión", + "smart_add": "🛒 Añadir selección a Bring!", + "empty": "¡Lista de la compra vacía!\nUsa el botón de abajo para generar sugerencias.", + "already_in_list": "🛒 \"{name}\" ya está en la lista de la compra", + "already_in_list_short": "ℹ️ Ya en la lista de la compra", + "add_prompt": "¿Quieres añadirlo a la lista de la compra?", + "smart_already": "📊 Las predicciones de compra ya predicen {name}", + "all_searched": "Todos los productos ya han sido buscados. Usa 🔄 para buscar individualmente.", + "search_complete": "Búsqueda completada: {count} productos", + "removed_sufficient": "🧹 {removed} producto(s) con stock suficiente eliminado(s) de la lista", + "suggest_buy": "🛒 Comprar: {qty} {unit}", + "suggest_buy_approx": "🛒 Al menos: {qty} {unit}", + "suggest_buy_tip": "Cantidad sugerida basada en tus últimos 14 días de consumo", + "suggest_buy_approx_tip": "Estimación mínima basada en el consumo (compra el tamaño de paquete más cercano)", + "bring_badge": "🛒 Ya en Bring!", + "add_urgent_toast": "🔴 {n} producto(s) urgente(s) añadido(s) automáticamente a Bring!", + "migration_done": "✅ {migrated} actualizado(s), {skipped} ya estaban ok", + "added_to_bring": "🛒 {n} productos añadidos a Bring!", + "added_to_bring_skip": "{n} ya presentes", + "all_on_bring": "¡Todos los productos ya estaban en Bring!", + "freq_high": "📈 Frecuente", + "freq_regular": "📊 Regular", + "freq_occasional": "📉 Ocasional", + "out_of_stock": "Sin stock", + "scan_toast": "📷 Escanear: {name}", + "empty_category": "No hay productos en esta categoría", + "session_empty": "🛒 Sin productos aún", + "urgency_critical": "Urgente", + "urgency_high": "Pronto", + "urgency_medium": "Planificar", + "urgency_low": "Previsión", + "urgency_medium_short": "Medio", + "urgency_low_short": "Ok", + "tag_urgent": "🔴 Urgente", + "tag_priority": "⭐ Prioridad", + "tag_check": "✅ Comprobar", + "smart_already_predicted": "📊 Las predicciones inteligentes ya predicen {name}{urgency}.", + "item_removed": "✅ ¡{name} eliminado de la lista!", + "urgency_spec_critical": "⚡ Urgente", + "urgency_spec_high": "🟠 Pronto", + "bring_add_n": "Añadir {n} a Bring!", + "bring_add_selected": "Añadir selección a Bring!", + "bring_adding": "Añadiendo...", + "bring_added_one": "1 producto añadido a Bring!", + "bring_added_many": "{n} productos añadidos a Bring!", + "bring_skipped": "({n} ya en la lista)", + "force_sync": "Forzar sincronización de Bring!", + "scan_target_label": "Estás buscando", + "scan_target_found": "¡Encontrado! Eliminar de la lista", + "bring_add_one": "Añadir 1 producto a Bring!", + "bring_add_many": "Añadir {n} productos a Bring!", + "syncing": "Sincronizando…", + "sync_done": "Sincronización completada", + "price_searching": "Buscando...", + "search_action": "Buscar", + "open_action": "Abrir", + "not_found": "No encontrado", + "search_price": "Buscar precio", + "tap_to_scan": "Toca para escanear", + "tag_title": "Etiqueta", + "remove_title": "Eliminar", + "found_count": "{found}/{total} productos encontrados", + "savings_offers": "· 🏷️ Ahorras {amount}€ con ofertas", + "searching_progress": "Buscando {current}/{total}...", + "remove_error": "Error al eliminar", + "btn_fetch_prices": "Buscar precios", + "price_total_label": "💰 Total estimado:", + "price_loading": "Buscando precios…", + "price_not_found": "precio n/d", + "suggest_loading": "Analizando...", + "suggest_error": "Error al generar sugerencias", + "priority_high": "Alta", + "priority_medium": "Media", + "priority_low": "Baja", + "smart_last_update": "Actualizado {time}", + "names_already_updated": "Todos los nombres ya están actualizados" + }, + "ai": { + "title": "🤖 Identificación IA", + "capture": "📸 Sacar foto", + "retake": "🔄 Repetir", + "hint": "Saca una foto del producto y la IA intentará identificarlo", + "identifying": "🤖 Identificando producto...", + "no_api_key": "⚠️ Clave API de Gemini no configurada.\nAñade GEMINI_API_KEY al archivo .env en el servidor.", + "fields_filled": "✅ Campos rellenados por IA", + "use_data": "✅ Usar datos de IA", + "use_data_no_barcode": "✅ Usar datos de IA (sin código de barras)" + }, + "log": { + "title": "📒 Registro de operaciones", + "type_added": "Añadido", + "type_waste": "Desechado", + "type_used": "Usado", + "type_bring": "Añadido a Bring!", + "undone_badge": "Deshecho", + "undo_title": "Deshacer esta operación", + "load_error": "Error al cargar el registro", + "empty": "No hay operaciones registradas.", + "undo_action_remove": "eliminación de", + "undo_action_restore": "reabastecimiento de", + "undo_confirm": "¿Deshacer esta operación?\n→ {action} {name}", + "undo_success": "↩ Operación deshecha para {name}", + "already_undone": "Operación ya deshecha", + "too_old": "No se pueden deshacer operaciones de más de 24 horas", + "undo_error": "Error al deshacer", + "recipe_prefix": "Receta" + }, + "chat": { + "title": "Chef Gemini", + "welcome": "¡Hola! Soy tu asistente de cocina", + "welcome_desc": "¡Pídeme que te prepare un zumo, un snack, un plato rápido… Conozco tu despensa, tus electrodomésticos y tus preferencias!", + "suggestion_snack": "🍿 Snack rápido", + "suggestion_juice": "🥤 Zumo/Batido", + "suggestion_light": "🥗 Algo ligero", + "suggestion_expiry": "⏰ Usar productos que caducan", + "clear": "Nueva conversación", + "placeholder": "Pregunta algo...", + "cleared": "Chat borrado", + "suggestion_snack_text": "¿Qué puedo preparar como snack rápido?", + "suggestion_juice_text": "Hazme un zumo o batido con lo que tengo", + "suggestion_light_text": "Tengo hambre pero quiero algo ligero", + "suggestion_expiry_text": "¿Qué está a punto de caducar y cómo puedo usarlo?", + "transfer_to_recipes": "Transferir a recetas", + "transferring": "Transfiriendo...", + "transferred": "¡Añadido a recetas!", + "open_recipe": "Abrir receta", + "quick_recipe_prompt": "¡Sugiere una receta rápida PARA UNA PERSONA usando los productos que caducan primero! Ignora el congelador, céntrate en la nevera y la despensa." + }, + "cooking": { + "close": "Cerrar", + "tts_btn": "Leer en voz alta", + "restart": "↺ Reiniciar", + "replay": "🔊 Repetir", + "timer": "⏱️ {time} · Temporizador", + "prev": "◀ Anterior", + "next": "Siguiente ▶", + "ingredient_used": "✔️ Deducido", + "ingredient_use_btn": "📦 Usar", + "ingredient_deduct_title": "Deducir de la despensa", + "timer_expired_tts": "¡Temporizador {label} finalizado!", + "timer_warning_tts": "¡Atención! {label}: ¡10 segundos restantes!", + "recipe_done_tts": "¡Receta completada! ¡Buen provecho!", + "expires_chip": "cad. {date}", + "finish": "✅ Finalizar", + "step_fallback": "Paso {n}", + "zerowaste_label": "♻️ Desperdicio", + "zerowaste_tip_title": "Consejo sin desperdicios" + }, + "settings": { + "title": "⚙️ Ajustes", + "tab_api": "Claves API", + "tab_bring": "Bring!", + "tab_recipe": "Recetas", + "tab_mealplan": "Plan semanal", + "tab_appliances": "Electrodomésticos", + "tab_spesa": "Compra online", + "tab_camera": "Cámara", + "tab_security": "Seguridad", + "tab_tts": "Voz (TTS)", + "tab_language": "Idioma", + "tab_scale": "Báscula inteligente", "gemini": { - "chat_title": "Chat con Gemini", - "not_configured": "🤖 Gemini no configurado — establece GEMINI_API_KEY en los ajustes" + "title": "🤖 Google Gemini IA", + "hint": "Clave API para identificación de productos, fechas de caducidad y recetas.", + "key_label": "Clave API Gemini" + }, + "bring": { + "title": "🛒 Lista de la compra Bring!", + "hint": "Credenciales para la integración con la lista de la compra Bring!", + "email_label": "📧 Email Bring!", + "password_label": "🔒 Contraseña Bring!" + }, + "price": { + "title": "💰 Estimación de precios (IA)", + "hint": "Mostrar el coste estimado por producto en la lista de la compra usando IA.", + "enabled_label": "Activar estimación de precios", + "country_label": "🌍 País de referencia", + "currency_label": "💱 Moneda", + "update_label": "🔄 Actualizar precios cada", + "update_suffix": "meses" + }, + "recipe": { + "title": "🍳 Preferencias de recetas", + "hint": "Configura las opciones predeterminadas para la generación de recetas.", + "persons_label": "👥 Raciones por defecto", + "options_label": "🎯 Opciones de receta por defecto", + "fast": "⚡ Comida rápida", + "light": "🥗 Comida ligera", + "expiry": "⏰ Prioridad caducidad", + "healthy": "💚 Extra saludable", + "opened": "📦 Prioridad productos abiertos", + "zerowaste": "♻️ Cero desperdicio", + "dietary_label": "🚫 Intolerancias / Restricciones", + "dietary_placeholder": "Ej.: sin gluten, sin lactosa, vegetariano..." + }, + "mealplan": { + "title": "📅 Plan de comidas semanal", + "hint": "Establece el tipo de comida para cada día. Se usará como guía en la generación de recetas.", + "enabled": "✅ Activar plan semanal", + "legend": "🌤️ = Almuerzo  ·  🌙 = Cena  ·  Toca un badge para cambiarlo.", + "types_title": "📋 Tipos disponibles", + "reset_btn": "↺ Restaurar valores por defecto" }, "appliances": { - "empty": "No hay electrodomésticos añadidos" + "title": "🔌 Electrodomésticos disponibles", + "hint": "Indica los electrodomésticos que tienes. Se tendrán en cuenta en la generación de recetas.", + "new_placeholder": "Ej.: Panificadora, Thermomix, Freidora de aire...", + "quick_title": "Añadir rápido:", + "oven": "🔥 Horno", + "microwave": "📡 Microondas", + "air_fryer": "🍟 Freidora de aire", + "bread_maker": "🍞 Panificadora", + "bimby": "🤖 Thermomix/Cookeo", + "mixer": "🌀 Robot de cocina", + "steamer": "♨️ Vaporera", + "pressure_cooker": "🫕 Olla a presión", + "toaster": "🍞 Tostadora", + "blender": "🍹 Batidora", + "empty": "No hay electrodomésticos añadidos" }, - "about": { - "title": "Acerca de", - "version": "Versión", - "report_bug": "Reportar un error", - "report_bug_hint": "¿Algo no funciona? Envíanos un informe directamente desde la app.", - "report_bug_modal_title": "Reportar un error", - "report_type_bug": "Error", - "report_type_feature": "Función", - "report_type_question": "Pregunta", - "report_field_title": "Título", - "report_field_title_ph": "Breve descripción del problema", - "report_field_desc": "Descripción", - "report_field_desc_ph": "Describe el problema en detalle…", - "report_field_steps": "Pasos para reproducir (opcional)", - "report_field_steps_ph": "1. Ir a…\n2. Tocar…\n3. Ver el error…", - "report_auto_info": "Adjuntado automáticamente: versión {version}, idioma {lang}.", - "report_send_btn": "Enviar informe", - "report_bug_sending": "Enviando…", - "report_bug_sent": "¡Informe enviado — gracias!", - "report_bug_error": "No se pudo enviar el informe. Comprueba tu conexión.", - "changelog": "Registro de cambios", - "github": "Repositorio GitHub" + "spesa": { + "title": "🛍️ Compra online", + "hint": "Configura el proveedor de compra online.", + "provider_label": "🏪 Proveedor", + "email_label": "📧 Email", + "password_label": "🔒 Contraseña", + "login_btn": "🔐 Iniciar sesión", + "ai_prompt_label": "🤖 Prompt IA de selección de producto", + "ai_prompt_placeholder": "Instrucciones para la IA al elegir entre varios productos...", + "ai_prompt_hint": "La IA usa este prompt para elegir el producto más apropiado de los resultados. Deja vacío para el comportamiento por defecto.", + "configure_first": "Configura primero la compra online en ajustes", + "missing_credentials": "Introduce email y contraseña", + "login_in_progress": "Iniciando sesión...", + "login_error_prefix": "Error:", + "login_network_error_prefix": "Error de red:", + "login_success_default": "¡Inicio de sesión exitoso!", + "result_name_label": "Nombre", + "result_card_label": "Tarjeta", + "result_pickup_label": "Punto de recogida", + "result_points_label": "Puntos de fidelidad", + "connected_relogin": "✅ Conectado — Iniciar sesión de nuevo", + "connected_as": "Conectado como {name}" }, - "export": { - "title": "Exportar inventario", - "hint": "Descarga el inventario actual en CSV o abre la versión imprimible (PDF).", - "btn_csv": "Descargar CSV", - "btn_pdf": "PDF / Imprimir", - "btn_title": "Exportar" + "camera": { + "title": "📷 Cámara", + "hint": "Elige qué cámara usar para el escaneo de códigos de barras e identificación IA.", + "device_label": "📸 Cámara por defecto", + "back": "📱 Trasera (por defecto)", + "front": "🤳 Frontal", + "devices_hint": "Si tienes varias cámaras, puedes seleccionar una específica de la lista de arriba tras conceder los permisos.", + "detect_btn": "🔄 Detectar cámaras", + "ai_fallback_label": "Identificación visual IA (repuesto 5s)", + "ai_fallback_hint": "Si no se lee ningún código de barras en 5 segundos, se envía automáticamente un fotograma a la IA para identificar el producto visualmente. Requiere Gemini configurado." }, - "startup": { - "connecting": "Conectando al servidor...", - "check_php_memory": "Memoria PHP", - "check_php_timeout": "Tiempo de espera PHP", - "check_php_upload": "Upload PHP", - "check_data_dir": "Carpeta de datos", - "check_rate_limits": "Dir. rate limits", - "check_backups": "Dir. copias de seguridad", - "check_write_test": "Prueba escritura disco", - "check_disk_space": "Espacio en disco", - "check_db_connect": "Conexión base de datos", - "check_db_tables": "Tablas de la BD", - "check_db_integrity": "Integridad BD", - "check_db_wal": "Modo WAL", - "check_db_size": "Tamaño de la BD", - "check_db_rows": "Datos del inventario", - "check_env": "Archivo .env", - "check_gemini": "Clave Gemini AI", - "check_bring_creds": "Credenciales Bring!", - "check_bring_token": "Token de Bring!", - "check_curl_ssl": "cURL SSL", - "check_internet": "Conexión a internet", - "fresh_install": "instalación nueva", - "warnings_found": "avisos detectados", - "all_ok": "Sistema OK", - "critical_error_short": "Error crítico", - "critical_error": "Error crítico: la aplicación no puede iniciarse. Revisa los registros del servidor.", - "error_network": "No se puede contactar con el servidor. Comprueba tu conexión de red.", - "retry": "Reintentar", - "syncing_local": "Sincronizando datos locales...", - "sync_done": "Datos locales sincronizados" + "security": { + "title": "🔒 Certificado HTTPS", + "hint": "Si el navegador muestra el error «Tu conexión no es privada» (ERR_CERT_AUTHORITY_INVALID), necesitas instalar el certificado CA en el dispositivo.", + "download_btn": "📥 Descargar certificado CA", + "token_title": "🔑 Token de ajustes", + "token_label": "Token de acceso", + "token_hint": "Si `SETTINGS_TOKEN` está configurado en el `.env` del servidor, introduce el token aquí antes de guardar los ajustes. Deja vacío si no está configurado.", + "token_placeholder": "(vacío = sin protección)", + "token_required_hint": "🔒 Este servidor requiere un token para guardar los ajustes.", + "cert_instructions": "Instrucciones para Chrome (Android):
    1. Descarga el certificado de arriba
    2. Ve a Ajustes → Seguridad y privacidad → Más ajustes de seguridad → Instalar desde almacenamiento
    3. Selecciona el archivo EverShelf_CA.crt descargado
    4. Elige «CA» y confirma
    5. Reinicia Chrome

    Instrucciones para Chrome (PC):
    1. Descarga el certificado de arriba
    2. Ve a chrome://settings/certificates
    3. Pestaña «Autoridades» → Importar → selecciona el archivo
    4. Marca «Confiar en este certificado para identificar sitios web»
    5. Reinicia Chrome" }, - "stats_monthly": { - "title": "Estadísticas Mensuales", - "consumed": "productos usados", - "trend_up": "+{pct}% vs {prev}", - "trend_down": "-{pct}% vs {prev}", - "trend_same": "mismo ritmo que el mes pasado", - "added": "añadidos", - "wasted": "desperdiciados", - "top_used": "más usado", - "top_cats": "Categorías principales", - "source": "Historial de transacciones · mes actual" + "tts": { + "title": "🔊 Voz & TTS", + "hint": "Configura la síntesis de voz mediante cualquier API REST externa. Los pasos de la receta y los temporizadores expirados se enviarán al endpoint configurado.", + "enabled": "✅ Activar TTS", + "engine_label": "⚙️ Motor TTS", + "engine_browser": "🔇 Navegador (sin conexión, sin configuración requerida)", + "engine_server": "🌐 Servidor externo (Home Assistant, API REST...)", + "voice_label": "🗣️ Voz", + "rate_label": "⚡ Velocidad", + "pitch_label": "🎵 Tono", + "url_label": "🌐 URL del endpoint", + "method_label": "📡 Método HTTP", + "auth_label": "🔐 Autenticación", + "auth_bearer": "Bearer Token", + "auth_custom": "Cabecera personalizada", + "auth_none": "Ninguna", + "token_label": "🔑 Bearer Token", + "custom_header_name": "📋 Nombre de cabecera", + "custom_header_value": "📋 Valor de cabecera", + "content_type_label": "📄 Content-Type", + "payload_key_label": "🗝️ Campo de texto en el payload", + "payload_key_hint": "Nombre del campo JSON que contendrá el texto a leer (ej.: message, text).", + "extra_fields_label": "➕ Campos adicionales (JSON)", + "extra_fields_placeholder": "{\"entity_id\": \"media_player.salon\"}", + "extra_fields_hint": "Campos adicionales a incluir en el payload, en formato JSON. Deja vacío si no es necesario.", + "test_btn": "🔊 Enviar voz de prueba", + "voices_loading": "Cargando voces…", + "voice_not_supported": "Voz no compatible con este navegador", + "voices_none": "No hay voces disponibles en este dispositivo", + "voices_hint": "Las voces disponibles dependen del SO y el navegador. Pulsa ↺ si la lista no carga.", + "url_missing": "⚠️ URL del endpoint faltante.", + "test_sending": "⏳ Enviando…", + "test_ok": "✅ Respuesta {code} — comprueba que el altavoz haya hablado.", + "heard_question": "¿Has escuchado la voz?", + "heard_yes": "Sí, la escuché", + "heard_no": "No, no escuché nada", + "test_ok_kiosk": "TTS funcionando.", + "test_fail_steps": "Comprueba: 1) el volumen del multimedia no es 0; 2) Google Text-to-Speech está instalado y actualizado; 3) el paquete de voz español está descargado en la configuración TTS de Android." + }, + "language": { + "title": "🌐 Idioma", + "hint": "Selecciona el idioma de la interfaz.", + "label": "🌐 Idioma", + "restart_notice": "La página se recargará para aplicar el nuevo idioma." + }, + "screensaver": { + "label": "Activar protector de pantalla", + "card_title": "🌙 Protector de pantalla", + "card_hint": "Muestra un reloj con información útil después de 5 minutos de inactividad. Desactivado por defecto.", + "timeout_1": "1 minuto", + "timeout_2": "2 minutos", + "timeout_5": "5 minutos", + "timeout_10": "10 minutos", + "timeout_15": "15 minutos", + "timeout_30": "30 minutos", + "timeout_60": "1 hora", + "start_after": "⏱️ Iniciar tras" + }, + "scale": { + "title": "⚖️ Báscula inteligente", + "hint": "Conecta una báscula Bluetooth mediante la pasarela Android para leer el peso automáticamente.", + "tab": "Báscula inteligente", + "enabled": "✅ Activar báscula inteligente", + "url_label": "🌐 URL de la pasarela WebSocket", + "url_placeholder": "ws://192.168.1.x:8765", + "url_hint": "URL mostrada por la app Android (misma red Wi-Fi). Ej.:", + "test_btn": "🔗 Probar conexión", + "download_btn": "📥 Descargar pasarela Android (APK)", + "download_hint": "App Android que conecta tu báscula BLE con EverShelf.", + "download_sub": "Fuente: evershelf-scale-gateway/ en la raíz del proyecto", + "live_weight": "peso en tiempo real", + "auto_reconnect": "🔁 Reconexión: automática", + "kiosk_title": "📡 Báscula BLE integrada en el kiosco", + "kiosk_hint": "La báscula está gestionada directamente por la pasarela BLE interna del kiosco. Para vincular un nuevo dispositivo, usa el asistente de configuración.", + "kiosk_reconfigure": "🔄 Reconfigurar báscula BLE", + "ble_protocols": "

    🔌 Protocolos BLE soportados:

    " + }, + "kiosk": { + "hint": "Convierte una tableta Android en un panel EverShelf permanente con pasarela BLE integrada.", + "download_btn": "📥 Descargar EverShelf Kiosk (APK)", + "download_sub": "Modo kiosco a pantalla completa + pasarela de báscula integrada. Fuente: evershelf-kiosk/", + "native_title": "Configuración del kiosco", + "native_hint": "URL del servidor, báscula BLE, protector de pantalla y asistente de configuración.", + "native_btn": "Abrir configuración del kiosco", + "native_tap_hint": "Toca el botón de engranaje en la parte superior derecha", + "native_update_hint": "Actualiza la app del kiosco para usar esta función", + "update_title": "Actualización del kiosco", + "check_updates_btn": "🔍 Buscar actualizaciones", + "needs_update": "⚠️ El kiosco instalado no admite esta función. Actualiza la app del kiosco para activarla." + }, + "saved": "✅ ¡Configuración guardada!", + "saved_local": "✅ Configuración guardada localmente", + "saved_local_error": "⚠️ Guardado localmente, error del servidor: {error}", + "theme": { + "title": "🌙 Apariencia", + "hint": "Elige el tema de la interfaz.", + "label": "🌙 Tema", + "off": "☀️ Claro", + "on": "🌙 Oscuro", + "auto": "🔄 Automático (sistema)" + }, + "zerowaste": { + "card_title": "♻️ Consejos sin desperdicios", + "card_hint": "Durante la cocción, muestra consejos sobre cómo reutilizar los restos generados en cada paso (peladuras, agua de cocción, etc.). Desactivado por defecto.", + "label": "Mostrar consejos durante la cocción" + }, + "backup": { + "tab": "Copia de seguridad", + "local_title": "Copia local", + "local_hint": "Instantánea diaria de la base de datos. Configura cuántos días de copias de seguridad conservar.", + "enabled": "Activar copia de seguridad diaria automática", + "retention_days": "Retención (días)", + "retention_info": "Las copias se conservan durante", + "backup_now": "Hacer copia ahora", + "backing_up": "Haciendo copia…", + "backed_up": "Copia completada", + "backup_error": "Error en la copia", + "last_backup": "Última copia", + "no_backup_yet": "Aún no se ha creado ninguna copia", + "list_empty": "No hay copias disponibles", + "restore_btn": "Restaurar", + "restore_confirm": "Restaurar la copia", + "delete_btn": "Eliminar", + "delete_confirm": "Eliminar la copia", + "gdrive_title": "Google Drive", + "gdrive_hint": "Copias de seguridad automáticas en Google Drive via OAuth 2.0. No se requieren bibliotecas externas.", + "gdrive_enabled": "Activar copia en Google Drive", + "gdrive_folder_id": "ID de carpeta de Drive", + "gdrive_folder_id_hint": "Copia el ID desde la URL de la carpeta de Drive: …/folders/ID", + "gdrive_retention_days": "Retención en Drive (días, 0=mantener todo)", + "gdrive_test": "Probar conexión", + "gdrive_ok": "Conexión exitosa!", + "gdrive_error": "Conexión fallida", + "gdrive_push_now": "Subir a Drive ahora", + "gdrive_pushing": "Subiendo…", + "gdrive_pushed": "Subido a Drive", + "gdrive_wizard_hint": "Opcional: copia de seguridad diaria automática en Google Drive via OAuth 2.0.", + "gdrive_skip": "Omitir — configurar después en Ajustes", + "gdrive_client_id": "Client ID", + "gdrive_client_secret": "Client Secret", + "gdrive_redirect_uri_hint": "Agrega http://localhost como URI de redireccionamiento autorizado en Google Cloud Console. Funciona en cualquier servidor, incluso sin dominio público.", + "gdrive_code_title": "Pegar la URL o el código de autorización", + "gdrive_code_hint": "Tras autorizar, el navegador abrirá http://localhost y puede mostrar un error de conexión — es normal. Copia la URL de la barra de direcciones (ej. http://localhost/?code=4%2F0A...) y pégala aquí.", + "gdrive_code_submit": "Confirmar", + "gdrive_code_empty": "Pega primero la URL o el código de autorización", + "gdrive_redirect_uri_label": "URI de redirección (agregar en Google Cloud Console):", + "gdrive_oauth_authorize": "Autorizar con Google", + "gdrive_oauth_authorized": "Autorizado", + "gdrive_oauth_not_authorized": "Aún no autorizado", + "gdrive_oauth_window_opened": "Ventana abierta — autoriza y regresa aquí", + "gdrive_oauth_how_to": "Cómo configurar OAuth 2.0 (paso a paso)", + "gdrive_oauth_steps": "
  • Ve a console.cloud.google.com y selecciona tu proyecto
  • Habilita la API de Google Drive: API y servicios → Habilitar API → Google Drive API
  • Ve a API y servicios → Credenciales → Crear credenciales → ID de cliente OAuth
  • Tipo de aplicación: Aplicación web; agrega la URL mostrada abajo como URI de redirección autorizado
  • Copia el Client ID y el Client Secret en los campos de arriba y guarda
  • Haz clic en Autorizar con Google: inicia sesión en tu cuenta de Google y concede acceso
  • La ventana se cierra automáticamente al finalizar y las copias de seguridad están listas
  • " + }, + "shopping": { + "tab": "Lista de la compra", + "title": "Lista de la compra", + "hint": "Configura la lista de la compra integrada o conecta Bring!.", + "enable_label": "Activar lista de la compra", + "mode_label": "Proveedor", + "mode_internal": "Integrado (sin Bring!)", + "mode_bring": "Bring! (app externa)", + "bring_section_title": "Configuración de Bring!", + "ai_section_title": "Asistencia IA", + "smart_suggestions_label": "Sugerencias IA", + "forecast_label": "Previsión de productos por agotar", + "auto_add_label": "Añadir automáticamente cuando", + "auto_add_suffix": "restante en stock (0 = solo cuando se agota)" + }, + "ha": { + "tab": "Home Assistant", + "title": "Home Assistant", + "hint": "Conecta EverShelf a Home Assistant para automatizaciones, notificaciones push y sensores REST.", + "enabled": "Activar integración con Home Assistant", + "connection_title": "Conexión", + "url_label": "URL de Home Assistant", + "url_placeholder": "http://192.168.1.50:8123", + "url_hint": "URL base de tu instancia de Home Assistant.", + "token_label": "Token de acceso de larga duración", + "token_hint": "Genera desde Perfil HA → Seguridad → Tokens de acceso de larga duración.", + "token_placeholder": "eyJhbGci...", + "token_saved": "Token guardado (oculto por seguridad)", + "test_btn": "Probar conexión", + "test_ok": "Conectado a {version}", + "test_fail": "Conexión fallida: {error}", + "test_bad_token": "HA accesible pero el token no es válido", + "testing": "Probando…", + "error_no_url": "Por favor, introduce primero la URL de Home Assistant.", + "tts_title": "TTS en altavoz inteligente", + "tts_hint": "Lee los pasos de la receta en un reproductor de medios de Home Assistant.", + "tts_entity_label": "Entity ID del reproductor multimedia", + "tts_entity_placeholder": "media_player.salon", + "tts_entity_hint": "ID de entidad del reproductor multimedia HA. Encuéntralo en HA: Herramientas para desarrolladores → Estados.", + "tts_platform_label": "Plataforma TTS", + "tts_platform_speak": "tts.speak (recomendado)", + "tts_platform_notify": "notify.* (servicio de notificaciones)", + "tts_apply_btn": "Aplicar preset HA a la pestaña TTS", + "tts_apply_hint": "Pre-rellena la pestaña TTS con la URL y el token de Home Assistant.", + "tts_preset_applied": "Preset HA aplicado a la pestaña TTS.", + "webhook_title": "Automatizaciones Webhook", + "webhook_hint": "Envía datos a Home Assistant cuando ocurren eventos en la despensa.", + "webhook_id_label": "ID de Webhook", + "webhook_id_placeholder": "evershelf_webhook_abc123", + "webhook_id_hint": "ID del webhook creado en HA. Copia desde: HA → Ajustes → Automatizaciones → Crear → Disparador Webhook.", + "webhook_events_label": "Notificar en estos eventos", + "event_expiry": "Productos próximos a caducar (diario)", + "event_shopping": "Artículo añadido a la lista de compras", + "event_stock": "Nivel de stock actualizado", + "expiry_days_label": "Antelación de caducidad (días)", + "expiry_days_hint": "Enviar alerta de caducidad N días antes de la fecha.", + "webhook_help": "En HA: Ajustes → Automatizaciones → Crear automatización → Disparador: Webhook → copia el ID generado.", + "notify_title": "Notificaciones push", + "notify_hint": "Envía notificaciones push a tu teléfono mediante un servicio notify de Home Assistant.", + "notify_service_label": "Servicio notify", + "notify_service_placeholder": "notify.mobile_app_mi_telefono", + "notify_service_hint": "Nombre del servicio notify de HA. Déjalo vacío para desactivar.", + "sensor_title": "Sensores REST", + "sensor_hint": "Añade a configuration.yaml para crear sensores de EverShelf en Home Assistant.", + "sensor_copy_btn": "Copiar YAML", + "sensor_copied": "¡YAML copiado al portapapeles!", + "save_btn": "Guardar ajustes HA", + "ha_hint": "Si usas Home Assistant, utiliza la pestaña Home Assistant para configurar TTS, webhooks y sensores." } + }, + "expiry": { + "today": "HOY", + "tomorrow": "Mañana", + "days": "{days} días", + "expired_days": "hace {days}d", + "expired_yesterday": "Ayer", + "expired_today": "Hoy", + "badge_today": "⚠️ ¡Caduca hoy!", + "badge_tomorrow": "⏰ Mañana", + "badge_tomorrow_long": "⏰ Caduca mañana", + "badge_days": "⏰ {n} días", + "badge_expired_ago": "⚠️ Caducado hace {n}d", + "badge_expired": "⛔ ¡Caducado!", + "badge_stable": "✅ Estable", + "badge_expiring_short": "⏰ Cad. en {n}d", + "badge_ok_still": "✅ Aún {n}d", + "badge_expires_red": "🔴 Cad. en {n}d", + "badge_expires_yellow": "🟡 Cad. en {n}d", + "badge_expired_bare": "⚠️ Caducado", + "badge_expires_warn": "⚠️ Cad. en {n}d", + "badge_days_left": "⏳ ~{n}d restantes", + "days_approx": "~{n} días", + "weeks_approx": "~{n} semanas", + "months_approx": "~{n} meses", + "years_approx": "~{n} años", + "expired_today_long": "Caducado hoy", + "expired_ago_long": "Caducado hace {n} días", + "expired_suffix": "— ¡Caducado!", + "expired_suffix_ok": "— Caducado (aún ok)", + "expired_suffix_warning": "— Caducado (comprobar primero)", + "opened_ago_long": "Abierto hace {n} días", + "opened_today_long": "Abierto hoy", + "opened_suffix": "— ¡Abierto demasiado tiempo!", + "opened_suffix_ok": "— Abierto (aún ok)", + "opened_suffix_warning": "— Abierto (comprobar primero)", + "days_compact": "{n}d", + "badge_check_soon": "Comprobar pronto" + }, + "status": { + "ok": "OK", + "check": "Comprobar", + "discard": "Desechar", + "tip_freezer_ok": "En congelador: aún seguro (~{n}d de margen)", + "tip_freezer_check": "En el congelador mucho tiempo, puede haber perdido calidad. Consumir pronto", + "tip_freezer_danger": "En el congelador demasiado tiempo, riesgo de quemadura por congelación y degradación", + "tip_highRisk_check": "Caducado recientemente, comprueba el olor y el aspecto antes de consumir", + "tip_highRisk_danger": "Producto perecedero caducado: desechar por seguridad", + "tip_medRisk_check1": "Comprueba el aspecto y el olor antes de consumir", + "tip_medRisk_check2": "Caducado hace tiempo, comprueba cuidadosamente antes de usar", + "tip_medRisk_danger": "Demasiado tiempo desde la caducidad, mejor desechar", + "tip_lowRisk_ok": "Producto de larga duración, aún seguro para consumir", + "tip_lowRisk_check": "Caducado hace más de un mes, comprueba la integridad del envase", + "tip_lowRisk_danger": "Caducado hace demasiado tiempo, mejor no arriesgarse" + }, + "toast": { + "product_saved": "¡Producto guardado!", + "product_created": "¡Producto creado!", + "product_updated": "✅ ¡Producto actualizado!", + "product_removed": "Producto eliminado", + "updated": "¡Actualizado!", + "quantity_confirmed": "✓ Cantidad confirmada", + "added_to_inventory": "✅ ¡{name} añadido!", + "removed_from_list": "✅ ¡{name} eliminado de la lista!", + "removed_from_list_short": "Eliminado de la lista", + "added_to_shopping": "🛒 ¡Añadido a la lista de la compra!", + "removed_from_shopping": "🛒 Eliminado de la lista de la compra", + "finished_to_bring": "🛒 Producto terminado → ¡añadido a Bring!", + "thrown_away": "🗑️ ¡{name} tirado!", + "thrown_away_partial": "🗑️ {qty} {unit} de {name} tirado(s)", + "finished_all": "📤 ¡{name} terminado!", + "product_finished_confirmed": "✅ Eliminado — añádelo de nuevo cuando reabastezcas", + "appliance_added": "Electrodoméstico añadido", + "item_added": "{name} añadido" + }, + "antiwaste": { + "title": "🌱 Informe anti-desperdicio", + "grade_label": "Nota", + "you": "Tú", + "avg_label": "Media", + "better": "🎉 ¡Desperdicias un {diff}% menos que la media {country}!", + "worse": "⚠️ Desperdicias más que la media {country}. ¡Hay margen de mejora!", + "on_par": "→ Estás en la media {country}. ¡Puedes hacerlo mejor!", + "saved_money": "~{amount}/mes ahorrado", + "saved_meals": "~{n} comidas salvadas", + "saved_co2": "{n} kg CO₂ evitados", + "trend_title": "Tendencia (últimos 3 meses)", + "months_ago_2": "-60 días", + "months_ago_1": "-30 días", + "this_month": "Ahora", + "country_it": "Media italiana", + "country_de": "Media alemana", + "country_en": "Media estadounidense", + "source": "Fuentes: REDUCE, Eurostat, USDA 2021", + "live_on": "Datos en vivo", + "live_off": "Sin conexión", + "meals": "comidas", + "annual_info": "📅 Tú ~{you} kg/año · media ~{avg} kg/año", + "badge_rate": "tasa de pérdida", + "badge_saved_money": "ahorrado vs media", + "badge_wasted": "artículos perdidos", + "badge_better": "menos que la media" + }, + "error": { + "generic": "Error", + "network": "Error de red", + "no_api_key": "Configura la clave API en los ajustes", + "loading": "Error al cargar el producto", + "not_found": "Producto no encontrado", + "not_found_manual": "Producto no encontrado. Introdúcelo manualmente.", + "search": "Error de búsqueda. Inténtalo de nuevo.", + "search_short": "Error de búsqueda", + "save": "Error al guardar", + "connection": "Error de conexión", + "camera": "No se puede acceder a la cámara", + "bring_add": "Error al añadir a Bring!", + "bring_connection": "Error de conexión con Bring!", + "identification": "Error de identificación", + "ai_quota": "Cuota de IA agotada. Inténtalo de nuevo en unos minutos.", + "barcode_empty": "Introduce un código de barras", + "barcode_format": "El código de barras solo puede contener números (4-14 dígitos)", + "min_chars": "Escribe al menos 2 caracteres", + "not_in_inventory": "Producto no en inventario", + "appliance_exists": "El electrodoméstico ya existe", + "already_exists": "Ya existe", + "network_retry": "Error de conexión. Inténtalo de nuevo.", + "select_items": "Selecciona al menos un producto", + "server_offline": "Conexión con el servidor perdida", + "server_restored": "Conexión con el servidor restaurada", + "server_retry": "Reintentar", + "unknown": "Error desconocido", + "prefix": "Error", + "no_inventory_entry": "No se encontró ninguna entrada de inventario", + "offline_title": "Sin conexión", + "offline_subtitle": "La app no puede conectar con el servidor. Verifica tu conexión Wi-Fi.", + "offline_checking": "Verificando conexión…", + "offline_restored": "¡Conexión restaurada!", + "offline_continue": "Continuar en modo sin conexión", + "offline_reading_cache": "Leyendo desde caché local", + "offline_ops_pending": "{n} operaciones pendientes", + "offline_synced": "{n} operaciones sincronizadas", + "offline_ai_disabled": "No disponible sin conexión", + "offline_cache_ready": "Offline — {n} productos en caché" + }, + "confirm_placeholder_search": null, + "confirm": { + "remove_item": "¿Realmente quieres eliminar este producto del inventario?", + "kiosk_exit": "¿Salir del modo kiosco?", + "cancel": "Cancelar", + "proceed": "Confirmar", + "discard_one": "Tirar 1 unidad" + }, + "location": { + "dispensa": "Despensa", + "frigo": "Nevera", + "freezer": "Congelador" + }, + "edit": { + "title": "Editar {name}", + "unknown_hint": "Introduce el nombre del producto y la información", + "label_name": "🏷️ Nombre del producto", + "choose_location_title": "¿Qué ubicación?", + "choose_location_hint": "Elige la ubicación a editar:", + "confirm_large_qty": "Estás configurando la cantidad a {qty} {unit}. Esto parece inusualmente alto. ¿Confirmar?" + }, + "screensaver": { + "recipe_btn": "Recetas", + "scan_btn": "Escanear producto" + }, + "days": { + "mon": "Lunes", + "tue": "Martes", + "wed": "Miércoles", + "thu": "Jueves", + "fri": "Viernes", + "sat": "Sábado", + "sun": "Domingo", + "mon_short": "Lun", + "tue_short": "Mar", + "wed_short": "Mié", + "thu_short": "Jue", + "fri_short": "Vie", + "sat_short": "Sáb", + "sun_short": "Dom" + }, + "meal_types": { + "lunch": "Almuerzo", + "dinner": "Cena", + "colazione": "Desayuno", + "merenda": "Merienda", + "dolce": "Postre", + "succo": "Zumo de fruta", + "pranzo": "Almuerzo", + "cena": "Cena" + }, + "scale": { + "status_connected": "Báscula conectada", + "status_searching": "Pasarela conectada, esperando báscula…", + "status_disconnected": "Pasarela de báscula inaccesible", + "status_error": "Error de conexión con la pasarela", + "not_connected": "Pasarela de báscula no conectada", + "read_btn": "⚖️ Leer desde báscula", + "reading_title": "Lectura de báscula", + "place_on_scale": "Coloca el producto en la báscula…", + "waiting_stable": "El peso se capturará automáticamente cuando la lectura sea estable.", + "no_url": "Introduce la URL de la pasarela", + "testing": "⏳ Probando conexión…", + "connected_ok": "¡Conexión con la pasarela exitosa!", + "timeout": "Tiempo de espera agotado: sin respuesta de la pasarela", + "error_connect": "No se puede conectar a la pasarela", + "tab": "Báscula inteligente", + "low_weight": "Peso < 10 g · introduce manualmente\n(la lectura automática requiere al menos 10 g)", + "density_hint": "(densidad {density} g/ml)", + "ml_hint": "(se convertirá a ml)", + "weight_detected": "Peso detectado — espera 10s de estabilidad…", + "weight_too_low": "Peso demasiado bajo — esperando…", + "stable": "✓ Estable", + "auto_confirm": "✅ {val} {unit} — confirmación automática en 5s (toca para cancelar)", + "cancelled_replace": "Cancelado — vuelve a colocar el ingrediente en la báscula para continuar" + }, + "prediction": { + "expected_qty": "Esperado: {expected} {unit}", + "actual_qty": "Actual: {actual} {unit}", + "check_suggestion": "Comprueba o pesa la cantidad restante" + }, + "date": { + "today": "📅 Hoy", + "yesterday": "📅 Ayer" + }, + "scanner": { + "title_barcode": "🔖 Escanear código de barras", + "barcode_hint": "Encuadra el código de barras del producto", + "barcode_manual_placeholder": "O introduce manualmente...", + "barcode_use_btn": "✅ Usar este código", + "ai_identifying": "🤖 Identificando producto...", + "ai_analyzing": "🤖 Análisis IA en curso...", + "product_label_hint": "Encuadra la etiqueta del producto", + "expiry_label_hint": "Encuadra la fecha de caducidad impresa en el producto", + "capture_btn": "📸 Capturar", + "capture_photo_btn": "📸 Sacar foto", + "retake_btn": "🔄 Repetir", + "camera_error_hint": "Asegúrate de usar HTTPS y haber concedido los permisos de cámara.
    Puedes introducir el código de barras manualmente o usar la identificación IA.", + "no_barcode": "Sin código de barras", + "save_new_btn": "🆕 Ninguno de estos — guardar como nuevo" + }, + "lowstock": { + "title": "⚠️ ¡Stock bajo!", + "message": "{name} se está agotando — solo quedan {qty}.", + "question": "¿Quieres añadirlo a la lista de la compra?", + "yes": "🛒 Sí, añadir a Bring!", + "no": "No, por ahora estoy bien" + }, + "move": { + "title": "📦 ¿Mover el resto?", + "question": "¿Quieres mover {thing} de {name} a otra ubicación?", + "question_short": "¿Quieres mover {thing} a otra ubicación?", + "thing_opened": "el paquete abierto", + "thing_rest": "el resto", + "stay_btn": "No, quedarse en {location}", + "moved_toast": "📦 Paquete abierto movido a {location}", + "vacuum_restore": "🫙 Restaurar al vacío", + "vacuum_seal_rest": "🔒 Sellar el resto al vacío" + }, + "nova": { + "1": "Sin procesar", + "2": "Ingrediente culinario", + "3": "Procesado", + "4": "Ultraprocesado" + }, + "meal_plan_types": { + "pasta": "Pasta", + "riso": "Arroz", + "carne": "Carne", + "pesce": "Pescado", + "legumi": "Legumbres", + "uova": "Huevos", + "formaggio": "Queso", + "pizza": "Pizza", + "affettati": "Fiambres", + "verdure": "Verduras", + "zuppa": "Sopa", + "insalata": "Ensalada", + "pane": "Pan/Bocadillo", + "dolce": "Postre", + "libero": "Libre" + }, + "meal_sub": { + "dolce_torta": "Tarta", + "dolce_crema": "Crema / Pudín", + "dolce_crumble": "Crumble / Tarta", + "dolce_biscotti": "Galletas / Pastelería", + "dolce_frutta": "Postre de fruta", + "succo_dolce": "Dulce / Afrutado", + "succo_energizzante": "Energizante", + "succo_detox": "Detox / Verde", + "succo_rinfrescante": "Refrescante", + "succo_vitaminico": "Vitamínico / Cítricos" + }, + "meal_plan": { + "reset_success": "Plan semanal restablecido", + "not_available": "no disponible en la despensa", + "suggested_by": "sugerido por el plan semanal" + }, + "nutrition": { + "title": "🥗 Análisis alimentario", + "score_excellent": "😄 Excelente", + "score_good": "🙂 Bien", + "score_improve": "😬 Mejorable", + "label_health": "🌿 Salud", + "label_variety": "🎨 Variedad", + "label_fresh": "❄️ Fresco", + "source": "Basado en {n} productos en tu despensa · EverShelf", + "products_count": "productos", + "today_title": "🥗 Tu despensa hoy", + "products_n": "{n} productos", + "macros_title": "Macronutrientes estimados", + "macros_proteins": "Proteínas", + "macros_carbs": "Carbohidratos", + "macros_fat": "Grasas", + "macros_fiber": "Fibra", + "macros_source": "Estimación basada en {n} productos en despensa" + }, + "facts": { + "greeting_morning": "Buenos días", + "greeting_afternoon": "Buenas tardes", + "greeting_evening": "Buenas noches", + "pantry_waiting": "¡{greeting}! Tu despensa te espera.", + "expired_one": "Tienes 1 producto caducado en tu despensa. ¡Compruébalo!", + "expired_many": "Tienes {n} productos caducados en tu despensa. ¡Compruébalos!", + "expired_list": "Productos caducados: {names}", + "expired_list_more": "y {n} más", + "freezer_expired_ok": "{name} está caducado, pero al estar en el congelador puede estar bien. ¡Compruébalo!", + "freezer_expired_old": "{name} en el congelador lleva demasiado tiempo caducado. Mejor tirarlo.", + "fridge_expired_one": "¡Tienes 1 producto caducado en la nevera!", + "fridge_expired_many": "¡Tienes {n} productos caducados en la nevera!", + "expiring_today": "¡{name} caduca hoy! Úsalo enseguida.", + "expiring_tomorrow": "{name} caduca mañana. ¡Planifica!", + "expiring_days": "{name} caduca en {days} días.", + "expiring_many": "Tienes {n} productos que caducan pronto.", + "expiring_this_week": "¡{n} productos caducan esta semana. Planifica tus comidas en consecuencia!", + "expiring_item_loc": "{name} ({loc}) caduca en {days} {dayslabel}.", + "expiring_this_month": "{n} productos caducarán este mes.", + "shopping_add": "Añadir a la lista: {names} 🛒", + "shopping_more": "y {n} más", + "shopping_empty": "¡Lista de la compra vacía. Todo en stock! ✅", + "in_fridge": "En la nevera: {name}.", + "in_freezer": "En el congelador: {name}. ¡No lo olvides!", + "top_category": "La categoría principal es {icon} {cat} con {n} productos.", + "cat_meat": "Tienes {n} productos cárnicos. 🥩", + "cat_dairy": "Tienes {n} productos lácteos en casa. 🥛", + "cat_veggies": "Tienes {n} tipos de verduras. ¡Genial para la salud! 🥬", + "cat_fruit": "Tienes {n} tipos de fruta. 🍎", + "cat_drinks": "Tienes {n} bebidas disponibles. 🥤", + "cat_frozen": "Tienes {n} artículos congelados. ❄️", + "cat_pasta": "Tienes {n} tipos de pasta. 🍝 ¿Y si hacemos una carbonara?", + "cat_canned": "Tienes {n} conservas en la despensa. 🥫", + "cat_snacks": "Tienes {n} snacks. ¡Resiste la tentación! 🍪", + "cat_condiments": "Tienes {n} condimentos disponibles. 🧂", + "item_random": "¿Sabías que tienes {name} en {loc}?", + "item_qty": "{name}: tienes {qty}.", + "no_expiry_count": "{n} productos no tienen fecha de caducidad.", + "furthest_expiry": "El producto con la fecha de caducidad más lejana es {name}: {months} meses.", + "high_qty": "¡Tienes un buen stock de {name}: {qty}!", + "low_qty_item": "{name} se está agotando. ¿Añadirlo a tu lista de la compra?", + "low_qty_count": "{n} productos están casi agotados.", + "morning_bread": "¡Buenos días! Tienes pan para el desayuno. 🍞", + "morning_milk": "¿Hay leche en la nevera para un café con leche? ☕🥛", + "morning_fruit": "¡Buenos días! Algo de fruta fresca es un gran comienzo. 🍎", + "noon_pasta": "Es hora de comer… ¿Y si preparamos un buen plato de pasta? 🍝", + "noon_salad": "¿Una ensalada fresca para comer? ¡Tienes {n} verduras! 🥗", + "evening_meat": "Para cenar podrías usar la carne que tienes. 🥩", + "evening_fish": "¿Qué tal pescado para cenar? 🐟", + "evening_expiring": "Tienes {n} productos que caducan esta semana — ¡úsalos esta noche!", + "night_reminder": "¡Buenas noches! Recuerda usar mañana: {names}.", + "weekly_balance": "Balance semanal: +{in} añadidos, −{out} consumidos.", + "weekly_added": "Has añadido {n} productos esta semana.", + "weekly_consumed": "Has consumido {n} productos esta semana. ¡Bien hecho!", + "tip_freezer": "💡 Los productos congelados duran mucho más que la fecha de caducidad.", + "tip_bread": "💡 El pan congelado conserva su frescura durante semanas.", + "tip_fifo": "💡 Para evitar desperdicios, usa primero los productos más cercanos a la caducidad (FIFO).", + "tip_meat": "💡 La carne en el congelador puede durar hasta 6 meses con seguridad.", + "tip_no_refreeze": "💡 Nunca vuelvas a congelar un producto descongelado. ¡Cocínalo enseguida!", + "tip_fridge": "💡 Una nevera ordenada te ahorra tiempo y dinero.", + "tip_canned": "💡 Las conservas abiertas deben ir a la nevera y consumirse en pocos días.", + "top_brand": "La marca más común en tu despensa es {brand} con {n} productos.", + "combo_pasta": "Tienes pasta y condimentos: ¡listo para un primer plato! 🍝", + "combo_sandwich": "Pan y carne: ¡un sándwich rápido siempre es buena idea! 🥪", + "combo_balanced": "Verduras y carne: ¡tienes todo para una comida equilibrada! 🥗🥩", + "pantry_empty": "¡La despensa está vacía! Es hora de ir al supermercado. 🛒", + "pantry_empty_scan": "No hay productos registrados. ¡Escanea algo para empezar!", + "location_distribution": "Distribución: {parts}", + "day": "día", + "days": "días" + }, + "kiosk_session": { + "first_item": "¡Primer artículo: {name}!", + "items_two_four": "{n} artículos — arrancando 🚀", + "items_five_nine": "{n} artículos — ¡buen ritmo! 💪", + "items_ten_twenty": "{n} artículos — casi un récord 🏆", + "items_twenty_plus": "{n} artículos — ¡compra épica! 🛒🔥", + "duplicates_one": "1 duplicado (mismo artículo dos veces)", + "duplicates_many": "{n} duplicados (cogido varias veces)", + "top_category": "Categoría principal: {cat} ({count}×)", + "items_fallback": "{n} artículo{plural} añadido{plural}" + }, + "kiosk": { + "check_btn": "🔍 Buscar actualizaciones", + "checking": "⏳ Comprobando…", + "error_check": "Error durante la comprobación de actualizaciones", + "error_start_install": "Error al iniciar la instalación", + "version_installed": "Instalado: {v}", + "update_available": "⬆️ Nueva versión disponible: {latest} (instalada: {current})", + "up_to_date": "✅ Estás actualizado — versión {v}", + "too_old": "⚠️ El kiosco instalado es demasiado antiguo para la comprobación automática de actualizaciones.
    Pulsa el botón de abajo para descargar e instalar la nueva versión directamente.", + "manual_install": "⚠️ Este kiosco no admite instalación automática.
    Procedimiento manual:
    1. Sal del kiosco (botón ✕ arriba a la izquierda)
    2. Desinstala la app EverShelf Kiosk
    3. Descarga e instala el nuevo APK desde GitHub:", + "starting_download": "⏳ Iniciando descarga…", + "install_btn": "⬇️ Instalar actualización", + "exit_title": "Salir del kiosco", + "refresh_title": "Actualizar página" + }, + "update": { + "new_version": "Nueva versión", + "btn": "Actualizar" + }, + "gemini": { + "chat_title": "Chat con Gemini", + "not_configured": "🤖 Gemini no configurado — establece GEMINI_API_KEY en los ajustes" + }, + "appliances": { + "empty": "No hay electrodomésticos añadidos" + }, + "about": { + "title": "Acerca de", + "version": "Versión", + "report_bug": "Reportar un error", + "report_bug_hint": "¿Algo no funciona? Envíanos un informe directamente desde la app.", + "report_bug_modal_title": "Reportar un error", + "report_type_bug": "Error", + "report_type_feature": "Función", + "report_type_question": "Pregunta", + "report_field_title": "Título", + "report_field_title_ph": "Breve descripción del problema", + "report_field_desc": "Descripción", + "report_field_desc_ph": "Describe el problema en detalle…", + "report_field_steps": "Pasos para reproducir (opcional)", + "report_field_steps_ph": "1. Ir a…\n2. Tocar…\n3. Ver el error…", + "report_auto_info": "Adjuntado automáticamente: versión {version}, idioma {lang}.", + "report_send_btn": "Enviar informe", + "report_bug_sending": "Enviando…", + "report_bug_sent": "¡Informe enviado — gracias!", + "report_bug_error": "No se pudo enviar el informe. Comprueba tu conexión.", + "changelog": "Registro de cambios", + "github": "Repositorio GitHub" + }, + "export": { + "title": "Exportar inventario", + "hint": "Descarga el inventario actual en CSV o abre la versión imprimible (PDF).", + "btn_csv": "Descargar CSV", + "btn_pdf": "PDF / Imprimir", + "btn_title": "Exportar" + }, + "startup": { + "connecting": "Conectando al servidor...", + "check_php_memory": "Memoria PHP", + "check_php_timeout": "Tiempo de espera PHP", + "check_php_upload": "Upload PHP", + "check_data_dir": "Carpeta de datos", + "check_rate_limits": "Dir. rate limits", + "check_backups": "Dir. copias de seguridad", + "check_write_test": "Prueba escritura disco", + "check_disk_space": "Espacio en disco", + "check_db_connect": "Conexión base de datos", + "check_db_tables": "Tablas de la BD", + "check_db_integrity": "Integridad BD", + "check_db_wal": "Modo WAL", + "check_db_size": "Tamaño de la BD", + "check_db_rows": "Datos del inventario", + "check_env": "Archivo .env", + "check_gemini": "Clave Gemini AI", + "check_bring_creds": "Credenciales Bring!", + "check_bring_token": "Token de Bring!", + "check_curl_ssl": "cURL SSL", + "check_internet": "Conexión a internet", + "fresh_install": "instalación nueva", + "warnings_found": "avisos detectados", + "all_ok": "Sistema OK", + "critical_error_short": "Error crítico", + "critical_error": "Error crítico: la aplicación no puede iniciarse. Revisa los registros del servidor.", + "error_network": "No se puede contactar con el servidor. Comprueba tu conexión de red.", + "retry": "Reintentar", + "syncing_local": "Sincronizando datos locales...", + "sync_done": "Datos locales sincronizados" + }, + "stats_monthly": { + "title": "Estadísticas Mensuales", + "consumed": "productos usados", + "trend_up": "+{pct}% vs {prev}", + "trend_down": "-{pct}% vs {prev}", + "trend_same": "mismo ritmo que el mes pasado", + "added": "añadidos", + "wasted": "desperdiciados", + "top_used": "más usado", + "top_cats": "Categorías principales", + "source": "Historial de transacciones · mes actual" + } } \ No newline at end of file diff --git a/translations/fr.json b/translations/fr.json index eda3281..1070b5b 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -1,1401 +1,1406 @@ { - "app": { - "name": "EverShelf", - "loading": "Chargement..." - }, - "nav": { - "title": "EverShelf", - "home": "Accueil", - "inventory": "Garde-manger", - "recipes": "Recettes", - "shopping": "Courses", - "log": "Journal", - "settings": "Paramètres" - }, - "btn": { - "back": "← Retour", - "save": "💾 Enregistrer", - "cancel": "✕ Annuler", - "close": "Fermer", - "add": "✅ Ajouter", - "delete": "Supprimer", - "edit": "✏️ Modifier", - "use": "Utiliser", - "edit_item": "Modifier", - "search": "🔍 Rechercher", - "go": "✅ Valider", - "toggle_password": "👁️ Afficher/Masquer", - "load_more": "Charger plus...", - "save_config": "💾 Enregistrer la configuration", - "save_product": "💾 Enregistrer le produit", - "restart": "↺ Redémarrer", - "reset_default": "↺ Rétablir les valeurs par défaut", - "save_info": "💾 Enregistrer les informations", - "retry": "🔄 Réessayer", - "yes_short": "Oui", - "no_short": "Non" - }, - "form": { - "select_placeholder": "-- Sélectionner --" - }, - "locations": { - "dispensa": "Garde-manger", - "frigo": "Réfrigérateur", - "freezer": "Congélateur", - "altro": "Autre" - }, - "categories": { - "latticini": "Produits laitiers", - "carne": "Viande", - "pesce": "Poisson", - "frutta": "Fruits", - "verdura": "Légumes", - "pasta": "Pâtes & Riz", - "pane": "Pain & Boulangerie", - "surgelati": "Surgelés", - "bevande": "Boissons", - "condimenti": "Condiments", - "snack": "Snacks & Sucreries", - "conserve": "Conserves", - "cereali": "Céréales & Légumineuses", - "igiene": "Hygiène", - "pulizia": "Entretien", - "altro": "Autre", - "select": "-- Sélectionner --" - }, - "units": { - "pz": "pcs", - "conf": "pkg", - "g": "g", - "ml": "ml", - "pieces": "Pièces", - "grams": "Grammes", - "box": "Paquet", - "boxes": "Paquets", - "millilitres": "Millilitres", - "from": "de" - }, - "shopping_sections": { - "frutta_verdura": "Fruits & Légumes", - "carne_pesce": "Viande & Poisson", - "latticini": "Produits laitiers & Frais", - "pane_dolci": "Pain & Pâtisseries", - "pasta": "Pâtes & Céréales", - "conserve": "Conserves & Sauces", - "surgelati": "Surgelés", - "bevande": "Boissons", - "pulizia_igiene": "Nettoyage & Hygiène", - "altro": "Autre" - }, - "dashboard": { - "expired_title": "🚫 Périmé", - "expiring_title": "⏰ Expire bientôt", - "stats_period": "📊 30 derniers jours", - "opened_title": "📦 Produits ouverts", - "review_title": "🔍 À vérifier", - "review_hint": "Quantités inhabituelles. Confirmez si elles sont correctes ou modifiez-les.", - "quick_recipe": "Recette rapide avec les produits qui expirent", - "banner_review_title": "Quantité anormale", - "banner_review_action_ok": "C'est correct", - "banner_review_action_finish": "🗑️ Tout fini", - "banner_review_action_edit": "Corriger", - "banner_review_action_weigh": "Peser", - "banner_review_dismiss": "Ignorer", - "banner_prediction_title": "Consommation à vérifier", - "banner_prediction_hint": "L'estimation de consommation s'adapte aux données récentes : confirmez uniquement si la quantité actuelle est correcte.", - "banner_prediction_action_confirm": "Confirmer {qty} {unit}", - "banner_prediction_action_weigh": "Peser maintenant", - "banner_prediction_action_edit": "Mettre à jour la quantité", - "banner_expired_title": "Produit périmé", - "banner_expired_today": "Périmé aujourd'hui", - "banner_expired_days": "Périmé il y a {days} jours", - "banner_expired_action_use": "Utiliser quand même", - "banner_expired_action_finished": "Je l'ai terminé !", - "banner_expired_action_throw": "Je l'ai jeté", - "banner_expired_action_edit": "Corriger la date", - "banner_anomaly_action_edit": "Corriger l'inventaire", - "banner_anomaly_action_dismiss": "La quantité est correcte", - "banner_no_expiry_title": "Date manquante : {name}", - "banner_no_expiry_detail": "Ce produit n'a pas de date de péremption. Voulez-vous en ajouter une ou confirmer qu'il ne périme pas ?", - "banner_no_expiry_action_set": "Définir une date de péremption", - "banner_no_expiry_action_dismiss": "Ne périme pas ✓", - "banner_no_expiry_toast_dismissed": "Marqué comme « sans date limite »", - "banner_expiring_title": "Expire bientôt", - "banner_expiring_today": "Expire aujourd'hui !", - "banner_expiring_tomorrow": "Expire demain", - "banner_expiring_days": "Expire dans {days} jours", - "banner_expiring_action_use": "Utiliser maintenant", - "banner_finished_title": "terminé ?", - "banner_finished_detail": "J'ai enregistré que {name} a atteint zéro stock. Est-il vraiment épuisé ou en avez-vous encore ?", - "banner_finished_action_yes": "Oui, c'est fini", - "banner_finished_action_no": "Non, j'en ai encore", - "banner_review_unusual_pkg_title": "Taille de paquet inhabituelle", - "banner_review_unusual_pkg_detail": "Vous avez défini un paquet de {qty} {unit} — la taille semble très grande. Vérifiez si c'est correct ou modifiez.", - "banner_review_low_qty_title": "Quantité très faible", - "banner_review_low_qty_detail": "Vous n'avez que {qty} en stock — cela semble très peu, peut-être une erreur de saisie. Confirmez si c'est correct.", - "banner_review_high_qty_title": "Quantité inhabituellement élevée", - "banner_review_high_qty_detail": "Vous avez {qty} en stock — le chiffre semble très élevé. Confirmez si c'est correct ou modifiez.", - "banner_prediction_rate_day": "Moyenne ~{n} {unit}/jour", - "banner_prediction_rate_week": "Moyenne ~{n} {unit}/semaine", - "banner_prediction_days_ago": "Il y a {n} jours vous avez réapprovisionné", - "banner_prediction_more": "estimation précédente : {expected} {unit}{time} ; quantité actuelle : {actual} {unit}.", - "banner_prediction_less": "estimation : {expected} {unit}{time} ; quantité actuelle : {actual} {unit}. Si votre rythme d'utilisation a changé, la prévision se met à jour automatiquement.", - "banner_finished_zero": "L'inventaire indique zéro, mais les mouvements enregistrés suggèrent qu'il ne devrait pas être vide.", - "banner_finished_expected": "D'après les enregistrements vous devriez avoir encore {qty} {unit}.", - "banner_finished_check": "Pouvez-vous vérifier ?", - "banner_anomaly_phantom_title": "vous avez plus de stock que prévu", - "banner_anomaly_phantom_detail": "L'inventaire indique {inv_qty} {unit}, mais selon les enregistrements vous ne devriez avoir que {expected_qty} {unit}. Avez-vous ajouté du stock sans l'enregistrer ?", - "banner_anomaly_untracked_title": "stock non enregistré comme entrée", - "banner_anomaly_untracked_detail": "Vous avez {inv_qty} {unit} en inventaire, mais les sorties enregistrées dépassent les entrées — le stock initial n'a probablement jamais été ajouté comme transaction « entrée ». Vous pouvez corriger la quantité ou saisir les entrées manquantes.", - "banner_anomaly_ghost_title": "vous avez moins de stock que prévu", - "banner_anomaly_ghost_detail": "D'après les opérations enregistrées vous devriez avoir {expected_qty} {unit} de {name}, mais l'inventaire n'en montre que {inv_qty} {unit}. Avez-vous pris du stock sans l'enregistrer ?", - "consumed": "Consommé : {n} ({pct}%)", - "wasted": "Gaspillé : {n} ({pct}%)", - "more_opened": "et {n} autres ouverts...", - "banner_expired_detail": "{when} · il vous reste encore {qty}.", - "banner_opened_detail": "{when} dans {location} · il vous reste encore {qty}.", - "banner_explain_title": "Demander une explication à Gemini", - "banner_explain_btn": "Expliquer", - "banner_analyzing": "🤖 Analyse en cours…" - }, - "inventory": { - "title": "Garde-manger", - "filter_all": "Tout", - "search_placeholder": "🔍 Rechercher un produit...", - "recent_title": "🕐 Utilisés récemment", - "popular_title": "⭐ Les plus utilisés", - "empty": "Aucun produit ici.\nScannez un produit pour l'ajouter !", - "no_items_found": "Aucun article trouvé", - "qty_remainder_suffix": "restant", - "vacuum_badge": "🫙 Sous vide", - "opened_badge": "📭 Ouvert", - "label_expiry": "📅 Péremption", - "label_storage": "🫙 Conservation", - "label_status": "📭 État", - "opened_since": "Ouvert depuis le {date}", - "label_position": "📍 Emplacement", - "label_quantity": "📦 Quantité", - "label_added": "📅 Ajouté", - "empty_text": "Aucun produit ici.
    Scannez un produit pour l'ajouter !", - "empty_db": "Aucun produit dans la base de données.
    Scannez un produit pour commencer !", - "qty_trace": "< 1" - }, - "scan": { - "title": "Scanner", - "mode_shopping": "🛒 Mode courses", - "mode_shopping_end": "✅ Terminer les courses", - "spesa_btn": "🛒 Courses", - "zoom": "Zoom", - "tab_barcode": "Code-barres", - "tab_name": "Nom", - "tab_ai": "IA", - "recents_label": "Récents", - "torch_hint": "Torche", - "torch_on": "Torche activée", - "torch_off": "Torche désactivée", - "torch_unavailable": "Torche non disponible sur cet appareil", - "flip_hint": "Retourner la caméra", - "flip_front": "Caméra frontale", - "flip_back": "Caméra arrière", - "num_ocr_btn": "🔢 Lire les chiffres avec l'IA", - "num_ocr_searching": "Recherche du code-barres avec l'IA...", - "num_ocr_found": "Code trouvé : {code}", - "num_ocr_not_found": "Aucun code-barres trouvé dans l'image", - "barcode_placeholder": "Entrez le code-barres...", - "quick_name_divider": "ou tapez le nom", - "quick_name_placeholder": "Ex. : Pommes, Courgettes, Pain...", - "manual_entry": "✏️ Saisie manuelle", - "ai_identify": "🤖 Identifier avec l'IA", - "hint": "Scannez le code-barres, tapez le nom du produit ou utilisez l'IA pour l'identifier", - "debug_toggle": "🐛 Journal de débogage", - "barcode_acquired": "🔖 Code-barres scanné : {code}", - "scan_barcode": "🔖 Scanner le code-barres", - "create_named": "Créer {name}", - "new_without_barcode": "Nouveau produit sans code-barres", - "status_ready": "Pointez la caméra sur le code-barres", - "status_scanning": "Scan en cours...", - "status_partial": "Lu : {code} — vérification...", - "status_invalid": "Invalide : {code} — nouvel essai", - "status_confirmed": "Confirmé !", - "status_parallel": "Scan combiné actif..." - }, - "action": { - "title": "Que voulez-vous faire ?", - "add_btn": "📥 AJOUTER", - "add_sub": "au garde-manger/réfrigérateur", - "use_btn": "📤 UTILISER / CONSOMMER", - "use_sub": "depuis le garde-manger/réfrigérateur", - "have_title": "📦 Déjà en stock !", - "add_more_sub": "en ajouter", - "use_qty_sub": "combien vous avez utilisé", - "throw_btn": "🗑️ JETER", - "throw_sub": "jeter", - "edit_sub": "péremption, emplacement…", - "create_recipe_btn": "Recette" - }, - "add": { - "title": "Ajouter au garde-manger", - "location_label": "📍 Où le rangez-vous ?", - "quantity_label": "📦 Quantité", - "conf_size_label": "📦 Chaque paquet contient :", - "conf_size_placeholder": "ex. 300", - "vacuum_label": "🫙 Sous vide", - "vacuum_hint": "La date de péremption sera automatiquement prolongée", - "submit": "✅ Ajouter", - "purchase_type_label": "🛒 Ce produit est...", - "new_btn": "🆕 Vient d'être acheté", - "existing_btn": "📦 Je l'avais déjà", - "remaining_label": "📦 Quantité restante", - "remaining_hint": "Approximativement combien en reste-t-il ?", - "remaining_full": "🟢 Plein", - "remaining_half": "🟠 À moitié", - "estimated_expiry": "Date de péremption estimée :", - "suffix_freezer": "(congélateur)", - "suffix_vacuum": "(sous vide)", - "hint_modify": "📝 Vous pouvez modifier la date ou la scanner avec la caméra", - "scan_expiry_title": "📷 Scanner la date de péremption", - "product_added": "✅ {name} ajouté !{qty}", - "suffix_freezer_vacuum": "(congélateur + sous vide)", - "history_badge_tip": "Moyenne de {n} entrées précédentes", - "vacuum_question": "Sous vide ?", - "vacuum_saved": "🔒 Sous vide !" - }, - "use": { - "title": "Utiliser / Consommer", - "location_label": "📍 Depuis où ?", - "quantity_label": "Combien avez-vous utilisé ?", - "change": "modifier", - "partial_hint": "Ou précisez la quantité utilisée :", - "partial_piece_hint": "Avez-vous utilisé seulement une partie ?", - "piece": "pièce", - "one_whole": "1 entier", - "use_all": "🗑️ Tout utilisé / Terminé", - "submit": "📤 Utiliser cette quantité", - "available": "📦 Disponible :", - "opened_badge": "OUVERT", - "not_in_inventory": "⚠️ Produit absent de l'inventaire.", - "expiry_warning": "⚠️ Utilisez en premier celui{loc} qui expire le {date} — {when} !", - "expiry_warning_opened": "⚠️ Celui{loc} est ouvert depuis {when} — utilisez-le en premier !", - "throw_title": "🗑️ Jeter le produit", - "throw_all": "🗑️ Tout jeter ({qty})", - "throw_qty_label": "Quelle quantité jeter ?", - "throw_qty_hint": "ou entrez une quantité :", - "throw_partial_btn": "🗑️ Jeter cette quantité", - "when_expired": "périmé il y a {n} jours", - "when_today": "expire aujourd'hui", - "when_tomorrow": "expire demain", - "when_days": "expire dans {n} jours", - "toast_used": "📤 {qty} de {name} utilisé", - "toast_bring": "🛒 Produit terminé → ajouté à Bring !", - "toast_opened_finished": "🔓 Emballage ouvert de {name} terminé !", - "disambiguation_hint": "Que voulez-vous dire par « tout fini » ?", - "disambiguation_all": "🗑️ Tout finir ({qty})", - "error_exceeds_stock": "⚠️ Vous ne pouvez pas utiliser plus que ce que vous avez disponible !", - "use_all_confirm_title": "✅ Tout terminer", - "use_all_confirm_msg": "Confirmez que vous avez terminé le produit :", - "use_all_confirm_btn": "✅ Oui, terminé", - "throw_all_confirm_title": "🗑️ Tout jeter", - "throw_all_confirm_msg": "Voulez-vous vraiment jeter tout le produit ?", - "throw_all_confirm_btn": "🗑️ Oui, jeter" - }, - "product": { - "title_new": "Nouveau produit", - "title_edit": "Modifier le produit", - "ai_fill": "📷 Prendre une photo et identifier avec l'IA", - "ai_fill_hint": "L'IA remplira automatiquement les champs du produit", - "name_label": "🏷️ Nom du produit *", - "name_placeholder": "Ex. : Lait entier, Pâtes penne...", - "brand_label": "🏢 Marque", - "brand_placeholder": "Ex. : Barilla, Danone, Heinz...", - "category_label": "📂 Catégorie", - "unit_label": "📏 Unité de mesure", - "default_qty_label": "🔢 Quantité par défaut", - "conf_size_label": "📦 Chaque paquet contient :", - "conf_size_placeholder": "ex. 300", - "notes_label": "📝 Notes", - "notes_placeholder": "Ex. : sans lactose, biologique, conserver au réfrigérateur après ouverture...", - "barcode_label": "🔖 Code-barres", - "barcode_placeholder": "Code-barres (si disponible)", - "barcode_hint": "⚠️ Ajoutez le code-barres pour la prochaine fois, il suffira de scanner !", - "submit": "💾 Enregistrer le produit", - "name_required": "Entrez le nom du produit", - "conf_size_required": "Précisez le contenu du paquet", - "expiry_estimated": "Date de péremption estimée :", - "scan_expiry": "Scanner la date de péremption", - "expiry_hint": "📝 Vous pouvez modifier la date ou la scanner avec la caméra", - "add_batch": "📦 + Lot avec une date différente", - "package_info": "📦 Paquet : {info}", - "edit_catalog": "⚙️ Modifier les infos produit (nom, marque, catégorie…)", - "not_recognized": "⚠️ Produit non reconnu", - "edit_info": "✏️ Modifier les informations", - "modify_details": "MODIFIER\npéremption, emplacement…", - "already_in_pantry": "📋 Déjà dans le garde-manger", - "no_barcode": "Pas de code-barres", - "unknown_product": "Produit non reconnu", - "edit_name_brand": "Modifier nom/marque", - "weight_label": "Poids", - "origin_label": "Origine", - "labels_label": "Labels", - "select_variant": "Sélectionnez la variante exacte ou utilisez les données IA :" - }, - "products": { - "title": "📦 Tous les produits", - "search_placeholder": "🔍 Rechercher un produit...", - "empty": "Aucun produit dans la base de données.\nScannez un produit pour commencer !", - "no_category": "Aucun produit dans cette catégorie" - }, - "recipes": { - "title": "🍳 Recettes", - "generate": "✨ Générer une nouvelle recette", - "archive_empty": "Aucune recette enregistrée. Générez votre première recette !", - "dialog_title": "🍳 Recette", - "dialog_desc": "Je vais générer une recette saine en utilisant les ingrédients du garde-manger, en priorisant les produits qui expirent.", - "meal_label": "🕐 Quel repas ?", - "persons_label": "👥 Pour combien de personnes ?", - "meal_type_label": "🎯 Type de repas", - "opt_fast": "⚡ Repas rapide", - "opt_light": "🥗 Appétit léger", - "opt_expiry": "⏰ Prioriser les produits qui expirent", - "opt_healthy": "💚 Extra sain", - "opt_opened": "📦 Prioriser les produits ouverts", - "opt_zero_waste": "♻️ Zéro déchet", - "generate_btn": "✨ Générer la recette", - "loading_msg": "Préparation de votre recette...", - "start_cooking": "👨‍🍳 Mode cuisine", - "regenerate": "🔄 En générer une autre", - "regen_choice_title": "Que veux-tu faire de cette recette ?", - "regen_replace": "🔄 En générer une autre (ignorer celle-ci)", - "regen_save_new": "💾 Sauvegarder dans l'archive et en générer une nouvelle", - "close_btn": "✅ Fermer", - "ingredients_title": "🧾 Ingrédients", - "tools_title": "Matériel nécessaire", - "steps_title": "👨‍🍳 Étapes", - "no_steps": "Aucune étape disponible", - "generate_error": "Erreur de génération", - "persons_short": "pers.", - "use_ingredient_title": "Utiliser l'ingrédient", - "recipe_qty_label": "Recette", - "from_where_label": "Depuis où ?", - "amount_label": "Combien", - "use_amount_btn": "Utiliser cette quantité", - "use_all_btn": "Tout utiliser / Terminé", - "packs_label": "Paquets", - "quantity_in_total": "Quantité en {unit} (total : {total})", - "packs_of_have": "Paquets de {size} (vous en avez {count})", - "scale_wait_stable": "Attendez 10s de poids stable pour le remplissage automatique…", - "ingredient_scaled_toast": "📦 Ingrédient déduit du garde-manger !", - "finished_added_bring_toast": "🛒 Produit terminé → ajouté à Bring !", - "load_error": "Erreur de chargement", - "favorite": "Ajouter aux favoris", - "unfavorite": "Retirer des favoris", - "adjust_persons": "Personnes" - }, - "shopping": { - "title": "🛒 Liste de courses", - "bring_loading": "Connexion à Bring !...", - "bring_not_configured": "Bring ! n'est pas configuré. Ajoutez votre e-mail et mot de passe dans les paramètres.", - "tab_to_buy": "🛍️ À acheter", - "tab_forecast": "🧠 Prévision", - "total_label": "💰 Total estimé", - "section_to_buy": "🛍️ À acheter", - "suggestions_title": "💡 Suggestions IA", - "suggestions_add": "✅ Ajouter la sélection à Bring !", - "search_prices": "🔍 Rechercher tous les prix", - "suggest_btn": "Suggérer des achats", - "smart_title": "🧠 Prévisions intelligentes", - "smart_empty": "Aucune prévision disponible.
    Ajoutez des produits à votre garde-manger pour recevoir des prévisions intelligentes.", - "smart_filter_all": "Tout", - "smart_filter_critical": "🔴 Urgent", - "smart_filter_high": "🟠 Bientôt", - "smart_filter_medium": "🟡 Planifier", - "smart_filter_low": "🟢 Prévision", - "smart_add": "🛒 Ajouter la sélection à Bring !", - "empty": "Liste de courses vide !\nUtilisez le bouton ci-dessous pour générer des suggestions.", - "already_in_list": "🛒 \"{name}\" est déjà dans la liste de courses", - "already_in_list_short": "ℹ️ Déjà dans la liste de courses", - "add_prompt": "Voulez-vous l'ajouter à la liste de courses ?", - "smart_already": "📊 Les prévisions achats prédisent déjà {name}", - "all_searched": "Tous les produits ont déjà été recherchés. Utilisez 🔄 pour rechercher individuellement.", - "search_complete": "Recherche terminée : {count} produits", - "removed_sufficient": "🧹 {removed} produit(s) avec stock suffisant retiré(s) de la liste", - "suggest_buy": "🛒 Acheter : {qty} {unit}", - "suggest_buy_approx": "🛒 Au minimum : {qty} {unit}", - "suggest_buy_tip": "Quantité suggérée basée sur vos 14 derniers jours de consommation", - "suggest_buy_approx_tip": "Estimation minimale basée sur la consommation (achetez le format le plus proche)", - "bring_badge": "🛒 Déjà sur Bring !", - "add_urgent_toast": "🔴 {n} produit(s) urgent(s) automatiquement ajouté(s) à Bring !", - "migration_done": "✅ {migrated} mis à jour, {skipped} déjà ok", - "added_to_bring": "🛒 {n} produits ajoutés à Bring !", - "added_to_bring_skip": "{n} déjà présents", - "all_on_bring": "Tous les produits étaient déjà sur Bring !", - "freq_high": "📈 Fréquent", - "freq_regular": "📊 Régulier", - "freq_occasional": "📉 Occasionnel", - "out_of_stock": "Rupture de stock", - "scan_toast": "📷 Scanner : {name}", - "empty_category": "Aucun produit dans cette catégorie", - "session_empty": "🛒 Aucun produit encore", - "urgency_critical": "Urgent", - "urgency_high": "Bientôt", - "urgency_medium": "Planifier", - "urgency_low": "Prévision", - "urgency_medium_short": "Moyen", - "urgency_low_short": "Ok", - "tag_urgent": "🔴 Urgent", - "tag_priority": "⭐ Priorité", - "tag_check": "✅ Vérifier", - "smart_already_predicted": "📊 Les prévisions achats prédisent déjà {name}{urgency}.", - "item_removed": "✅ {name} retiré de la liste !", - "urgency_spec_critical": "⚡ Urgent", - "urgency_spec_high": "🟠 Bientôt", - "bring_add_n": "Ajouter {n} à Bring !", - "bring_add_selected": "Ajouter la sélection à Bring !", - "bring_adding": "Ajout en cours...", - "bring_added_one": "1 produit ajouté à Bring !", - "bring_added_many": "{n} produits ajoutés à Bring !", - "bring_skipped": "({n} déjà dans la liste)", - "force_sync": "Forcer la synchronisation Bring !", - "scan_target_label": "Vous cherchez", - "scan_target_found": "Trouvé ! Retirer de la liste", - "bring_add_one": "Ajouter 1 produit à Bring !", - "bring_add_many": "Ajouter {n} produits à Bring !", - "syncing": "Synchronisation…", - "sync_done": "Synchronisation terminée", - "price_searching": "Recherche en cours...", - "search_action": "Rechercher", - "open_action": "Ouvrir", - "not_found": "Non trouvé", - "search_price": "Rechercher le prix", - "tap_to_scan": "Appuyez pour scanner", - "tag_title": "Tag", - "remove_title": "Retirer", - "found_count": "{found}/{total} produits trouvés", - "savings_offers": "· 🏷️ Vous économisez {amount}€ avec les offres", - "searching_progress": "Recherche {current}/{total}...", - "remove_error": "Erreur de suppression", - "btn_fetch_prices": "Trouver les prix", - "price_total_label": "💰 Total estimé :", - "price_loading": "Recherche des prix…", - "price_not_found": "prix n/d", - "suggest_loading": "Analyse en cours...", - "suggest_error": "Erreur de génération des suggestions", - "priority_high": "Élevée", - "priority_medium": "Moyenne", - "priority_low": "Faible", - "smart_last_update": "Mis à jour {time}", - "names_already_updated": "Tous les noms sont déjà à jour" - }, - "ai": { - "title": "🤖 Identification IA", - "capture": "📸 Prendre une photo", - "retake": "🔄 Reprendre", - "hint": "Prenez une photo du produit et l'IA essaiera de l'identifier", - "identifying": "🤖 Identification du produit...", - "no_api_key": "⚠️ Clé API Gemini non configurée.\nAjoutez GEMINI_API_KEY au fichier .env sur le serveur.", - "fields_filled": "✅ Champs remplis par l'IA", - "use_data": "✅ Utiliser les données IA", - "use_data_no_barcode": "✅ Utiliser les données IA (sans code-barres)" - }, - "log": { - "title": "📒 Journal des opérations", - "type_added": "Ajouté", - "type_waste": "Jeté", - "type_used": "Utilisé", - "type_bring": "Ajouté à Bring !", - "undone_badge": "Annulé", - "undo_title": "Annuler cette opération", - "load_error": "Erreur de chargement du journal", - "empty": "Aucune opération enregistrée.", - "undo_action_remove": "suppression de", - "undo_action_restore": "réapprovisionnement de", - "undo_confirm": "Annuler cette opération ?\n→ {action} {name}", - "undo_success": "↩ Opération annulée pour {name}", - "already_undone": "Opération déjà annulée", - "too_old": "Impossible d'annuler des opérations de plus de 24 heures", - "undo_error": "Erreur lors de l'annulation", - "recipe_prefix": "Recette" - }, - "chat": { - "title": "Chef Gemini", - "welcome": "Bonjour ! Je suis votre assistant cuisine", - "welcome_desc": "Demandez-moi de préparer un jus, un snack, un plat rapide… Je connais votre garde-manger, vos appareils et vos préférences !", - "suggestion_snack": "🍿 Snack rapide", - "suggestion_juice": "🥤 Jus/Smoothie", - "suggestion_light": "🥗 Quelque chose de léger", - "suggestion_expiry": "⏰ Utiliser les produits qui expirent", - "clear": "Nouvelle conversation", - "placeholder": "Demandez quelque chose...", - "cleared": "Chat effacé", - "suggestion_snack_text": "Que puis-je préparer comme snack rapide ?", - "suggestion_juice_text": "Faites-moi un jus ou un smoothie avec ce que j'ai", - "suggestion_light_text": "J'ai faim mais je veux quelque chose de léger", - "suggestion_expiry_text": "Qu'est-ce qui va bientôt expirer et comment l'utiliser ?", - "transfer_to_recipes": "Transférer aux recettes", - "transferring": "Transfert en cours...", - "transferred": "Ajouté aux recettes !", - "open_recipe": "Ouvrir la recette", - "quick_recipe_prompt": "Suggérez une recette rapide POUR UNE PERSONNE en utilisant les produits qui expirent en premier ! Ignorez les congélateurs, concentrez-vous sur le réfrigérateur et le garde-manger." - }, - "cooking": { - "close": "Fermer", - "tts_btn": "Lire à voix haute", - "restart": "↺ Recommencer", - "replay": "🔊 Rejouer", - "timer": "⏱️ {time} · Minuterie", - "prev": "◀ Précédent", - "next": "Suivant ▶", - "ingredient_used": "✔️ Déduit", - "ingredient_use_btn": "📦 Utiliser", - "ingredient_deduct_title": "Déduire du garde-manger", - "timer_expired_tts": "Minuterie {label} terminée !", - "timer_warning_tts": "Attention ! {label} : 10 secondes restantes !", - "recipe_done_tts": "Recette terminée ! Bon appétit !", - "expires_chip": "exp. {date}", - "finish": "✅ Terminer", - "step_fallback": "Étape {n}", - "zerowaste_label": "♻️ Déchet", - "zerowaste_tip_title": "Conseil zéro déchet" - }, - "settings": { - "title": "⚙️ Paramètres", - "tab_api": "Clés API", - "tab_bring": "Bring !", - "tab_recipe": "Recettes", - "tab_mealplan": "Plan hebdomadaire", - "tab_appliances": "Appareils", - "tab_spesa": "Courses en ligne", - "tab_camera": "Caméra", - "tab_security": "Sécurité", - "tab_tts": "Voix (TTS)", - "tab_language": "Langue", - "tab_scale": "Balance connectée", - "gemini": { - "title": "🤖 Google Gemini IA", - "hint": "Clé API pour l'identification des produits, les dates de péremption et les recettes.", - "key_label": "Clé API Gemini" - }, - "bring": { - "title": "🛒 Liste de courses Bring !", - "hint": "Identifiants pour l'intégration de la liste de courses Bring !", - "email_label": "📧 E-mail Bring !", - "password_label": "🔒 Mot de passe Bring !" - }, - "price": { - "title": "💰 Estimation des prix (IA)", - "hint": "Afficher le coût estimé par produit dans la liste de courses à l'aide de l'IA.", - "enabled_label": "Activer l'estimation des prix", - "country_label": "🌍 Pays de référence", - "currency_label": "💱 Devise", - "update_label": "🔄 Actualiser les prix tous les", - "update_suffix": "mois" - }, - "recipe": { - "title": "🍳 Préférences de recettes", - "hint": "Configurez les options par défaut pour la génération de recettes.", - "persons_label": "👥 Portions par défaut", - "options_label": "🎯 Options de recette par défaut", - "fast": "⚡ Repas rapide", - "light": "🥗 Repas léger", - "expiry": "⏰ Priorité péremption", - "healthy": "💚 Extra sain", - "opened": "📦 Priorité produits ouverts", - "zerowaste": "♻️ Zéro déchet", - "dietary_label": "🚫 Intolérances / Restrictions", - "dietary_placeholder": "Ex. : sans gluten, sans lactose, végétarien..." - }, - "mealplan": { - "title": "📅 Plan de repas hebdomadaire", - "hint": "Définissez le type de repas pour chaque jour. Il sera utilisé comme guide pour la génération de recettes.", - "enabled": "✅ Activer le plan hebdomadaire", - "legend": "🌤️ = Déjeuner  ·  🌙 = Dîner  ·  Appuyez sur un badge pour le modifier.", - "types_title": "📋 Types disponibles", - "reset_btn": "↺ Restaurer les valeurs par défaut" - }, - "appliances": { - "title": "🔌 Appareils disponibles", - "hint": "Indiquez les appareils que vous possédez. Ils seront pris en compte lors de la génération de recettes.", - "new_placeholder": "Ex. : Machine à pain, Thermomix, Friteuse à air...", - "quick_title": "Ajout rapide :", - "oven": "🔥 Four", - "microwave": "📡 Micro-ondes", - "air_fryer": "🍟 Friteuse à air", - "bread_maker": "🍞 Machine à pain", - "bimby": "🤖 Thermomix/Cookeo", - "mixer": "🌀 Robot pâtissier", - "steamer": "♨️ Cuiseur vapeur", - "pressure_cooker": "🫕 Cocotte-minute", - "toaster": "🍞 Grille-pain", - "blender": "🍹 Mixeur", - "empty": "Aucun appareil ajouté" - }, - "spesa": { - "title": "🛍️ Courses en ligne", - "hint": "Configurez le fournisseur de courses en ligne.", - "provider_label": "🏪 Fournisseur", - "email_label": "📧 E-mail", - "password_label": "🔒 Mot de passe", - "login_btn": "🔐 Connexion", - "ai_prompt_label": "🤖 Prompt IA de sélection de produit", - "ai_prompt_placeholder": "Instructions pour l'IA lors du choix entre plusieurs produits...", - "ai_prompt_hint": "L'IA utilise ce prompt pour choisir le produit le plus approprié parmi les résultats. Laissez vide pour le comportement par défaut.", - "configure_first": "Configurez d'abord les courses en ligne dans les paramètres", - "missing_credentials": "Entrez l'e-mail et le mot de passe", - "login_in_progress": "Connexion en cours...", - "login_error_prefix": "Erreur :", - "login_network_error_prefix": "Erreur réseau :", - "login_success_default": "Connexion réussie !", - "result_name_label": "Nom", - "result_card_label": "Carte", - "result_pickup_label": "Point de retrait", - "result_points_label": "Points fidélité", - "connected_relogin": "✅ Connecté — Se reconnecter", - "connected_as": "Connecté en tant que {name}" - }, - "camera": { - "title": "📷 Caméra", - "hint": "Choisissez la caméra à utiliser pour le scan de code-barres et l'identification IA.", - "device_label": "📸 Caméra par défaut", - "back": "📱 Arrière (par défaut)", - "front": "🤳 Frontale", - "devices_hint": "Si vous avez plusieurs caméras, vous pouvez en sélectionner une dans la liste ci-dessus après avoir accordé les permissions.", - "detect_btn": "🔄 Détecter les caméras" - }, - "security": { - "title": "🔒 Certificat HTTPS", - "hint": "Si le navigateur affiche l'erreur « Votre connexion n'est pas privée » (ERR_CERT_AUTHORITY_INVALID), vous devez installer le certificat CA sur l'appareil.", - "download_btn": "📥 Télécharger le certificat CA", - "token_title": "🔑 Token de paramètres", - "token_label": "Token d'accès", - "token_hint": "Si `SETTINGS_TOKEN` est configuré dans le `.env` du serveur, entrez le token ici avant de sauvegarder les paramètres. Laissez vide si non configuré.", - "token_placeholder": "(vide = pas de protection)", - "token_required_hint": "🔒 Ce serveur nécessite un token pour sauvegarder les paramètres.", - "cert_instructions": "Instructions pour Chrome (Android) :
    1. Téléchargez le certificat ci-dessus
    2. Allez dans Paramètres → Sécurité & Confidentialité → Plus de paramètres de sécurité → Installer depuis le stockage
    3. Sélectionnez le fichier EverShelf_CA.crt téléchargé
    4. Choisissez « CA » et confirmez
    5. Redémarrez Chrome

    Instructions pour Chrome (PC) :
    1. Téléchargez le certificat ci-dessus
    2. Allez dans chrome://settings/certificates
    3. Onglet « Autorités » → Importer → sélectionnez le fichier
    4. Cochez « Approuver ce certificat pour identifier les sites web »
    5. Redémarrez Chrome" - }, - "tts": { - "title": "🔊 Voix & TTS", - "hint": "Configurez la synthèse vocale via une API REST externe. Les étapes de recette et les minuteries expirées seront envoyées à l'endpoint configuré.", - "enabled": "✅ Activer la TTS", - "engine_label": "⚙️ Moteur TTS", - "engine_browser": "🔇 Navigateur (hors ligne, aucune configuration requise)", - "engine_server": "🌐 Serveur externe (Home Assistant, API REST...)", - "voice_label": "🗣️ Voix", - "rate_label": "⚡ Vitesse", - "pitch_label": "🎵 Tonalité", - "url_label": "🌐 URL de l'endpoint", - "method_label": "📡 Méthode HTTP", - "auth_label": "🔐 Authentification", - "auth_bearer": "Bearer Token", - "auth_custom": "En-tête personnalisé", - "auth_none": "Aucune", - "token_label": "🔑 Bearer Token", - "custom_header_name": "📋 Nom de l'en-tête", - "custom_header_value": "📋 Valeur de l'en-tête", - "content_type_label": "📄 Content-Type", - "payload_key_label": "🗝️ Champ texte dans le payload", - "payload_key_hint": "Nom du champ JSON qui contiendra le texte à lire (ex. : message, text).", - "extra_fields_label": "➕ Champs supplémentaires (JSON)", - "extra_fields_placeholder": "{\"entity_id\": \"media_player.salon\"}", - "extra_fields_hint": "Champs supplémentaires à inclure dans le payload, en format JSON. Laissez vide si non nécessaire.", - "test_btn": "🔊 Envoyer une voix de test", - "voices_loading": "Chargement des voix…", - "voice_not_supported": "Voix non supportée par ce navigateur", - "voices_none": "Aucune voix disponible sur cet appareil", - "voices_hint": "Les voix disponibles dépendent du système d'exploitation et du navigateur. Appuyez sur ↺ si la liste ne se charge pas.", - "url_missing": "⚠️ URL de l'endpoint manquante.", - "test_sending": "⏳ Envoi…", - "test_ok": "✅ Réponse {code} — vérifiez que le haut-parleur a parlé.", - "heard_question": "Avez-vous entendu la voix ?", - "heard_yes": "Oui, je l'ai entendu", - "heard_no": "Non, je n'ai rien entendu", - "test_ok_kiosk": "TTS fonctionne.", - "test_fail_steps": "Vérifiez : 1) le volume média n'est pas 0 ; 2) Google Text-to-Speech est installé et mis à jour ; 3) le pack vocal français est téléchargé dans les paramètres TTS Android." - }, - "language": { - "title": "🌐 Langue", - "hint": "Sélectionnez la langue de l'interface.", - "label": "🌐 Langue", - "restart_notice": "La page sera rechargée pour appliquer la nouvelle langue." - }, - "screensaver": { - "label": "Activer l'économiseur d'écran", - "card_title": "🌙 Économiseur d'écran", - "card_hint": "Affiche une horloge avec des informations utiles après 5 minutes d'inactivité. Désactivé par défaut.", - "timeout_1": "1 minute", - "timeout_2": "2 minutes", - "timeout_5": "5 minutes", - "timeout_10": "10 minutes", - "timeout_15": "15 minutes", - "timeout_30": "30 minutes", - "timeout_60": "1 heure", - "start_after": "⏱️ Démarrer après" - }, - "scale": { - "title": "⚖️ Balance connectée", - "hint": "Connectez une balance Bluetooth via la passerelle Android pour lire automatiquement le poids.", - "tab": "Balance connectée", - "enabled": "✅ Activer la balance connectée", - "url_label": "🌐 URL de la passerelle WebSocket", - "url_placeholder": "ws://192.168.1.x:8765", - "url_hint": "URL affichée par l'application Android (même réseau Wi-Fi). Ex. :", - "test_btn": "🔗 Tester la connexion", - "download_btn": "📥 Télécharger la passerelle Android (APK)", - "download_hint": "Application Android qui connecte votre balance BLE et EverShelf.", - "download_sub": "Source : evershelf-scale-gateway/ dans la racine du projet", - "live_weight": "poids en temps réel", - "auto_reconnect": "🔁 Reconnexion : automatique", - "kiosk_title": "📡 Balance BLE intégrée dans le kiosque", - "kiosk_hint": "La balance est directement gérée par la passerelle BLE interne du kiosque. Pour associer un nouvel appareil, utilisez l'assistant de configuration.", - "kiosk_reconfigure": "🔄 Reconfigurer la balance BLE", - "ble_protocols": "

    🔌 Protocoles BLE supportés :

    " - }, - "kiosk": { - "hint": "Transformez une tablette Android en panneau EverShelf permanent avec passerelle BLE intégrée.", - "download_btn": "📥 Télécharger EverShelf Kiosk (APK)", - "download_sub": "Mode kiosque plein écran + passerelle de balance intégrée. Source : evershelf-kiosk/", - "native_title": "Configuration du kiosque", - "native_hint": "URL du serveur, balance BLE, économiseur d'écran et assistant de configuration.", - "native_btn": "Ouvrir la configuration du kiosque", - "native_tap_hint": "Appuyez sur le bouton engrenage en haut à droite", - "native_update_hint": "Mettez à jour l'application kiosque pour utiliser cette fonctionnalité", - "update_title": "Mise à jour du kiosque", - "check_updates_btn": "🔍 Vérifier les mises à jour", - "needs_update": "⚠️ Le kiosque installé ne supporte pas cette fonctionnalité. Mettez à jour l'application kiosque pour l'activer." - }, - "saved": "✅ Configuration enregistrée !", - "saved_local": "✅ Configuration enregistrée localement", - "saved_local_error": "⚠️ Enregistré localement, erreur serveur : {error}", - "theme": { - "title": "🌙 Apparence", - "hint": "Choisissez le thème de l'interface.", - "label": "🌙 Thème", - "off": "☀️ Clair", - "on": "🌙 Sombre", - "auto": "🔄 Automatique (système)" - }, - "zerowaste": { - "card_title": "♻️ Conseils zéro déchet", - "card_hint": "Pendant la cuisson, affichez des conseils pour réutiliser les déchets produits à chaque étape (épluchures, eau de cuisson, etc.). Désactivé par défaut.", - "label": "Afficher les conseils pendant la cuisson" - }, - "backup": { - "tab": "Sauvegarde", - "local_title": "Sauvegarde locale", - "local_hint": "Instantané quotidien de la base de données. Configurez le nombre de jours de rétention.", - "enabled": "Activer la sauvegarde automatique quotidienne", - "retention_days": "Rétention (jours)", - "retention_info": "Les sauvegardes sont conservées pendant", - "backup_now": "Sauvegarder maintenant", - "backing_up": "Sauvegarde en cours…", - "backed_up": "Sauvegarde terminée", - "backup_error": "Erreur de sauvegarde", - "last_backup": "Dernière sauvegarde", - "no_backup_yet": "Aucune sauvegarde créée", - "list_empty": "Aucune sauvegarde disponible", - "restore_btn": "Restaurer", - "restore_confirm": "Restaurer la sauvegarde", - "delete_btn": "Supprimer", - "delete_confirm": "Supprimer la sauvegarde", - "gdrive_title": "Google Drive", - "gdrive_hint": "Sauvegardez automatiquement sur Google Drive via OAuth 2.0. Aucune bibliothèque externe requise.", - "gdrive_enabled": "Activer la sauvegarde Google Drive", - "gdrive_folder_id": "ID du dossier Drive", - "gdrive_folder_id_hint": "Copiez l'ID depuis l'URL du dossier Drive : …/folders/ID", - "gdrive_retention_days": "Rétention Drive (jours, 0=tout garder)", - "gdrive_test": "Tester la connexion", - "gdrive_ok": "Connexion réussie !", - "gdrive_error": "Échec de la connexion", - "gdrive_push_now": "Téléverser sur Drive maintenant", - "gdrive_pushing": "Téléversement en cours…", - "gdrive_pushed": "Téléversé sur Drive", - "gdrive_wizard_hint": "Optionnel : sauvegarde quotidienne automatique sur Google Drive via OAuth 2.0.", - "gdrive_skip": "Passer — configurer plus tard dans Paramètres", - "gdrive_client_id": "Client ID", - "gdrive_client_secret": "Client Secret", - "gdrive_redirect_uri_hint": "Ajoute http://localhost comme URI de redirection autorisé dans la Google Cloud Console. Fonctionne sur n'importe quel serveur, même sans domaine public.", - "gdrive_code_title": "Coller l'URL ou le code d'autorisation", - "gdrive_code_hint": "Après autorisation, le navigateur ouvre http://localhost et peut afficher une erreur de connexion — c'est normal. Copie l'URL dans la barre d'adresse (ex. http://localhost/?code=4%2F0A...) et colle-la ici.", - "gdrive_code_submit": "Confirmer", - "gdrive_code_empty": "Coller d'abord l'URL ou le code d'autorisation", - "gdrive_redirect_uri_label": "URI de redirection (ajouter dans Google Cloud Console) :", - "gdrive_oauth_authorize": "Autoriser avec Google", - "gdrive_oauth_authorized": "Autorisé", - "gdrive_oauth_not_authorized": "Pas encore autorisé", - "gdrive_oauth_window_opened": "Fenêtre ouverte — autorisez et revenez ici", - "gdrive_oauth_how_to": "Configurer OAuth 2.0 (étape par étape)", - "gdrive_oauth_steps": "
  • Allez sur console.cloud.google.com et sélectionnez votre projet
  • Activez l’API Google Drive : API et services → Activer les API → Google Drive API
  • Allez dans API et services → Identifiants → Créer des identifiants → ID client OAuth
  • Type d’application : Application Web ; ajoutez l’URL affichée ci-dessous comme URI de redirection autorisé
  • Copiez le Client ID et le Client Secret dans les champs ci-dessus et enregistrez
  • Cliquez sur Autoriser avec Google : connectez-vous et accordez l’accès
  • La fenêtre se ferme automatiquement une fois terminé et les sauvegardes sont prêtes
  • " - }, - "shopping": { - "tab": "Liste de courses", - "title": "Liste de courses", - "hint": "Configurez la liste de courses intégrée ou connectez Bring!.", - "enable_label": "Activer la liste de courses", - "mode_label": "Fournisseur", - "mode_internal": "Intégré (sans Bring!)", - "mode_bring": "Bring! (application externe)", - "bring_section_title": "Configuration Bring!", - "ai_section_title": "Assistance IA", - "smart_suggestions_label": "Suggestions IA", - "forecast_label": "Prévision des produits bientôt épuisés", - "auto_add_label": "Ajouter automatiquement quand", - "auto_add_suffix": "restant en stock (0 = seulement quand épuisé)" - }, - "ha": { - "tab": "Home Assistant", - "title": "Home Assistant", - "hint": "Connectez EverShelf à Home Assistant pour les automations, les notifications push et les capteurs REST.", - "enabled": "Activer l'intégration Home Assistant", - "connection_title": "Connexion", - "url_label": "URL Home Assistant", - "url_placeholder": "http://192.168.1.50:8123", - "url_hint": "URL de base de votre instance Home Assistant.", - "token_label": "Jeton d'accès longue durée", - "token_hint": "Générez depuis Profil HA → Sécurité → Jetons d'accès longue durée.", - "token_placeholder": "eyJhbGci...", - "token_saved": "Jeton enregistré (masqué pour des raisons de sécurité)", - "test_btn": "Tester la connexion", - "test_ok": "Connecté à {version}", - "test_fail": "Connexion échouée : {error}", - "test_bad_token": "HA accessible mais le jeton est invalide", - "testing": "Test en cours…", - "error_no_url": "Veuillez d'abord saisir l'URL de Home Assistant.", - "tts_title": "TTS sur enceinte connectée", - "tts_hint": "Lisez les étapes de recette sur un media player Home Assistant.", - "tts_entity_label": "Entity ID du lecteur multimédia", - "tts_entity_placeholder": "media_player.salon", - "tts_entity_hint": "Entity ID du lecteur multimédia HA. Disponible dans HA : Outils développeur → États.", - "tts_platform_label": "Plateforme TTS", - "tts_platform_speak": "tts.speak (recommandé)", - "tts_platform_notify": "notify.* (service de notification)", - "tts_apply_btn": "Appliquer le preset HA à l'onglet TTS", - "tts_apply_hint": "Pré-remplit l'onglet TTS avec l'URL et le jeton de Home Assistant.", - "tts_preset_applied": "Preset HA appliqué à l'onglet TTS.", - "webhook_title": "Automations Webhook", - "webhook_hint": "Envoyez des données à Home Assistant lors d'événements dans le garde-manger.", - "webhook_id_label": "ID Webhook", - "webhook_id_placeholder": "evershelf_webhook_abc123", - "webhook_id_hint": "ID du webhook créé dans HA. Copiez depuis : HA → Paramètres → Automations → Créer → Déclencheur Webhook.", - "webhook_events_label": "Notifier pour ces événements", - "event_expiry": "Produits expirant bientôt (quotidien)", - "event_shopping": "Article ajouté à la liste de courses", - "event_stock": "Niveau de stock mis à jour", - "expiry_days_label": "Préavis d'expiration (jours)", - "expiry_days_hint": "Envoyer l'alerte d'expiration N jours avant la date d'expiration.", - "webhook_help": "Dans HA : Paramètres → Automations → Créer → Déclencheur : Webhook → copier l'ID généré.", - "notify_title": "Notifications push", - "notify_hint": "Envoyez des notifications push sur votre téléphone via un service notify de Home Assistant.", - "notify_service_label": "Service notify", - "notify_service_placeholder": "notify.mobile_app_mon_telephone", - "notify_service_hint": "Nom du service notify HA. Laissez vide pour désactiver.", - "sensor_title": "Capteurs REST", - "sensor_hint": "Ajoutez à configuration.yaml pour créer des capteurs EverShelf dans Home Assistant.", - "sensor_copy_btn": "Copier le YAML", - "sensor_copied": "YAML copié dans le presse-papiers !", - "save_btn": "Enregistrer les paramètres HA", - "ha_hint": "Si vous utilisez Home Assistant, utilisez l'onglet Home Assistant pour configurer TTS, webhooks et capteurs." - } - }, - "expiry": { - "today": "AUJOURD'HUI", - "tomorrow": "Demain", - "days": "{days} jours", - "expired_days": "il y a {days}j", - "expired_yesterday": "Hier", - "expired_today": "Aujourd'hui", - "badge_today": "⚠️ Expire aujourd'hui !", - "badge_tomorrow": "⏰ Demain", - "badge_tomorrow_long": "⏰ Expire demain", - "badge_days": "⏰ {n} jours", - "badge_expired_ago": "⚠️ Périmé il y a {n}j", - "badge_expired": "⛔ Périmé !", - "badge_stable": "✅ Stable", - "badge_expiring_short": "⏰ Exp. dans {n}j", - "badge_ok_still": "✅ Encore {n}j", - "badge_expires_red": "🔴 Exp. dans {n}j", - "badge_expires_yellow": "🟡 Exp. dans {n}j", - "badge_expired_bare": "⚠️ Périmé", - "badge_expires_warn": "⚠️ Exp. dans {n}j", - "badge_days_left": "⏳ ~{n}j restants", - "days_approx": "~{n} jours", - "weeks_approx": "~{n} semaines", - "months_approx": "~{n} mois", - "years_approx": "~{n} ans", - "expired_today_long": "Périmé aujourd'hui", - "expired_ago_long": "Périmé il y a {n} jours", - "expired_suffix": "— Périmé !", - "expired_suffix_ok": "— Périmé (encore ok)", - "expired_suffix_warning": "— Périmé (vérifier d'abord)", - "opened_ago_long": "Ouvert il y a {n} jours", - "opened_today_long": "Ouvert aujourd'hui", - "opened_suffix": "— Ouvert depuis trop longtemps !", - "opened_suffix_ok": "— Ouvert (encore ok)", - "opened_suffix_warning": "— Ouvert (vérifier d'abord)", - "days_compact": "{n}j", - "badge_check_soon": "Vérifier prochainement" - }, - "status": { - "ok": "OK", - "check": "Vérifier", - "discard": "Jeter", - "tip_freezer_ok": "Au congélateur : encore sûr (~{n}j de marge)", - "tip_freezer_check": "Au congélateur depuis longtemps, peut avoir perdu en qualité. Consommer rapidement", - "tip_freezer_danger": "Au congélateur trop longtemps, risque de brûlure de congélation et de dégradation", - "tip_highRisk_check": "Périmé récemment, vérifiez l'odeur et l'aspect avant de consommer", - "tip_highRisk_danger": "Produit périssable périmé : jeter pour la sécurité", - "tip_medRisk_check1": "Vérifiez l'aspect et l'odeur avant de consommer", - "tip_medRisk_check2": "Périmé depuis un moment, vérifiez soigneusement avant utilisation", - "tip_medRisk_danger": "Trop longtemps depuis la péremption, mieux vaut jeter", - "tip_lowRisk_ok": "Produit longue conservation, encore sûr à consommer", - "tip_lowRisk_check": "Périmé depuis plus d'un mois, vérifiez l'intégrité de l'emballage", - "tip_lowRisk_danger": "Périmé depuis trop longtemps, mieux vaut ne pas risquer" - }, - "toast": { - "product_saved": "Produit enregistré !", - "product_created": "Produit créé !", - "product_updated": "✅ Produit mis à jour !", - "product_removed": "Produit supprimé", - "updated": "Mis à jour !", - "quantity_confirmed": "✓ Quantité confirmée", - "added_to_inventory": "✅ {name} ajouté !", - "removed_from_list": "✅ {name} retiré de la liste !", - "removed_from_list_short": "Retiré de la liste", - "added_to_shopping": "🛒 Ajouté à la liste de courses !", - "removed_from_shopping": "🛒 Retiré de la liste de courses", - "finished_to_bring": "🛒 Produit terminé → ajouté à Bring !", - "thrown_away": "🗑️ {name} jeté !", - "thrown_away_partial": "🗑️ {qty} {unit} de {name} jeté(s)", - "finished_all": "📤 {name} terminé !", - "product_finished_confirmed": "✅ Supprimé — ajoutez-le à nouveau lors du réapprovisionnement", - "appliance_added": "Appareil ajouté", - "item_added": "{name} ajouté" - }, - "antiwaste": { - "title": "🌱 Rapport anti-gaspi", - "grade_label": "Note", - "you": "Vous", - "avg_label": "Moy.", - "better": "🎉 Vous gaspillez {diff}% de moins que la moyenne {country} !", - "worse": "⚠️ Vous gaspillez plus que la moyenne {country}. Des progrès sont possibles !", - "on_par": "→ Vous êtes dans la moyenne {country}. Vous pouvez faire mieux !", - "saved_money": "~{amount}/mois économisé", - "saved_meals": "~{n} repas sauvés", - "saved_co2": "{n} kg CO₂ évités", - "trend_title": "Tendance (3 derniers mois)", - "months_ago_2": "-60 jours", - "months_ago_1": "-30 jours", - "this_month": "Maintenant", - "country_it": "Moy. italienne", - "country_de": "Moy. allemande", - "country_en": "Moy. américaine", - "source": "Sources : REDUCE, Eurostat, USDA 2021", - "live_on": "Données en direct", - "live_off": "Hors ligne", - "meals": "repas", - "annual_info": "📅 Vous ~{you} kg/an · moy. ~{avg} kg/an", - "badge_rate": "taux de perte", - "badge_saved_money": "économisé vs moy.", - "badge_wasted": "articles perdus", - "badge_better": "moins que la moy." - }, - "error": { - "generic": "Erreur", - "network": "Erreur réseau", - "no_api_key": "Configurez la clé API dans les paramètres", - "loading": "Erreur de chargement du produit", - "not_found": "Produit introuvable", - "not_found_manual": "Produit introuvable. Entrez-le manuellement.", - "search": "Erreur de recherche. Réessayez.", - "search_short": "Erreur de recherche", - "save": "Erreur d'enregistrement", - "connection": "Erreur de connexion", - "camera": "Impossible d'accéder à la caméra", - "bring_add": "Erreur d'ajout à Bring !", - "bring_connection": "Erreur de connexion à Bring !", - "identification": "Erreur d'identification", - "ai_quota": "Quota IA épuisé. Réessayez dans quelques minutes.", - "barcode_empty": "Entrez un code-barres", - "barcode_format": "Le code-barres ne doit contenir que des chiffres (4-14 chiffres)", - "min_chars": "Tapez au moins 2 caractères", - "not_in_inventory": "Produit absent de l'inventaire", - "appliance_exists": "L'appareil existe déjà", - "already_exists": "Existe déjà", - "network_retry": "Erreur de connexion. Réessayez.", - "select_items": "Sélectionnez au moins un produit", - "server_offline": "Connexion au serveur perdue", - "server_restored": "Connexion au serveur rétablie", - "server_retry": "Réessayer", - "unknown": "Erreur inconnue", - "prefix": "Erreur", - "no_inventory_entry": "Aucune entrée d'inventaire trouvée", - "offline_title": "Aucune connexion", - "offline_subtitle": "L'app ne peut pas atteindre le serveur. Vérifiez votre connexion Wi-Fi.", - "offline_checking": "Vérification de la connexion…", - "offline_restored": "Connexion rétablie !", - "offline_continue": "Continuer en mode hors ligne", - "offline_reading_cache": "Lecture depuis le cache local", - "offline_ops_pending": "{n} opérations en attente", - "offline_synced": "{n} opérations synchronisées", - "offline_ai_disabled": "Indisponible hors ligne", - "offline_cache_ready": "Offline — {n} produits en cache" - }, - "confirm_placeholder_search": null, - "confirm": { - "remove_item": "Voulez-vous vraiment supprimer ce produit de l'inventaire ?", - "kiosk_exit": "Quitter le mode kiosque ?", - "cancel": "Annuler", - "proceed": "Confirmer", - "discard_one": "Jeter 1 pièce" - }, - "location": { - "dispensa": "Garde-manger", - "frigo": "Réfrigérateur", - "freezer": "Congélateur" - }, - "edit": { - "title": "Modifier {name}", - "unknown_hint": "Entrez le nom du produit et les informations", - "label_name": "🏷️ Nom du produit", - "choose_location_title": "Quel emplacement ?", - "choose_location_hint": "Choisissez l'emplacement à modifier :", - "confirm_large_qty": "Vous définissez la quantité à {qty} {unit}. Cela semble inhabituellement élevé. Confirmer ?" - }, - "screensaver": { - "recipe_btn": "Recettes", - "scan_btn": "Scanner un produit" - }, - "days": { - "mon": "Lundi", - "tue": "Mardi", - "wed": "Mercredi", - "thu": "Jeudi", - "fri": "Vendredi", - "sat": "Samedi", - "sun": "Dimanche", - "mon_short": "Lun", - "tue_short": "Mar", - "wed_short": "Mer", - "thu_short": "Jeu", - "fri_short": "Ven", - "sat_short": "Sam", - "sun_short": "Dim" - }, - "meal_types": { - "lunch": "Déjeuner", - "dinner": "Dîner", - "colazione": "Petit-déjeuner", - "merenda": "Goûter", - "dolce": "Dessert", - "succo": "Jus de fruits", - "pranzo": "Déjeuner", - "cena": "Dîner" - }, - "scale": { - "status_connected": "Balance connectée", - "status_searching": "Passerelle connectée, attente de la balance…", - "status_disconnected": "Passerelle de balance inaccessible", - "status_error": "Erreur de connexion à la passerelle", - "not_connected": "Passerelle de balance non connectée", - "read_btn": "⚖️ Lire depuis la balance", - "reading_title": "Lecture de la balance", - "place_on_scale": "Posez le produit sur la balance…", - "waiting_stable": "Le poids sera capturé automatiquement une fois la lecture stable.", - "no_url": "Entrez l'URL de la passerelle", - "testing": "⏳ Test de connexion…", - "connected_ok": "Connexion à la passerelle réussie !", - "timeout": "Délai dépassé : pas de réponse de la passerelle", - "error_connect": "Impossible de se connecter à la passerelle", - "tab": "Balance connectée", - "low_weight": "Poids < 10 g · entrez manuellement\n(la lecture automatique nécessite au moins 10 g)", - "density_hint": "(densité {density} g/ml)", - "ml_hint": "(sera converti en ml)", - "weight_detected": "Poids détecté — attendez 10s de stabilité…", - "weight_too_low": "Poids trop faible — attente…", - "stable": "✓ Stable", - "auto_confirm": "✅ {val} {unit} — confirmation automatique dans 5s (appuyez pour annuler)", - "cancelled_replace": "Annulé — replacez l'ingrédient sur la balance pour reprendre" - }, - "prediction": { - "expected_qty": "Attendu : {expected} {unit}", - "actual_qty": "Actuel : {actual} {unit}", - "check_suggestion": "Vérifiez ou pesez la quantité restante" - }, - "date": { - "today": "📅 Aujourd'hui", - "yesterday": "📅 Hier" - }, - "scanner": { - "title_barcode": "🔖 Scanner le code-barres", - "barcode_hint": "Cadrez le code-barres du produit", - "barcode_manual_placeholder": "Ou entrez manuellement...", - "barcode_use_btn": "✅ Utiliser ce code", - "ai_identifying": "🤖 Identification du produit...", - "ai_analyzing": "🤖 Analyse IA en cours...", - "product_label_hint": "Cadrez l'étiquette du produit", - "expiry_label_hint": "Cadrez la date de péremption imprimée sur le produit", - "capture_btn": "📸 Capturer", - "capture_photo_btn": "📸 Prendre une photo", - "retake_btn": "🔄 Reprendre", - "camera_error_hint": "Assurez-vous d'utiliser HTTPS et d'avoir accordé les permissions caméra.
    Vous pouvez entrer le code-barres manuellement ou utiliser l'identification IA.", - "no_barcode": "Pas de code-barres", - "save_new_btn": "🆕 Aucun de ceux-ci — enregistrer comme nouveau" - }, - "lowstock": { - "title": "⚠️ Stock faible !", - "message": "{name} est presque épuisé — il ne reste que {qty}.", - "question": "Voulez-vous l'ajouter à la liste de courses ?", - "yes": "🛒 Oui, ajouter à Bring !", - "no": "Non, pour l'instant ça va" - }, - "move": { - "title": "📦 Déplacer le reste ?", - "question": "Voulez-vous déplacer {thing} de {name} vers un autre emplacement ?", - "question_short": "Voulez-vous déplacer {thing} vers un autre emplacement ?", - "thing_opened": "l'emballage ouvert", - "thing_rest": "le reste", - "stay_btn": "Non, rester dans {location}", - "moved_toast": "📦 Emballage ouvert déplacé vers {location}", - "vacuum_restore": "🫙 Restaurer sous vide", - "vacuum_seal_rest": "🔒 Mettre le reste sous vide" - }, - "nova": { - "1": "Non transformé", - "2": "Ingrédient culinaire", - "3": "Transformé", - "4": "Ultra-transformé" - }, - "meal_plan_types": { - "pasta": "Pâtes", - "riso": "Riz", - "carne": "Viande", - "pesce": "Poisson", - "legumi": "Légumineuses", - "uova": "Œufs", - "formaggio": "Fromage", - "pizza": "Pizza", - "affettati": "Charcuterie", - "verdure": "Légumes", - "zuppa": "Soupe", - "insalata": "Salade", - "pane": "Pain/Sandwich", - "dolce": "Dessert", - "libero": "Libre" - }, - "meal_sub": { - "dolce_torta": "Gâteau", - "dolce_crema": "Crème / Pudding", - "dolce_crumble": "Crumble / Tarte", - "dolce_biscotti": "Biscuits / Pâtisseries", - "dolce_frutta": "Dessert aux fruits", - "succo_dolce": "Sucré / Fruité", - "succo_energizzante": "Énergisant", - "succo_detox": "Détox / Vert", - "succo_rinfrescante": "Rafraîchissant", - "succo_vitaminico": "Vitaminé / Agrumes" - }, - "meal_plan": { - "reset_success": "Plan hebdomadaire réinitialisé", - "not_available": "non disponible dans le garde-manger", - "suggested_by": "suggéré par le plan hebdomadaire" - }, - "nutrition": { - "title": "🥗 Analyse alimentaire", - "score_excellent": "😄 Excellent", - "score_good": "🙂 Bien", - "score_improve": "😬 Améliorable", - "label_health": "🌿 Santé", - "label_variety": "🎨 Variété", - "label_fresh": "❄️ Frais", - "source": "Basé sur {n} produits dans votre garde-manger · EverShelf", - "products_count": "produits", - "today_title": "🥗 Votre garde-manger aujourd'hui", - "products_n": "{n} produits", - "macros_title": "Macronutriments estimés", - "macros_proteins": "Protéines", - "macros_carbs": "Glucides", - "macros_fat": "Lipides", - "macros_fiber": "Fibres", - "macros_source": "Estimation basée sur {n} produits en stock" - }, - "facts": { - "greeting_morning": "Bonjour", - "greeting_afternoon": "Bon après-midi", - "greeting_evening": "Bonsoir", - "pantry_waiting": "{greeting} ! Votre garde-manger vous attend.", - "expired_one": "Vous avez 1 produit périmé dans votre garde-manger. Vérifiez-le !", - "expired_many": "Vous avez {n} produits périmés dans votre garde-manger. Vérifiez-les !", - "expired_list": "Produits périmés : {names}", - "expired_list_more": "et {n} autres", - "freezer_expired_ok": "{name} est périmé, mais étant au congélateur il peut encore être bon ! Vérifiez.", - "freezer_expired_old": "{name} au congélateur est périmé depuis trop longtemps. Mieux vaut le jeter.", - "fridge_expired_one": "Vous avez 1 produit périmé dans le réfrigérateur !", - "fridge_expired_many": "Vous avez {n} produits périmés dans le réfrigérateur !", - "expiring_today": "{name} expire aujourd'hui ! Utilisez-le immédiatement.", - "expiring_tomorrow": "{name} expire demain. Planifiez !", - "expiring_days": "{name} expire dans {days} jours.", - "expiring_many": "Vous avez {n} produits qui expirent bientôt.", - "expiring_this_week": "{n} produits expirent cette semaine. Planifiez vos repas en conséquence !", - "expiring_item_loc": "{name} ({loc}) expire dans {days} {dayslabel}.", - "expiring_this_month": "{n} produits expireront ce mois-ci.", - "shopping_add": "Ajouter à la liste : {names} 🛒", - "shopping_more": "et {n} autres", - "shopping_empty": "Liste de courses vide. Tout est en stock ! ✅", - "in_fridge": "Dans le réfrigérateur : {name}.", - "in_freezer": "Dans le congélateur : {name}. N'oubliez pas !", - "top_category": "Catégorie principale : {icon} {cat} avec {n} produits.", - "cat_meat": "Vous avez {n} produits carnés. 🥩", - "cat_dairy": "Vous avez {n} produits laitiers chez vous. 🥛", - "cat_veggies": "Vous avez {n} types de légumes. Super pour la santé ! 🥬", - "cat_fruit": "Vous avez {n} types de fruits. 🍎", - "cat_drinks": "Vous avez {n} boissons disponibles. 🥤", - "cat_frozen": "Vous avez {n} articles surgelés. ❄️", - "cat_pasta": "Vous avez {n} types de pâtes. 🍝 Et si on faisait une carbonara ?", - "cat_canned": "Vous avez {n} conserves dans le garde-manger. 🥫", - "cat_snacks": "Vous avez {n} snacks. Résistez à la tentation ! 🍪", - "cat_condiments": "Vous avez {n} condiments disponibles. 🧂", - "item_random": "Le saviez-vous ? Vous avez {name} dans {loc}.", - "item_qty": "{name} : vous en avez {qty}.", - "no_expiry_count": "{n} produits n'ont pas de date de péremption.", - "furthest_expiry": "Le produit avec la date de péremption la plus lointaine est {name} : {months} mois.", - "high_qty": "Vous avez un beau stock de {name} : {qty} !", - "low_qty_item": "{name} est presque épuisé. L'ajouter à votre liste de courses ?", - "low_qty_count": "{n} produits sont presque épuisés.", - "morning_bread": "Bonjour ! Vous avez du pain pour le petit-déjeuner. 🍞", - "morning_milk": "Y a-t-il du lait dans le réfrigérateur pour un cappuccino ? ☕🥛", - "morning_fruit": "Bonjour ! Des fruits frais sont un excellent début de journée. 🍎", - "noon_pasta": "C'est l'heure du déjeuner… Et si on faisait un bon bol de pâtes ? 🍝", - "noon_salad": "Une salade fraîche pour le déjeuner ? Vous avez {n} légumes ! 🥗", - "evening_meat": "Pour le dîner, vous pourriez utiliser la viande que vous avez. 🥩", - "evening_fish": "Et si on mangeait du poisson ce soir ? 🐟", - "evening_expiring": "Vous avez {n} produits qui expirent cette semaine — utilisez-les ce soir !", - "night_reminder": "Bonne nuit ! N'oubliez pas d'utiliser demain : {names}.", - "weekly_balance": "Bilan hebdomadaire : +{in} ajoutés, −{out} consommés.", - "weekly_added": "Vous avez ajouté {n} produits cette semaine.", - "weekly_consumed": "Vous avez consommé {n} produits cette semaine. Bravo !", - "tip_freezer": "💡 Les produits surgelés durent bien plus longtemps que la date de péremption.", - "tip_bread": "💡 Le pain congelé garde sa fraîcheur pendant des semaines.", - "tip_fifo": "💡 Pour éviter le gaspillage, utilisez en premier les produits les plus proches de la péremption (FIFO).", - "tip_meat": "💡 La viande au congélateur peut se conserver jusqu'à 6 mois en toute sécurité.", - "tip_no_refreeze": "💡 Ne jamais recongeler un produit décongelé. Cuisinez-le immédiatement !", - "tip_fridge": "💡 Un réfrigérateur bien rangé vous fait gagner du temps et de l'argent.", - "tip_canned": "💡 Les conserves ouvertes doivent aller au réfrigérateur et être consommées en quelques jours.", - "top_brand": "La marque la plus courante dans votre garde-manger est {brand} avec {n} produits.", - "combo_pasta": "Vous avez des pâtes et des condiments : prêt pour un premier plat ! 🍝", - "combo_sandwich": "Pain et viande : un sandwich rapide est toujours une bonne idée ! 🥪", - "combo_balanced": "Légumes et viande : vous avez tout pour un repas équilibré ! 🥗🥩", - "pantry_empty": "Le garde-manger est vide ! Il est temps de faire les courses. 🛒", - "pantry_empty_scan": "Aucun produit enregistré. Scannez quelque chose pour commencer !", - "location_distribution": "Distribution : {parts}", - "day": "jour", - "days": "jours" - }, - "kiosk_session": { - "first_item": "Premier article : {name} !", - "items_two_four": "{n} articles — on démarre 🚀", - "items_five_nine": "{n} articles — bon rythme ! 💪", - "items_ten_twenty": "{n} articles — presque un record 🏆", - "items_twenty_plus": "{n} articles — courses épiques ! 🛒🔥", - "duplicates_one": "1 doublon (même article deux fois)", - "duplicates_many": "{n} doublons (pris plusieurs fois)", - "top_category": "Catégorie principale : {cat} ({count}×)", - "items_fallback": "{n} article{plural} ajouté{plural}" - }, - "kiosk": { - "check_btn": "🔍 Vérifier les mises à jour", - "checking": "⏳ Vérification…", - "error_check": "Erreur lors de la vérification des mises à jour", - "error_start_install": "Erreur au démarrage de l'installation", - "version_installed": "Installé : {v}", - "update_available": "⬆️ Nouvelle version disponible : {latest} (installée : {current})", - "up_to_date": "✅ Vous êtes à jour — version {v}", - "too_old": "⚠️ Le kiosque installé est trop ancien pour la vérification automatique des mises à jour.
    Appuyez sur le bouton ci-dessous pour télécharger et installer la nouvelle version directement.", - "manual_install": "⚠️ Ce kiosque ne supporte pas l'installation automatique.
    Procédure manuelle :
    1. Quittez le kiosque (bouton ✕ en haut à gauche)
    2. Désinstallez l'application EverShelf Kiosk
    3. Téléchargez et installez le nouvel APK depuis GitHub :", - "starting_download": "⏳ Démarrage du téléchargement…", - "install_btn": "⬇️ Installer la mise à jour", - "exit_title": "Quitter le kiosque", - "refresh_title": "Actualiser la page" - }, - "update": { - "new_version": "Nouvelle version", - "btn": "Mettre à jour" - }, + "app": { + "name": "EverShelf", + "loading": "Chargement..." + }, + "nav": { + "title": "EverShelf", + "home": "Accueil", + "inventory": "Garde-manger", + "recipes": "Recettes", + "shopping": "Courses", + "log": "Journal", + "settings": "Paramètres" + }, + "btn": { + "back": "← Retour", + "save": "💾 Enregistrer", + "cancel": "✕ Annuler", + "close": "Fermer", + "add": "✅ Ajouter", + "delete": "Supprimer", + "edit": "✏️ Modifier", + "use": "Utiliser", + "edit_item": "Modifier", + "search": "🔍 Rechercher", + "go": "✅ Valider", + "toggle_password": "👁️ Afficher/Masquer", + "load_more": "Charger plus...", + "save_config": "💾 Enregistrer la configuration", + "save_product": "💾 Enregistrer le produit", + "restart": "↺ Redémarrer", + "reset_default": "↺ Rétablir les valeurs par défaut", + "save_info": "💾 Enregistrer les informations", + "retry": "🔄 Réessayer", + "yes_short": "Oui", + "no_short": "Non" + }, + "form": { + "select_placeholder": "-- Sélectionner --" + }, + "locations": { + "dispensa": "Garde-manger", + "frigo": "Réfrigérateur", + "freezer": "Congélateur", + "altro": "Autre" + }, + "categories": { + "latticini": "Produits laitiers", + "carne": "Viande", + "pesce": "Poisson", + "frutta": "Fruits", + "verdura": "Légumes", + "pasta": "Pâtes & Riz", + "pane": "Pain & Boulangerie", + "surgelati": "Surgelés", + "bevande": "Boissons", + "condimenti": "Condiments", + "snack": "Snacks & Sucreries", + "conserve": "Conserves", + "cereali": "Céréales & Légumineuses", + "igiene": "Hygiène", + "pulizia": "Entretien", + "altro": "Autre", + "select": "-- Sélectionner --" + }, + "units": { + "pz": "pcs", + "conf": "pkg", + "g": "g", + "ml": "ml", + "pieces": "Pièces", + "grams": "Grammes", + "box": "Paquet", + "boxes": "Paquets", + "millilitres": "Millilitres", + "from": "de" + }, + "shopping_sections": { + "frutta_verdura": "Fruits & Légumes", + "carne_pesce": "Viande & Poisson", + "latticini": "Produits laitiers & Frais", + "pane_dolci": "Pain & Pâtisseries", + "pasta": "Pâtes & Céréales", + "conserve": "Conserves & Sauces", + "surgelati": "Surgelés", + "bevande": "Boissons", + "pulizia_igiene": "Nettoyage & Hygiène", + "altro": "Autre" + }, + "dashboard": { + "expired_title": "🚫 Périmé", + "expiring_title": "⏰ Expire bientôt", + "stats_period": "📊 30 derniers jours", + "opened_title": "📦 Produits ouverts", + "review_title": "🔍 À vérifier", + "review_hint": "Quantités inhabituelles. Confirmez si elles sont correctes ou modifiez-les.", + "quick_recipe": "Recette rapide avec les produits qui expirent", + "banner_review_title": "Quantité anormale", + "banner_review_action_ok": "C'est correct", + "banner_review_action_finish": "🗑️ Tout fini", + "banner_review_action_edit": "Corriger", + "banner_review_action_weigh": "Peser", + "banner_review_dismiss": "Ignorer", + "banner_prediction_title": "Consommation à vérifier", + "banner_prediction_hint": "L'estimation de consommation s'adapte aux données récentes : confirmez uniquement si la quantité actuelle est correcte.", + "banner_prediction_action_confirm": "Confirmer {qty} {unit}", + "banner_prediction_action_weigh": "Peser maintenant", + "banner_prediction_action_edit": "Mettre à jour la quantité", + "banner_expired_title": "Produit périmé", + "banner_expired_today": "Périmé aujourd'hui", + "banner_expired_days": "Périmé il y a {days} jours", + "banner_expired_action_use": "Utiliser quand même", + "banner_expired_action_finished": "Je l'ai terminé !", + "banner_expired_action_throw": "Je l'ai jeté", + "banner_expired_action_edit": "Corriger la date", + "banner_anomaly_action_edit": "Corriger l'inventaire", + "banner_anomaly_action_dismiss": "La quantité est correcte", + "banner_no_expiry_title": "Date manquante : {name}", + "banner_no_expiry_detail": "Ce produit n'a pas de date de péremption. Voulez-vous en ajouter une ou confirmer qu'il ne périme pas ?", + "banner_no_expiry_action_set": "Définir une date de péremption", + "banner_no_expiry_action_dismiss": "Ne périme pas ✓", + "banner_no_expiry_toast_dismissed": "Marqué comme « sans date limite »", + "banner_expiring_title": "Expire bientôt", + "banner_expiring_today": "Expire aujourd'hui !", + "banner_expiring_tomorrow": "Expire demain", + "banner_expiring_days": "Expire dans {days} jours", + "banner_expiring_action_use": "Utiliser maintenant", + "banner_finished_title": "terminé ?", + "banner_finished_detail": "J'ai enregistré que {name} a atteint zéro stock. Est-il vraiment épuisé ou en avez-vous encore ?", + "banner_finished_action_yes": "Oui, c'est fini", + "banner_finished_action_no": "Non, j'en ai encore", + "banner_review_unusual_pkg_title": "Taille de paquet inhabituelle", + "banner_review_unusual_pkg_detail": "Vous avez défini un paquet de {qty} {unit} — la taille semble très grande. Vérifiez si c'est correct ou modifiez.", + "banner_review_low_qty_title": "Quantité très faible", + "banner_review_low_qty_detail": "Vous n'avez que {qty} en stock — cela semble très peu, peut-être une erreur de saisie. Confirmez si c'est correct.", + "banner_review_high_qty_title": "Quantité inhabituellement élevée", + "banner_review_high_qty_detail": "Vous avez {qty} en stock — le chiffre semble très élevé. Confirmez si c'est correct ou modifiez.", + "banner_prediction_rate_day": "Moyenne ~{n} {unit}/jour", + "banner_prediction_rate_week": "Moyenne ~{n} {unit}/semaine", + "banner_prediction_days_ago": "Il y a {n} jours vous avez réapprovisionné", + "banner_prediction_more": "estimation précédente : {expected} {unit}{time} ; quantité actuelle : {actual} {unit}.", + "banner_prediction_less": "estimation : {expected} {unit}{time} ; quantité actuelle : {actual} {unit}. Si votre rythme d'utilisation a changé, la prévision se met à jour automatiquement.", + "banner_finished_zero": "L'inventaire indique zéro, mais les mouvements enregistrés suggèrent qu'il ne devrait pas être vide.", + "banner_finished_expected": "D'après les enregistrements vous devriez avoir encore {qty} {unit}.", + "banner_finished_check": "Pouvez-vous vérifier ?", + "banner_anomaly_phantom_title": "vous avez plus de stock que prévu", + "banner_anomaly_phantom_detail": "L'inventaire indique {inv_qty} {unit}, mais selon les enregistrements vous ne devriez avoir que {expected_qty} {unit}. Avez-vous ajouté du stock sans l'enregistrer ?", + "banner_anomaly_untracked_title": "stock non enregistré comme entrée", + "banner_anomaly_untracked_detail": "Vous avez {inv_qty} {unit} en inventaire, mais les sorties enregistrées dépassent les entrées — le stock initial n'a probablement jamais été ajouté comme transaction « entrée ». Vous pouvez corriger la quantité ou saisir les entrées manquantes.", + "banner_anomaly_ghost_title": "vous avez moins de stock que prévu", + "banner_anomaly_ghost_detail": "D'après les opérations enregistrées vous devriez avoir {expected_qty} {unit} de {name}, mais l'inventaire n'en montre que {inv_qty} {unit}. Avez-vous pris du stock sans l'enregistrer ?", + "consumed": "Consommé : {n} ({pct}%)", + "wasted": "Gaspillé : {n} ({pct}%)", + "more_opened": "et {n} autres ouverts...", + "banner_expired_detail": "{when} · il vous reste encore {qty}.", + "banner_opened_detail": "{when} dans {location} · il vous reste encore {qty}.", + "banner_explain_title": "Demander une explication à Gemini", + "banner_explain_btn": "Expliquer", + "banner_analyzing": "🤖 Analyse en cours…" + }, + "inventory": { + "title": "Garde-manger", + "filter_all": "Tout", + "search_placeholder": "🔍 Rechercher un produit...", + "recent_title": "🕐 Utilisés récemment", + "popular_title": "⭐ Les plus utilisés", + "empty": "Aucun produit ici.\nScannez un produit pour l'ajouter !", + "no_items_found": "Aucun article trouvé", + "qty_remainder_suffix": "restant", + "vacuum_badge": "🫙 Sous vide", + "opened_badge": "📭 Ouvert", + "label_expiry": "📅 Péremption", + "label_storage": "🫙 Conservation", + "label_status": "📭 État", + "opened_since": "Ouvert depuis le {date}", + "label_position": "📍 Emplacement", + "label_quantity": "📦 Quantité", + "label_added": "📅 Ajouté", + "empty_text": "Aucun produit ici.
    Scannez un produit pour l'ajouter !", + "empty_db": "Aucun produit dans la base de données.
    Scannez un produit pour commencer !", + "qty_trace": "< 1" + }, + "scan": { + "title": "Scanner", + "mode_shopping": "🛒 Mode courses", + "mode_shopping_end": "✅ Terminer les courses", + "spesa_btn": "🛒 Courses", + "zoom": "Zoom", + "tab_barcode": "Code-barres", + "tab_name": "Nom", + "tab_ai": "IA", + "recents_label": "Récents", + "torch_hint": "Torche", + "torch_on": "Torche activée", + "torch_off": "Torche désactivée", + "torch_unavailable": "Torche non disponible sur cet appareil", + "flip_hint": "Retourner la caméra", + "flip_front": "Caméra frontale", + "flip_back": "Caméra arrière", + "num_ocr_btn": "🔢 Lire les chiffres avec l'IA", + "num_ocr_searching": "Recherche du code-barres avec l'IA...", + "num_ocr_found": "Code trouvé : {code}", + "num_ocr_not_found": "Aucun code-barres trouvé dans l'image", + "barcode_placeholder": "Entrez le code-barres...", + "quick_name_divider": "ou tapez le nom", + "quick_name_placeholder": "Ex. : Pommes, Courgettes, Pain...", + "manual_entry": "✏️ Saisie manuelle", + "ai_identify": "🤖 Identifier avec l'IA", + "hint": "Scannez le code-barres, tapez le nom du produit ou utilisez l'IA pour l'identifier", + "debug_toggle": "🐛 Journal de débogage", + "barcode_acquired": "🔖 Code-barres scanné : {code}", + "scan_barcode": "🔖 Scanner le code-barres", + "create_named": "Créer {name}", + "new_without_barcode": "Nouveau produit sans code-barres", + "status_ready": "Pointez la caméra sur le code-barres", + "status_scanning": "Scan en cours...", + "status_partial": "Lu : {code} — vérification...", + "status_invalid": "Invalide : {code} — nouvel essai", + "status_confirmed": "Confirmé !", + "status_parallel": "Scan combiné actif...", + "ai_fallback_searching": "Identification IA en cours...", + "ai_fallback_found": "Produit identifié par l'IA", + "ai_fallback_not_found": "IA : produit non reconnu" + }, + "action": { + "title": "Que voulez-vous faire ?", + "add_btn": "📥 AJOUTER", + "add_sub": "au garde-manger/réfrigérateur", + "use_btn": "📤 UTILISER / CONSOMMER", + "use_sub": "depuis le garde-manger/réfrigérateur", + "have_title": "📦 Déjà en stock !", + "add_more_sub": "en ajouter", + "use_qty_sub": "combien vous avez utilisé", + "throw_btn": "🗑️ JETER", + "throw_sub": "jeter", + "edit_sub": "péremption, emplacement…", + "create_recipe_btn": "Recette" + }, + "add": { + "title": "Ajouter au garde-manger", + "location_label": "📍 Où le rangez-vous ?", + "quantity_label": "📦 Quantité", + "conf_size_label": "📦 Chaque paquet contient :", + "conf_size_placeholder": "ex. 300", + "vacuum_label": "🫙 Sous vide", + "vacuum_hint": "La date de péremption sera automatiquement prolongée", + "submit": "✅ Ajouter", + "purchase_type_label": "🛒 Ce produit est...", + "new_btn": "🆕 Vient d'être acheté", + "existing_btn": "📦 Je l'avais déjà", + "remaining_label": "📦 Quantité restante", + "remaining_hint": "Approximativement combien en reste-t-il ?", + "remaining_full": "🟢 Plein", + "remaining_half": "🟠 À moitié", + "estimated_expiry": "Date de péremption estimée :", + "suffix_freezer": "(congélateur)", + "suffix_vacuum": "(sous vide)", + "hint_modify": "📝 Vous pouvez modifier la date ou la scanner avec la caméra", + "scan_expiry_title": "📷 Scanner la date de péremption", + "product_added": "✅ {name} ajouté !{qty}", + "suffix_freezer_vacuum": "(congélateur + sous vide)", + "history_badge_tip": "Moyenne de {n} entrées précédentes", + "vacuum_question": "Sous vide ?", + "vacuum_saved": "🔒 Sous vide !" + }, + "use": { + "title": "Utiliser / Consommer", + "location_label": "📍 Depuis où ?", + "quantity_label": "Combien avez-vous utilisé ?", + "change": "modifier", + "partial_hint": "Ou précisez la quantité utilisée :", + "partial_piece_hint": "Avez-vous utilisé seulement une partie ?", + "piece": "pièce", + "one_whole": "1 entier", + "use_all": "🗑️ Tout utilisé / Terminé", + "submit": "📤 Utiliser cette quantité", + "available": "📦 Disponible :", + "opened_badge": "OUVERT", + "not_in_inventory": "⚠️ Produit absent de l'inventaire.", + "expiry_warning": "⚠️ Utilisez en premier celui{loc} qui expire le {date} — {when} !", + "expiry_warning_opened": "⚠️ Celui{loc} est ouvert depuis {when} — utilisez-le en premier !", + "throw_title": "🗑️ Jeter le produit", + "throw_all": "🗑️ Tout jeter ({qty})", + "throw_qty_label": "Quelle quantité jeter ?", + "throw_qty_hint": "ou entrez une quantité :", + "throw_partial_btn": "🗑️ Jeter cette quantité", + "when_expired": "périmé il y a {n} jours", + "when_today": "expire aujourd'hui", + "when_tomorrow": "expire demain", + "when_days": "expire dans {n} jours", + "toast_used": "📤 {qty} de {name} utilisé", + "toast_bring": "🛒 Produit terminé → ajouté à Bring !", + "toast_opened_finished": "🔓 Emballage ouvert de {name} terminé !", + "disambiguation_hint": "Que voulez-vous dire par « tout fini » ?", + "disambiguation_all": "🗑️ Tout finir ({qty})", + "error_exceeds_stock": "⚠️ Vous ne pouvez pas utiliser plus que ce que vous avez disponible !", + "use_all_confirm_title": "✅ Tout terminer", + "use_all_confirm_msg": "Confirmez que vous avez terminé le produit :", + "use_all_confirm_btn": "✅ Oui, terminé", + "throw_all_confirm_title": "🗑️ Tout jeter", + "throw_all_confirm_msg": "Voulez-vous vraiment jeter tout le produit ?", + "throw_all_confirm_btn": "🗑️ Oui, jeter" + }, + "product": { + "title_new": "Nouveau produit", + "title_edit": "Modifier le produit", + "ai_fill": "📷 Prendre une photo et identifier avec l'IA", + "ai_fill_hint": "L'IA remplira automatiquement les champs du produit", + "name_label": "🏷️ Nom du produit *", + "name_placeholder": "Ex. : Lait entier, Pâtes penne...", + "brand_label": "🏢 Marque", + "brand_placeholder": "Ex. : Barilla, Danone, Heinz...", + "category_label": "📂 Catégorie", + "unit_label": "📏 Unité de mesure", + "default_qty_label": "🔢 Quantité par défaut", + "conf_size_label": "📦 Chaque paquet contient :", + "conf_size_placeholder": "ex. 300", + "notes_label": "📝 Notes", + "notes_placeholder": "Ex. : sans lactose, biologique, conserver au réfrigérateur après ouverture...", + "barcode_label": "🔖 Code-barres", + "barcode_placeholder": "Code-barres (si disponible)", + "barcode_hint": "⚠️ Ajoutez le code-barres pour la prochaine fois, il suffira de scanner !", + "submit": "💾 Enregistrer le produit", + "name_required": "Entrez le nom du produit", + "conf_size_required": "Précisez le contenu du paquet", + "expiry_estimated": "Date de péremption estimée :", + "scan_expiry": "Scanner la date de péremption", + "expiry_hint": "📝 Vous pouvez modifier la date ou la scanner avec la caméra", + "add_batch": "📦 + Lot avec une date différente", + "package_info": "📦 Paquet : {info}", + "edit_catalog": "⚙️ Modifier les infos produit (nom, marque, catégorie…)", + "not_recognized": "⚠️ Produit non reconnu", + "edit_info": "✏️ Modifier les informations", + "modify_details": "MODIFIER\npéremption, emplacement…", + "already_in_pantry": "📋 Déjà dans le garde-manger", + "no_barcode": "Pas de code-barres", + "unknown_product": "Produit non reconnu", + "edit_name_brand": "Modifier nom/marque", + "weight_label": "Poids", + "origin_label": "Origine", + "labels_label": "Labels", + "select_variant": "Sélectionnez la variante exacte ou utilisez les données IA :" + }, + "products": { + "title": "📦 Tous les produits", + "search_placeholder": "🔍 Rechercher un produit...", + "empty": "Aucun produit dans la base de données.\nScannez un produit pour commencer !", + "no_category": "Aucun produit dans cette catégorie" + }, + "recipes": { + "title": "🍳 Recettes", + "generate": "✨ Générer une nouvelle recette", + "archive_empty": "Aucune recette enregistrée. Générez votre première recette !", + "dialog_title": "🍳 Recette", + "dialog_desc": "Je vais générer une recette saine en utilisant les ingrédients du garde-manger, en priorisant les produits qui expirent.", + "meal_label": "🕐 Quel repas ?", + "persons_label": "👥 Pour combien de personnes ?", + "meal_type_label": "🎯 Type de repas", + "opt_fast": "⚡ Repas rapide", + "opt_light": "🥗 Appétit léger", + "opt_expiry": "⏰ Prioriser les produits qui expirent", + "opt_healthy": "💚 Extra sain", + "opt_opened": "📦 Prioriser les produits ouverts", + "opt_zero_waste": "♻️ Zéro déchet", + "generate_btn": "✨ Générer la recette", + "loading_msg": "Préparation de votre recette...", + "start_cooking": "👨‍🍳 Mode cuisine", + "regenerate": "🔄 En générer une autre", + "regen_choice_title": "Que veux-tu faire de cette recette ?", + "regen_replace": "🔄 En générer une autre (ignorer celle-ci)", + "regen_save_new": "💾 Sauvegarder dans l'archive et en générer une nouvelle", + "close_btn": "✅ Fermer", + "ingredients_title": "🧾 Ingrédients", + "tools_title": "Matériel nécessaire", + "steps_title": "👨‍🍳 Étapes", + "no_steps": "Aucune étape disponible", + "generate_error": "Erreur de génération", + "persons_short": "pers.", + "use_ingredient_title": "Utiliser l'ingrédient", + "recipe_qty_label": "Recette", + "from_where_label": "Depuis où ?", + "amount_label": "Combien", + "use_amount_btn": "Utiliser cette quantité", + "use_all_btn": "Tout utiliser / Terminé", + "packs_label": "Paquets", + "quantity_in_total": "Quantité en {unit} (total : {total})", + "packs_of_have": "Paquets de {size} (vous en avez {count})", + "scale_wait_stable": "Attendez 10s de poids stable pour le remplissage automatique…", + "ingredient_scaled_toast": "📦 Ingrédient déduit du garde-manger !", + "finished_added_bring_toast": "🛒 Produit terminé → ajouté à Bring !", + "load_error": "Erreur de chargement", + "favorite": "Ajouter aux favoris", + "unfavorite": "Retirer des favoris", + "adjust_persons": "Personnes" + }, + "shopping": { + "title": "🛒 Liste de courses", + "bring_loading": "Connexion à Bring !...", + "bring_not_configured": "Bring ! n'est pas configuré. Ajoutez votre e-mail et mot de passe dans les paramètres.", + "tab_to_buy": "🛍️ À acheter", + "tab_forecast": "🧠 Prévision", + "total_label": "💰 Total estimé", + "section_to_buy": "🛍️ À acheter", + "suggestions_title": "💡 Suggestions IA", + "suggestions_add": "✅ Ajouter la sélection à Bring !", + "search_prices": "🔍 Rechercher tous les prix", + "suggest_btn": "Suggérer des achats", + "smart_title": "🧠 Prévisions intelligentes", + "smart_empty": "Aucune prévision disponible.
    Ajoutez des produits à votre garde-manger pour recevoir des prévisions intelligentes.", + "smart_filter_all": "Tout", + "smart_filter_critical": "🔴 Urgent", + "smart_filter_high": "🟠 Bientôt", + "smart_filter_medium": "🟡 Planifier", + "smart_filter_low": "🟢 Prévision", + "smart_add": "🛒 Ajouter la sélection à Bring !", + "empty": "Liste de courses vide !\nUtilisez le bouton ci-dessous pour générer des suggestions.", + "already_in_list": "🛒 \"{name}\" est déjà dans la liste de courses", + "already_in_list_short": "ℹ️ Déjà dans la liste de courses", + "add_prompt": "Voulez-vous l'ajouter à la liste de courses ?", + "smart_already": "📊 Les prévisions achats prédisent déjà {name}", + "all_searched": "Tous les produits ont déjà été recherchés. Utilisez 🔄 pour rechercher individuellement.", + "search_complete": "Recherche terminée : {count} produits", + "removed_sufficient": "🧹 {removed} produit(s) avec stock suffisant retiré(s) de la liste", + "suggest_buy": "🛒 Acheter : {qty} {unit}", + "suggest_buy_approx": "🛒 Au minimum : {qty} {unit}", + "suggest_buy_tip": "Quantité suggérée basée sur vos 14 derniers jours de consommation", + "suggest_buy_approx_tip": "Estimation minimale basée sur la consommation (achetez le format le plus proche)", + "bring_badge": "🛒 Déjà sur Bring !", + "add_urgent_toast": "🔴 {n} produit(s) urgent(s) automatiquement ajouté(s) à Bring !", + "migration_done": "✅ {migrated} mis à jour, {skipped} déjà ok", + "added_to_bring": "🛒 {n} produits ajoutés à Bring !", + "added_to_bring_skip": "{n} déjà présents", + "all_on_bring": "Tous les produits étaient déjà sur Bring !", + "freq_high": "📈 Fréquent", + "freq_regular": "📊 Régulier", + "freq_occasional": "📉 Occasionnel", + "out_of_stock": "Rupture de stock", + "scan_toast": "📷 Scanner : {name}", + "empty_category": "Aucun produit dans cette catégorie", + "session_empty": "🛒 Aucun produit encore", + "urgency_critical": "Urgent", + "urgency_high": "Bientôt", + "urgency_medium": "Planifier", + "urgency_low": "Prévision", + "urgency_medium_short": "Moyen", + "urgency_low_short": "Ok", + "tag_urgent": "🔴 Urgent", + "tag_priority": "⭐ Priorité", + "tag_check": "✅ Vérifier", + "smart_already_predicted": "📊 Les prévisions achats prédisent déjà {name}{urgency}.", + "item_removed": "✅ {name} retiré de la liste !", + "urgency_spec_critical": "⚡ Urgent", + "urgency_spec_high": "🟠 Bientôt", + "bring_add_n": "Ajouter {n} à Bring !", + "bring_add_selected": "Ajouter la sélection à Bring !", + "bring_adding": "Ajout en cours...", + "bring_added_one": "1 produit ajouté à Bring !", + "bring_added_many": "{n} produits ajoutés à Bring !", + "bring_skipped": "({n} déjà dans la liste)", + "force_sync": "Forcer la synchronisation Bring !", + "scan_target_label": "Vous cherchez", + "scan_target_found": "Trouvé ! Retirer de la liste", + "bring_add_one": "Ajouter 1 produit à Bring !", + "bring_add_many": "Ajouter {n} produits à Bring !", + "syncing": "Synchronisation…", + "sync_done": "Synchronisation terminée", + "price_searching": "Recherche en cours...", + "search_action": "Rechercher", + "open_action": "Ouvrir", + "not_found": "Non trouvé", + "search_price": "Rechercher le prix", + "tap_to_scan": "Appuyez pour scanner", + "tag_title": "Tag", + "remove_title": "Retirer", + "found_count": "{found}/{total} produits trouvés", + "savings_offers": "· 🏷️ Vous économisez {amount}€ avec les offres", + "searching_progress": "Recherche {current}/{total}...", + "remove_error": "Erreur de suppression", + "btn_fetch_prices": "Trouver les prix", + "price_total_label": "💰 Total estimé :", + "price_loading": "Recherche des prix…", + "price_not_found": "prix n/d", + "suggest_loading": "Analyse en cours...", + "suggest_error": "Erreur de génération des suggestions", + "priority_high": "Élevée", + "priority_medium": "Moyenne", + "priority_low": "Faible", + "smart_last_update": "Mis à jour {time}", + "names_already_updated": "Tous les noms sont déjà à jour" + }, + "ai": { + "title": "🤖 Identification IA", + "capture": "📸 Prendre une photo", + "retake": "🔄 Reprendre", + "hint": "Prenez une photo du produit et l'IA essaiera de l'identifier", + "identifying": "🤖 Identification du produit...", + "no_api_key": "⚠️ Clé API Gemini non configurée.\nAjoutez GEMINI_API_KEY au fichier .env sur le serveur.", + "fields_filled": "✅ Champs remplis par l'IA", + "use_data": "✅ Utiliser les données IA", + "use_data_no_barcode": "✅ Utiliser les données IA (sans code-barres)" + }, + "log": { + "title": "📒 Journal des opérations", + "type_added": "Ajouté", + "type_waste": "Jeté", + "type_used": "Utilisé", + "type_bring": "Ajouté à Bring !", + "undone_badge": "Annulé", + "undo_title": "Annuler cette opération", + "load_error": "Erreur de chargement du journal", + "empty": "Aucune opération enregistrée.", + "undo_action_remove": "suppression de", + "undo_action_restore": "réapprovisionnement de", + "undo_confirm": "Annuler cette opération ?\n→ {action} {name}", + "undo_success": "↩ Opération annulée pour {name}", + "already_undone": "Opération déjà annulée", + "too_old": "Impossible d'annuler des opérations de plus de 24 heures", + "undo_error": "Erreur lors de l'annulation", + "recipe_prefix": "Recette" + }, + "chat": { + "title": "Chef Gemini", + "welcome": "Bonjour ! Je suis votre assistant cuisine", + "welcome_desc": "Demandez-moi de préparer un jus, un snack, un plat rapide… Je connais votre garde-manger, vos appareils et vos préférences !", + "suggestion_snack": "🍿 Snack rapide", + "suggestion_juice": "🥤 Jus/Smoothie", + "suggestion_light": "🥗 Quelque chose de léger", + "suggestion_expiry": "⏰ Utiliser les produits qui expirent", + "clear": "Nouvelle conversation", + "placeholder": "Demandez quelque chose...", + "cleared": "Chat effacé", + "suggestion_snack_text": "Que puis-je préparer comme snack rapide ?", + "suggestion_juice_text": "Faites-moi un jus ou un smoothie avec ce que j'ai", + "suggestion_light_text": "J'ai faim mais je veux quelque chose de léger", + "suggestion_expiry_text": "Qu'est-ce qui va bientôt expirer et comment l'utiliser ?", + "transfer_to_recipes": "Transférer aux recettes", + "transferring": "Transfert en cours...", + "transferred": "Ajouté aux recettes !", + "open_recipe": "Ouvrir la recette", + "quick_recipe_prompt": "Suggérez une recette rapide POUR UNE PERSONNE en utilisant les produits qui expirent en premier ! Ignorez les congélateurs, concentrez-vous sur le réfrigérateur et le garde-manger." + }, + "cooking": { + "close": "Fermer", + "tts_btn": "Lire à voix haute", + "restart": "↺ Recommencer", + "replay": "🔊 Rejouer", + "timer": "⏱️ {time} · Minuterie", + "prev": "◀ Précédent", + "next": "Suivant ▶", + "ingredient_used": "✔️ Déduit", + "ingredient_use_btn": "📦 Utiliser", + "ingredient_deduct_title": "Déduire du garde-manger", + "timer_expired_tts": "Minuterie {label} terminée !", + "timer_warning_tts": "Attention ! {label} : 10 secondes restantes !", + "recipe_done_tts": "Recette terminée ! Bon appétit !", + "expires_chip": "exp. {date}", + "finish": "✅ Terminer", + "step_fallback": "Étape {n}", + "zerowaste_label": "♻️ Déchet", + "zerowaste_tip_title": "Conseil zéro déchet" + }, + "settings": { + "title": "⚙️ Paramètres", + "tab_api": "Clés API", + "tab_bring": "Bring !", + "tab_recipe": "Recettes", + "tab_mealplan": "Plan hebdomadaire", + "tab_appliances": "Appareils", + "tab_spesa": "Courses en ligne", + "tab_camera": "Caméra", + "tab_security": "Sécurité", + "tab_tts": "Voix (TTS)", + "tab_language": "Langue", + "tab_scale": "Balance connectée", "gemini": { - "chat_title": "Discussion avec Gemini", - "not_configured": "🤖 Gemini non configuré — définissez GEMINI_API_KEY dans les paramètres" + "title": "🤖 Google Gemini IA", + "hint": "Clé API pour l'identification des produits, les dates de péremption et les recettes.", + "key_label": "Clé API Gemini" + }, + "bring": { + "title": "🛒 Liste de courses Bring !", + "hint": "Identifiants pour l'intégration de la liste de courses Bring !", + "email_label": "📧 E-mail Bring !", + "password_label": "🔒 Mot de passe Bring !" + }, + "price": { + "title": "💰 Estimation des prix (IA)", + "hint": "Afficher le coût estimé par produit dans la liste de courses à l'aide de l'IA.", + "enabled_label": "Activer l'estimation des prix", + "country_label": "🌍 Pays de référence", + "currency_label": "💱 Devise", + "update_label": "🔄 Actualiser les prix tous les", + "update_suffix": "mois" + }, + "recipe": { + "title": "🍳 Préférences de recettes", + "hint": "Configurez les options par défaut pour la génération de recettes.", + "persons_label": "👥 Portions par défaut", + "options_label": "🎯 Options de recette par défaut", + "fast": "⚡ Repas rapide", + "light": "🥗 Repas léger", + "expiry": "⏰ Priorité péremption", + "healthy": "💚 Extra sain", + "opened": "📦 Priorité produits ouverts", + "zerowaste": "♻️ Zéro déchet", + "dietary_label": "🚫 Intolérances / Restrictions", + "dietary_placeholder": "Ex. : sans gluten, sans lactose, végétarien..." + }, + "mealplan": { + "title": "📅 Plan de repas hebdomadaire", + "hint": "Définissez le type de repas pour chaque jour. Il sera utilisé comme guide pour la génération de recettes.", + "enabled": "✅ Activer le plan hebdomadaire", + "legend": "🌤️ = Déjeuner  ·  🌙 = Dîner  ·  Appuyez sur un badge pour le modifier.", + "types_title": "📋 Types disponibles", + "reset_btn": "↺ Restaurer les valeurs par défaut" }, "appliances": { - "empty": "Aucun appareil ajouté" + "title": "🔌 Appareils disponibles", + "hint": "Indiquez les appareils que vous possédez. Ils seront pris en compte lors de la génération de recettes.", + "new_placeholder": "Ex. : Machine à pain, Thermomix, Friteuse à air...", + "quick_title": "Ajout rapide :", + "oven": "🔥 Four", + "microwave": "📡 Micro-ondes", + "air_fryer": "🍟 Friteuse à air", + "bread_maker": "🍞 Machine à pain", + "bimby": "🤖 Thermomix/Cookeo", + "mixer": "🌀 Robot pâtissier", + "steamer": "♨️ Cuiseur vapeur", + "pressure_cooker": "🫕 Cocotte-minute", + "toaster": "🍞 Grille-pain", + "blender": "🍹 Mixeur", + "empty": "Aucun appareil ajouté" }, - "about": { - "title": "À propos", - "version": "Version", - "report_bug": "Signaler un bug", - "report_bug_hint": "Quelque chose ne fonctionne pas ? Envoyez-nous un rapport directement depuis l'application.", - "report_bug_modal_title": "Signaler un bug", - "report_type_bug": "Bug", - "report_type_feature": "Fonctionnalité", - "report_type_question": "Question", - "report_field_title": "Titre", - "report_field_title_ph": "Brève description du problème", - "report_field_desc": "Description", - "report_field_desc_ph": "Décrivez le problème en détail…", - "report_field_steps": "Étapes pour reproduire (optionnel)", - "report_field_steps_ph": "1. Aller à…\n2. Appuyer sur…\n3. Voir l'erreur…", - "report_auto_info": "Joint automatiquement : version {version}, langue {lang}.", - "report_send_btn": "Envoyer le rapport", - "report_bug_sending": "Envoi…", - "report_bug_sent": "Rapport envoyé — merci !", - "report_bug_error": "Impossible d'envoyer le rapport. Vérifiez votre connexion.", - "changelog": "Journal des modifications", - "github": "Dépôt GitHub" + "spesa": { + "title": "🛍️ Courses en ligne", + "hint": "Configurez le fournisseur de courses en ligne.", + "provider_label": "🏪 Fournisseur", + "email_label": "📧 E-mail", + "password_label": "🔒 Mot de passe", + "login_btn": "🔐 Connexion", + "ai_prompt_label": "🤖 Prompt IA de sélection de produit", + "ai_prompt_placeholder": "Instructions pour l'IA lors du choix entre plusieurs produits...", + "ai_prompt_hint": "L'IA utilise ce prompt pour choisir le produit le plus approprié parmi les résultats. Laissez vide pour le comportement par défaut.", + "configure_first": "Configurez d'abord les courses en ligne dans les paramètres", + "missing_credentials": "Entrez l'e-mail et le mot de passe", + "login_in_progress": "Connexion en cours...", + "login_error_prefix": "Erreur :", + "login_network_error_prefix": "Erreur réseau :", + "login_success_default": "Connexion réussie !", + "result_name_label": "Nom", + "result_card_label": "Carte", + "result_pickup_label": "Point de retrait", + "result_points_label": "Points fidélité", + "connected_relogin": "✅ Connecté — Se reconnecter", + "connected_as": "Connecté en tant que {name}" }, - "export": { - "title": "Exporter l'inventaire", - "hint": "Téléchargez l'inventaire actuel en CSV ou ouvrez la version imprimable (PDF).", - "btn_csv": "Télécharger CSV", - "btn_pdf": "PDF / Imprimer", - "btn_title": "Exporter" + "camera": { + "title": "📷 Caméra", + "hint": "Choisissez la caméra à utiliser pour le scan de code-barres et l'identification IA.", + "device_label": "📸 Caméra par défaut", + "back": "📱 Arrière (par défaut)", + "front": "🤳 Frontale", + "devices_hint": "Si vous avez plusieurs caméras, vous pouvez en sélectionner une dans la liste ci-dessus après avoir accordé les permissions.", + "detect_btn": "🔄 Détecter les caméras", + "ai_fallback_label": "Identification visuelle IA (repli 5s)", + "ai_fallback_hint": "Si aucun code-barres n'est lu en 5 secondes, une image est automatiquement envoyée à l'IA pour identifier visuellement le produit. Nécessite Gemini configuré." }, - "startup": { - "connecting": "Connexion au serveur...", - "check_php_memory": "Mémoire PHP", - "check_php_timeout": "Délai PHP", - "check_php_upload": "Upload PHP", - "check_data_dir": "Dossier de données", - "check_rate_limits": "Dossier rate limits", - "check_backups": "Dossier sauvegardes", - "check_write_test": "Test d'écriture disque", - "check_disk_space": "Espace disque", - "check_db_connect": "Connexion base de données", - "check_db_tables": "Tables de la BDD", - "check_db_integrity": "Intégrité BDD", - "check_db_wal": "Mode WAL", - "check_db_size": "Taille de la BDD", - "check_db_rows": "Données inventaire", - "check_env": "Fichier .env", - "check_gemini": "Clé Gemini AI", - "check_bring_creds": "Identifiants Bring!", - "check_bring_token": "Token Bring!", - "check_curl_ssl": "cURL SSL", - "check_internet": "Connexion internet", - "fresh_install": "nouvelle installation", - "warnings_found": "avertissements détectés", - "all_ok": "Système OK", - "critical_error_short": "Erreur critique", - "critical_error": "Erreur critique : l'application ne peut pas démarrer. Vérifiez les logs.", - "error_network": "Impossible de contacter le serveur. Vérifiez votre connexion réseau.", - "retry": "Réessayer", - "syncing_local": "Synchronisation des données locales...", - "sync_done": "Données locales synchronisées" + "security": { + "title": "🔒 Certificat HTTPS", + "hint": "Si le navigateur affiche l'erreur « Votre connexion n'est pas privée » (ERR_CERT_AUTHORITY_INVALID), vous devez installer le certificat CA sur l'appareil.", + "download_btn": "📥 Télécharger le certificat CA", + "token_title": "🔑 Token de paramètres", + "token_label": "Token d'accès", + "token_hint": "Si `SETTINGS_TOKEN` est configuré dans le `.env` du serveur, entrez le token ici avant de sauvegarder les paramètres. Laissez vide si non configuré.", + "token_placeholder": "(vide = pas de protection)", + "token_required_hint": "🔒 Ce serveur nécessite un token pour sauvegarder les paramètres.", + "cert_instructions": "Instructions pour Chrome (Android) :
    1. Téléchargez le certificat ci-dessus
    2. Allez dans Paramètres → Sécurité & Confidentialité → Plus de paramètres de sécurité → Installer depuis le stockage
    3. Sélectionnez le fichier EverShelf_CA.crt téléchargé
    4. Choisissez « CA » et confirmez
    5. Redémarrez Chrome

    Instructions pour Chrome (PC) :
    1. Téléchargez le certificat ci-dessus
    2. Allez dans chrome://settings/certificates
    3. Onglet « Autorités » → Importer → sélectionnez le fichier
    4. Cochez « Approuver ce certificat pour identifier les sites web »
    5. Redémarrez Chrome" }, - "stats_monthly": { - "title": "Statistiques Mensuelles", - "consumed": "produits utilisés", - "trend_up": "+{pct}% vs {prev}", - "trend_down": "-{pct}% vs {prev}", - "trend_same": "même rythme que le mois dernier", - "added": "ajoutés", - "wasted": "gaspillés", - "top_used": "le plus utilisé", - "top_cats": "Catégories principales", - "source": "Historique des transactions · mois en cours" + "tts": { + "title": "🔊 Voix & TTS", + "hint": "Configurez la synthèse vocale via une API REST externe. Les étapes de recette et les minuteries expirées seront envoyées à l'endpoint configuré.", + "enabled": "✅ Activer la TTS", + "engine_label": "⚙️ Moteur TTS", + "engine_browser": "🔇 Navigateur (hors ligne, aucune configuration requise)", + "engine_server": "🌐 Serveur externe (Home Assistant, API REST...)", + "voice_label": "🗣️ Voix", + "rate_label": "⚡ Vitesse", + "pitch_label": "🎵 Tonalité", + "url_label": "🌐 URL de l'endpoint", + "method_label": "📡 Méthode HTTP", + "auth_label": "🔐 Authentification", + "auth_bearer": "Bearer Token", + "auth_custom": "En-tête personnalisé", + "auth_none": "Aucune", + "token_label": "🔑 Bearer Token", + "custom_header_name": "📋 Nom de l'en-tête", + "custom_header_value": "📋 Valeur de l'en-tête", + "content_type_label": "📄 Content-Type", + "payload_key_label": "🗝️ Champ texte dans le payload", + "payload_key_hint": "Nom du champ JSON qui contiendra le texte à lire (ex. : message, text).", + "extra_fields_label": "➕ Champs supplémentaires (JSON)", + "extra_fields_placeholder": "{\"entity_id\": \"media_player.salon\"}", + "extra_fields_hint": "Champs supplémentaires à inclure dans le payload, en format JSON. Laissez vide si non nécessaire.", + "test_btn": "🔊 Envoyer une voix de test", + "voices_loading": "Chargement des voix…", + "voice_not_supported": "Voix non supportée par ce navigateur", + "voices_none": "Aucune voix disponible sur cet appareil", + "voices_hint": "Les voix disponibles dépendent du système d'exploitation et du navigateur. Appuyez sur ↺ si la liste ne se charge pas.", + "url_missing": "⚠️ URL de l'endpoint manquante.", + "test_sending": "⏳ Envoi…", + "test_ok": "✅ Réponse {code} — vérifiez que le haut-parleur a parlé.", + "heard_question": "Avez-vous entendu la voix ?", + "heard_yes": "Oui, je l'ai entendu", + "heard_no": "Non, je n'ai rien entendu", + "test_ok_kiosk": "TTS fonctionne.", + "test_fail_steps": "Vérifiez : 1) le volume média n'est pas 0 ; 2) Google Text-to-Speech est installé et mis à jour ; 3) le pack vocal français est téléchargé dans les paramètres TTS Android." + }, + "language": { + "title": "🌐 Langue", + "hint": "Sélectionnez la langue de l'interface.", + "label": "🌐 Langue", + "restart_notice": "La page sera rechargée pour appliquer la nouvelle langue." + }, + "screensaver": { + "label": "Activer l'économiseur d'écran", + "card_title": "🌙 Économiseur d'écran", + "card_hint": "Affiche une horloge avec des informations utiles après 5 minutes d'inactivité. Désactivé par défaut.", + "timeout_1": "1 minute", + "timeout_2": "2 minutes", + "timeout_5": "5 minutes", + "timeout_10": "10 minutes", + "timeout_15": "15 minutes", + "timeout_30": "30 minutes", + "timeout_60": "1 heure", + "start_after": "⏱️ Démarrer après" + }, + "scale": { + "title": "⚖️ Balance connectée", + "hint": "Connectez une balance Bluetooth via la passerelle Android pour lire automatiquement le poids.", + "tab": "Balance connectée", + "enabled": "✅ Activer la balance connectée", + "url_label": "🌐 URL de la passerelle WebSocket", + "url_placeholder": "ws://192.168.1.x:8765", + "url_hint": "URL affichée par l'application Android (même réseau Wi-Fi). Ex. :", + "test_btn": "🔗 Tester la connexion", + "download_btn": "📥 Télécharger la passerelle Android (APK)", + "download_hint": "Application Android qui connecte votre balance BLE et EverShelf.", + "download_sub": "Source : evershelf-scale-gateway/ dans la racine du projet", + "live_weight": "poids en temps réel", + "auto_reconnect": "🔁 Reconnexion : automatique", + "kiosk_title": "📡 Balance BLE intégrée dans le kiosque", + "kiosk_hint": "La balance est directement gérée par la passerelle BLE interne du kiosque. Pour associer un nouvel appareil, utilisez l'assistant de configuration.", + "kiosk_reconfigure": "🔄 Reconfigurer la balance BLE", + "ble_protocols": "

    🔌 Protocoles BLE supportés :

    " + }, + "kiosk": { + "hint": "Transformez une tablette Android en panneau EverShelf permanent avec passerelle BLE intégrée.", + "download_btn": "📥 Télécharger EverShelf Kiosk (APK)", + "download_sub": "Mode kiosque plein écran + passerelle de balance intégrée. Source : evershelf-kiosk/", + "native_title": "Configuration du kiosque", + "native_hint": "URL du serveur, balance BLE, économiseur d'écran et assistant de configuration.", + "native_btn": "Ouvrir la configuration du kiosque", + "native_tap_hint": "Appuyez sur le bouton engrenage en haut à droite", + "native_update_hint": "Mettez à jour l'application kiosque pour utiliser cette fonctionnalité", + "update_title": "Mise à jour du kiosque", + "check_updates_btn": "🔍 Vérifier les mises à jour", + "needs_update": "⚠️ Le kiosque installé ne supporte pas cette fonctionnalité. Mettez à jour l'application kiosque pour l'activer." + }, + "saved": "✅ Configuration enregistrée !", + "saved_local": "✅ Configuration enregistrée localement", + "saved_local_error": "⚠️ Enregistré localement, erreur serveur : {error}", + "theme": { + "title": "🌙 Apparence", + "hint": "Choisissez le thème de l'interface.", + "label": "🌙 Thème", + "off": "☀️ Clair", + "on": "🌙 Sombre", + "auto": "🔄 Automatique (système)" + }, + "zerowaste": { + "card_title": "♻️ Conseils zéro déchet", + "card_hint": "Pendant la cuisson, affichez des conseils pour réutiliser les déchets produits à chaque étape (épluchures, eau de cuisson, etc.). Désactivé par défaut.", + "label": "Afficher les conseils pendant la cuisson" + }, + "backup": { + "tab": "Sauvegarde", + "local_title": "Sauvegarde locale", + "local_hint": "Instantané quotidien de la base de données. Configurez le nombre de jours de rétention.", + "enabled": "Activer la sauvegarde automatique quotidienne", + "retention_days": "Rétention (jours)", + "retention_info": "Les sauvegardes sont conservées pendant", + "backup_now": "Sauvegarder maintenant", + "backing_up": "Sauvegarde en cours…", + "backed_up": "Sauvegarde terminée", + "backup_error": "Erreur de sauvegarde", + "last_backup": "Dernière sauvegarde", + "no_backup_yet": "Aucune sauvegarde créée", + "list_empty": "Aucune sauvegarde disponible", + "restore_btn": "Restaurer", + "restore_confirm": "Restaurer la sauvegarde", + "delete_btn": "Supprimer", + "delete_confirm": "Supprimer la sauvegarde", + "gdrive_title": "Google Drive", + "gdrive_hint": "Sauvegardez automatiquement sur Google Drive via OAuth 2.0. Aucune bibliothèque externe requise.", + "gdrive_enabled": "Activer la sauvegarde Google Drive", + "gdrive_folder_id": "ID du dossier Drive", + "gdrive_folder_id_hint": "Copiez l'ID depuis l'URL du dossier Drive : …/folders/ID", + "gdrive_retention_days": "Rétention Drive (jours, 0=tout garder)", + "gdrive_test": "Tester la connexion", + "gdrive_ok": "Connexion réussie !", + "gdrive_error": "Échec de la connexion", + "gdrive_push_now": "Téléverser sur Drive maintenant", + "gdrive_pushing": "Téléversement en cours…", + "gdrive_pushed": "Téléversé sur Drive", + "gdrive_wizard_hint": "Optionnel : sauvegarde quotidienne automatique sur Google Drive via OAuth 2.0.", + "gdrive_skip": "Passer — configurer plus tard dans Paramètres", + "gdrive_client_id": "Client ID", + "gdrive_client_secret": "Client Secret", + "gdrive_redirect_uri_hint": "Ajoute http://localhost comme URI de redirection autorisé dans la Google Cloud Console. Fonctionne sur n'importe quel serveur, même sans domaine public.", + "gdrive_code_title": "Coller l'URL ou le code d'autorisation", + "gdrive_code_hint": "Après autorisation, le navigateur ouvre http://localhost et peut afficher une erreur de connexion — c'est normal. Copie l'URL dans la barre d'adresse (ex. http://localhost/?code=4%2F0A...) et colle-la ici.", + "gdrive_code_submit": "Confirmer", + "gdrive_code_empty": "Coller d'abord l'URL ou le code d'autorisation", + "gdrive_redirect_uri_label": "URI de redirection (ajouter dans Google Cloud Console) :", + "gdrive_oauth_authorize": "Autoriser avec Google", + "gdrive_oauth_authorized": "Autorisé", + "gdrive_oauth_not_authorized": "Pas encore autorisé", + "gdrive_oauth_window_opened": "Fenêtre ouverte — autorisez et revenez ici", + "gdrive_oauth_how_to": "Configurer OAuth 2.0 (étape par étape)", + "gdrive_oauth_steps": "
  • Allez sur console.cloud.google.com et sélectionnez votre projet
  • Activez l’API Google Drive : API et services → Activer les API → Google Drive API
  • Allez dans API et services → Identifiants → Créer des identifiants → ID client OAuth
  • Type d’application : Application Web ; ajoutez l’URL affichée ci-dessous comme URI de redirection autorisé
  • Copiez le Client ID et le Client Secret dans les champs ci-dessus et enregistrez
  • Cliquez sur Autoriser avec Google : connectez-vous et accordez l’accès
  • La fenêtre se ferme automatiquement une fois terminé et les sauvegardes sont prêtes
  • " + }, + "shopping": { + "tab": "Liste de courses", + "title": "Liste de courses", + "hint": "Configurez la liste de courses intégrée ou connectez Bring!.", + "enable_label": "Activer la liste de courses", + "mode_label": "Fournisseur", + "mode_internal": "Intégré (sans Bring!)", + "mode_bring": "Bring! (application externe)", + "bring_section_title": "Configuration Bring!", + "ai_section_title": "Assistance IA", + "smart_suggestions_label": "Suggestions IA", + "forecast_label": "Prévision des produits bientôt épuisés", + "auto_add_label": "Ajouter automatiquement quand", + "auto_add_suffix": "restant en stock (0 = seulement quand épuisé)" + }, + "ha": { + "tab": "Home Assistant", + "title": "Home Assistant", + "hint": "Connectez EverShelf à Home Assistant pour les automations, les notifications push et les capteurs REST.", + "enabled": "Activer l'intégration Home Assistant", + "connection_title": "Connexion", + "url_label": "URL Home Assistant", + "url_placeholder": "http://192.168.1.50:8123", + "url_hint": "URL de base de votre instance Home Assistant.", + "token_label": "Jeton d'accès longue durée", + "token_hint": "Générez depuis Profil HA → Sécurité → Jetons d'accès longue durée.", + "token_placeholder": "eyJhbGci...", + "token_saved": "Jeton enregistré (masqué pour des raisons de sécurité)", + "test_btn": "Tester la connexion", + "test_ok": "Connecté à {version}", + "test_fail": "Connexion échouée : {error}", + "test_bad_token": "HA accessible mais le jeton est invalide", + "testing": "Test en cours…", + "error_no_url": "Veuillez d'abord saisir l'URL de Home Assistant.", + "tts_title": "TTS sur enceinte connectée", + "tts_hint": "Lisez les étapes de recette sur un media player Home Assistant.", + "tts_entity_label": "Entity ID du lecteur multimédia", + "tts_entity_placeholder": "media_player.salon", + "tts_entity_hint": "Entity ID du lecteur multimédia HA. Disponible dans HA : Outils développeur → États.", + "tts_platform_label": "Plateforme TTS", + "tts_platform_speak": "tts.speak (recommandé)", + "tts_platform_notify": "notify.* (service de notification)", + "tts_apply_btn": "Appliquer le preset HA à l'onglet TTS", + "tts_apply_hint": "Pré-remplit l'onglet TTS avec l'URL et le jeton de Home Assistant.", + "tts_preset_applied": "Preset HA appliqué à l'onglet TTS.", + "webhook_title": "Automations Webhook", + "webhook_hint": "Envoyez des données à Home Assistant lors d'événements dans le garde-manger.", + "webhook_id_label": "ID Webhook", + "webhook_id_placeholder": "evershelf_webhook_abc123", + "webhook_id_hint": "ID du webhook créé dans HA. Copiez depuis : HA → Paramètres → Automations → Créer → Déclencheur Webhook.", + "webhook_events_label": "Notifier pour ces événements", + "event_expiry": "Produits expirant bientôt (quotidien)", + "event_shopping": "Article ajouté à la liste de courses", + "event_stock": "Niveau de stock mis à jour", + "expiry_days_label": "Préavis d'expiration (jours)", + "expiry_days_hint": "Envoyer l'alerte d'expiration N jours avant la date d'expiration.", + "webhook_help": "Dans HA : Paramètres → Automations → Créer → Déclencheur : Webhook → copier l'ID généré.", + "notify_title": "Notifications push", + "notify_hint": "Envoyez des notifications push sur votre téléphone via un service notify de Home Assistant.", + "notify_service_label": "Service notify", + "notify_service_placeholder": "notify.mobile_app_mon_telephone", + "notify_service_hint": "Nom du service notify HA. Laissez vide pour désactiver.", + "sensor_title": "Capteurs REST", + "sensor_hint": "Ajoutez à configuration.yaml pour créer des capteurs EverShelf dans Home Assistant.", + "sensor_copy_btn": "Copier le YAML", + "sensor_copied": "YAML copié dans le presse-papiers !", + "save_btn": "Enregistrer les paramètres HA", + "ha_hint": "Si vous utilisez Home Assistant, utilisez l'onglet Home Assistant pour configurer TTS, webhooks et capteurs." } + }, + "expiry": { + "today": "AUJOURD'HUI", + "tomorrow": "Demain", + "days": "{days} jours", + "expired_days": "il y a {days}j", + "expired_yesterday": "Hier", + "expired_today": "Aujourd'hui", + "badge_today": "⚠️ Expire aujourd'hui !", + "badge_tomorrow": "⏰ Demain", + "badge_tomorrow_long": "⏰ Expire demain", + "badge_days": "⏰ {n} jours", + "badge_expired_ago": "⚠️ Périmé il y a {n}j", + "badge_expired": "⛔ Périmé !", + "badge_stable": "✅ Stable", + "badge_expiring_short": "⏰ Exp. dans {n}j", + "badge_ok_still": "✅ Encore {n}j", + "badge_expires_red": "🔴 Exp. dans {n}j", + "badge_expires_yellow": "🟡 Exp. dans {n}j", + "badge_expired_bare": "⚠️ Périmé", + "badge_expires_warn": "⚠️ Exp. dans {n}j", + "badge_days_left": "⏳ ~{n}j restants", + "days_approx": "~{n} jours", + "weeks_approx": "~{n} semaines", + "months_approx": "~{n} mois", + "years_approx": "~{n} ans", + "expired_today_long": "Périmé aujourd'hui", + "expired_ago_long": "Périmé il y a {n} jours", + "expired_suffix": "— Périmé !", + "expired_suffix_ok": "— Périmé (encore ok)", + "expired_suffix_warning": "— Périmé (vérifier d'abord)", + "opened_ago_long": "Ouvert il y a {n} jours", + "opened_today_long": "Ouvert aujourd'hui", + "opened_suffix": "— Ouvert depuis trop longtemps !", + "opened_suffix_ok": "— Ouvert (encore ok)", + "opened_suffix_warning": "— Ouvert (vérifier d'abord)", + "days_compact": "{n}j", + "badge_check_soon": "Vérifier prochainement" + }, + "status": { + "ok": "OK", + "check": "Vérifier", + "discard": "Jeter", + "tip_freezer_ok": "Au congélateur : encore sûr (~{n}j de marge)", + "tip_freezer_check": "Au congélateur depuis longtemps, peut avoir perdu en qualité. Consommer rapidement", + "tip_freezer_danger": "Au congélateur trop longtemps, risque de brûlure de congélation et de dégradation", + "tip_highRisk_check": "Périmé récemment, vérifiez l'odeur et l'aspect avant de consommer", + "tip_highRisk_danger": "Produit périssable périmé : jeter pour la sécurité", + "tip_medRisk_check1": "Vérifiez l'aspect et l'odeur avant de consommer", + "tip_medRisk_check2": "Périmé depuis un moment, vérifiez soigneusement avant utilisation", + "tip_medRisk_danger": "Trop longtemps depuis la péremption, mieux vaut jeter", + "tip_lowRisk_ok": "Produit longue conservation, encore sûr à consommer", + "tip_lowRisk_check": "Périmé depuis plus d'un mois, vérifiez l'intégrité de l'emballage", + "tip_lowRisk_danger": "Périmé depuis trop longtemps, mieux vaut ne pas risquer" + }, + "toast": { + "product_saved": "Produit enregistré !", + "product_created": "Produit créé !", + "product_updated": "✅ Produit mis à jour !", + "product_removed": "Produit supprimé", + "updated": "Mis à jour !", + "quantity_confirmed": "✓ Quantité confirmée", + "added_to_inventory": "✅ {name} ajouté !", + "removed_from_list": "✅ {name} retiré de la liste !", + "removed_from_list_short": "Retiré de la liste", + "added_to_shopping": "🛒 Ajouté à la liste de courses !", + "removed_from_shopping": "🛒 Retiré de la liste de courses", + "finished_to_bring": "🛒 Produit terminé → ajouté à Bring !", + "thrown_away": "🗑️ {name} jeté !", + "thrown_away_partial": "🗑️ {qty} {unit} de {name} jeté(s)", + "finished_all": "📤 {name} terminé !", + "product_finished_confirmed": "✅ Supprimé — ajoutez-le à nouveau lors du réapprovisionnement", + "appliance_added": "Appareil ajouté", + "item_added": "{name} ajouté" + }, + "antiwaste": { + "title": "🌱 Rapport anti-gaspi", + "grade_label": "Note", + "you": "Vous", + "avg_label": "Moy.", + "better": "🎉 Vous gaspillez {diff}% de moins que la moyenne {country} !", + "worse": "⚠️ Vous gaspillez plus que la moyenne {country}. Des progrès sont possibles !", + "on_par": "→ Vous êtes dans la moyenne {country}. Vous pouvez faire mieux !", + "saved_money": "~{amount}/mois économisé", + "saved_meals": "~{n} repas sauvés", + "saved_co2": "{n} kg CO₂ évités", + "trend_title": "Tendance (3 derniers mois)", + "months_ago_2": "-60 jours", + "months_ago_1": "-30 jours", + "this_month": "Maintenant", + "country_it": "Moy. italienne", + "country_de": "Moy. allemande", + "country_en": "Moy. américaine", + "source": "Sources : REDUCE, Eurostat, USDA 2021", + "live_on": "Données en direct", + "live_off": "Hors ligne", + "meals": "repas", + "annual_info": "📅 Vous ~{you} kg/an · moy. ~{avg} kg/an", + "badge_rate": "taux de perte", + "badge_saved_money": "économisé vs moy.", + "badge_wasted": "articles perdus", + "badge_better": "moins que la moy." + }, + "error": { + "generic": "Erreur", + "network": "Erreur réseau", + "no_api_key": "Configurez la clé API dans les paramètres", + "loading": "Erreur de chargement du produit", + "not_found": "Produit introuvable", + "not_found_manual": "Produit introuvable. Entrez-le manuellement.", + "search": "Erreur de recherche. Réessayez.", + "search_short": "Erreur de recherche", + "save": "Erreur d'enregistrement", + "connection": "Erreur de connexion", + "camera": "Impossible d'accéder à la caméra", + "bring_add": "Erreur d'ajout à Bring !", + "bring_connection": "Erreur de connexion à Bring !", + "identification": "Erreur d'identification", + "ai_quota": "Quota IA épuisé. Réessayez dans quelques minutes.", + "barcode_empty": "Entrez un code-barres", + "barcode_format": "Le code-barres ne doit contenir que des chiffres (4-14 chiffres)", + "min_chars": "Tapez au moins 2 caractères", + "not_in_inventory": "Produit absent de l'inventaire", + "appliance_exists": "L'appareil existe déjà", + "already_exists": "Existe déjà", + "network_retry": "Erreur de connexion. Réessayez.", + "select_items": "Sélectionnez au moins un produit", + "server_offline": "Connexion au serveur perdue", + "server_restored": "Connexion au serveur rétablie", + "server_retry": "Réessayer", + "unknown": "Erreur inconnue", + "prefix": "Erreur", + "no_inventory_entry": "Aucune entrée d'inventaire trouvée", + "offline_title": "Aucune connexion", + "offline_subtitle": "L'app ne peut pas atteindre le serveur. Vérifiez votre connexion Wi-Fi.", + "offline_checking": "Vérification de la connexion…", + "offline_restored": "Connexion rétablie !", + "offline_continue": "Continuer en mode hors ligne", + "offline_reading_cache": "Lecture depuis le cache local", + "offline_ops_pending": "{n} opérations en attente", + "offline_synced": "{n} opérations synchronisées", + "offline_ai_disabled": "Indisponible hors ligne", + "offline_cache_ready": "Offline — {n} produits en cache" + }, + "confirm_placeholder_search": null, + "confirm": { + "remove_item": "Voulez-vous vraiment supprimer ce produit de l'inventaire ?", + "kiosk_exit": "Quitter le mode kiosque ?", + "cancel": "Annuler", + "proceed": "Confirmer", + "discard_one": "Jeter 1 pièce" + }, + "location": { + "dispensa": "Garde-manger", + "frigo": "Réfrigérateur", + "freezer": "Congélateur" + }, + "edit": { + "title": "Modifier {name}", + "unknown_hint": "Entrez le nom du produit et les informations", + "label_name": "🏷️ Nom du produit", + "choose_location_title": "Quel emplacement ?", + "choose_location_hint": "Choisissez l'emplacement à modifier :", + "confirm_large_qty": "Vous définissez la quantité à {qty} {unit}. Cela semble inhabituellement élevé. Confirmer ?" + }, + "screensaver": { + "recipe_btn": "Recettes", + "scan_btn": "Scanner un produit" + }, + "days": { + "mon": "Lundi", + "tue": "Mardi", + "wed": "Mercredi", + "thu": "Jeudi", + "fri": "Vendredi", + "sat": "Samedi", + "sun": "Dimanche", + "mon_short": "Lun", + "tue_short": "Mar", + "wed_short": "Mer", + "thu_short": "Jeu", + "fri_short": "Ven", + "sat_short": "Sam", + "sun_short": "Dim" + }, + "meal_types": { + "lunch": "Déjeuner", + "dinner": "Dîner", + "colazione": "Petit-déjeuner", + "merenda": "Goûter", + "dolce": "Dessert", + "succo": "Jus de fruits", + "pranzo": "Déjeuner", + "cena": "Dîner" + }, + "scale": { + "status_connected": "Balance connectée", + "status_searching": "Passerelle connectée, attente de la balance…", + "status_disconnected": "Passerelle de balance inaccessible", + "status_error": "Erreur de connexion à la passerelle", + "not_connected": "Passerelle de balance non connectée", + "read_btn": "⚖️ Lire depuis la balance", + "reading_title": "Lecture de la balance", + "place_on_scale": "Posez le produit sur la balance…", + "waiting_stable": "Le poids sera capturé automatiquement une fois la lecture stable.", + "no_url": "Entrez l'URL de la passerelle", + "testing": "⏳ Test de connexion…", + "connected_ok": "Connexion à la passerelle réussie !", + "timeout": "Délai dépassé : pas de réponse de la passerelle", + "error_connect": "Impossible de se connecter à la passerelle", + "tab": "Balance connectée", + "low_weight": "Poids < 10 g · entrez manuellement\n(la lecture automatique nécessite au moins 10 g)", + "density_hint": "(densité {density} g/ml)", + "ml_hint": "(sera converti en ml)", + "weight_detected": "Poids détecté — attendez 10s de stabilité…", + "weight_too_low": "Poids trop faible — attente…", + "stable": "✓ Stable", + "auto_confirm": "✅ {val} {unit} — confirmation automatique dans 5s (appuyez pour annuler)", + "cancelled_replace": "Annulé — replacez l'ingrédient sur la balance pour reprendre" + }, + "prediction": { + "expected_qty": "Attendu : {expected} {unit}", + "actual_qty": "Actuel : {actual} {unit}", + "check_suggestion": "Vérifiez ou pesez la quantité restante" + }, + "date": { + "today": "📅 Aujourd'hui", + "yesterday": "📅 Hier" + }, + "scanner": { + "title_barcode": "🔖 Scanner le code-barres", + "barcode_hint": "Cadrez le code-barres du produit", + "barcode_manual_placeholder": "Ou entrez manuellement...", + "barcode_use_btn": "✅ Utiliser ce code", + "ai_identifying": "🤖 Identification du produit...", + "ai_analyzing": "🤖 Analyse IA en cours...", + "product_label_hint": "Cadrez l'étiquette du produit", + "expiry_label_hint": "Cadrez la date de péremption imprimée sur le produit", + "capture_btn": "📸 Capturer", + "capture_photo_btn": "📸 Prendre une photo", + "retake_btn": "🔄 Reprendre", + "camera_error_hint": "Assurez-vous d'utiliser HTTPS et d'avoir accordé les permissions caméra.
    Vous pouvez entrer le code-barres manuellement ou utiliser l'identification IA.", + "no_barcode": "Pas de code-barres", + "save_new_btn": "🆕 Aucun de ceux-ci — enregistrer comme nouveau" + }, + "lowstock": { + "title": "⚠️ Stock faible !", + "message": "{name} est presque épuisé — il ne reste que {qty}.", + "question": "Voulez-vous l'ajouter à la liste de courses ?", + "yes": "🛒 Oui, ajouter à Bring !", + "no": "Non, pour l'instant ça va" + }, + "move": { + "title": "📦 Déplacer le reste ?", + "question": "Voulez-vous déplacer {thing} de {name} vers un autre emplacement ?", + "question_short": "Voulez-vous déplacer {thing} vers un autre emplacement ?", + "thing_opened": "l'emballage ouvert", + "thing_rest": "le reste", + "stay_btn": "Non, rester dans {location}", + "moved_toast": "📦 Emballage ouvert déplacé vers {location}", + "vacuum_restore": "🫙 Restaurer sous vide", + "vacuum_seal_rest": "🔒 Mettre le reste sous vide" + }, + "nova": { + "1": "Non transformé", + "2": "Ingrédient culinaire", + "3": "Transformé", + "4": "Ultra-transformé" + }, + "meal_plan_types": { + "pasta": "Pâtes", + "riso": "Riz", + "carne": "Viande", + "pesce": "Poisson", + "legumi": "Légumineuses", + "uova": "Œufs", + "formaggio": "Fromage", + "pizza": "Pizza", + "affettati": "Charcuterie", + "verdure": "Légumes", + "zuppa": "Soupe", + "insalata": "Salade", + "pane": "Pain/Sandwich", + "dolce": "Dessert", + "libero": "Libre" + }, + "meal_sub": { + "dolce_torta": "Gâteau", + "dolce_crema": "Crème / Pudding", + "dolce_crumble": "Crumble / Tarte", + "dolce_biscotti": "Biscuits / Pâtisseries", + "dolce_frutta": "Dessert aux fruits", + "succo_dolce": "Sucré / Fruité", + "succo_energizzante": "Énergisant", + "succo_detox": "Détox / Vert", + "succo_rinfrescante": "Rafraîchissant", + "succo_vitaminico": "Vitaminé / Agrumes" + }, + "meal_plan": { + "reset_success": "Plan hebdomadaire réinitialisé", + "not_available": "non disponible dans le garde-manger", + "suggested_by": "suggéré par le plan hebdomadaire" + }, + "nutrition": { + "title": "🥗 Analyse alimentaire", + "score_excellent": "😄 Excellent", + "score_good": "🙂 Bien", + "score_improve": "😬 Améliorable", + "label_health": "🌿 Santé", + "label_variety": "🎨 Variété", + "label_fresh": "❄️ Frais", + "source": "Basé sur {n} produits dans votre garde-manger · EverShelf", + "products_count": "produits", + "today_title": "🥗 Votre garde-manger aujourd'hui", + "products_n": "{n} produits", + "macros_title": "Macronutriments estimés", + "macros_proteins": "Protéines", + "macros_carbs": "Glucides", + "macros_fat": "Lipides", + "macros_fiber": "Fibres", + "macros_source": "Estimation basée sur {n} produits en stock" + }, + "facts": { + "greeting_morning": "Bonjour", + "greeting_afternoon": "Bon après-midi", + "greeting_evening": "Bonsoir", + "pantry_waiting": "{greeting} ! Votre garde-manger vous attend.", + "expired_one": "Vous avez 1 produit périmé dans votre garde-manger. Vérifiez-le !", + "expired_many": "Vous avez {n} produits périmés dans votre garde-manger. Vérifiez-les !", + "expired_list": "Produits périmés : {names}", + "expired_list_more": "et {n} autres", + "freezer_expired_ok": "{name} est périmé, mais étant au congélateur il peut encore être bon ! Vérifiez.", + "freezer_expired_old": "{name} au congélateur est périmé depuis trop longtemps. Mieux vaut le jeter.", + "fridge_expired_one": "Vous avez 1 produit périmé dans le réfrigérateur !", + "fridge_expired_many": "Vous avez {n} produits périmés dans le réfrigérateur !", + "expiring_today": "{name} expire aujourd'hui ! Utilisez-le immédiatement.", + "expiring_tomorrow": "{name} expire demain. Planifiez !", + "expiring_days": "{name} expire dans {days} jours.", + "expiring_many": "Vous avez {n} produits qui expirent bientôt.", + "expiring_this_week": "{n} produits expirent cette semaine. Planifiez vos repas en conséquence !", + "expiring_item_loc": "{name} ({loc}) expire dans {days} {dayslabel}.", + "expiring_this_month": "{n} produits expireront ce mois-ci.", + "shopping_add": "Ajouter à la liste : {names} 🛒", + "shopping_more": "et {n} autres", + "shopping_empty": "Liste de courses vide. Tout est en stock ! ✅", + "in_fridge": "Dans le réfrigérateur : {name}.", + "in_freezer": "Dans le congélateur : {name}. N'oubliez pas !", + "top_category": "Catégorie principale : {icon} {cat} avec {n} produits.", + "cat_meat": "Vous avez {n} produits carnés. 🥩", + "cat_dairy": "Vous avez {n} produits laitiers chez vous. 🥛", + "cat_veggies": "Vous avez {n} types de légumes. Super pour la santé ! 🥬", + "cat_fruit": "Vous avez {n} types de fruits. 🍎", + "cat_drinks": "Vous avez {n} boissons disponibles. 🥤", + "cat_frozen": "Vous avez {n} articles surgelés. ❄️", + "cat_pasta": "Vous avez {n} types de pâtes. 🍝 Et si on faisait une carbonara ?", + "cat_canned": "Vous avez {n} conserves dans le garde-manger. 🥫", + "cat_snacks": "Vous avez {n} snacks. Résistez à la tentation ! 🍪", + "cat_condiments": "Vous avez {n} condiments disponibles. 🧂", + "item_random": "Le saviez-vous ? Vous avez {name} dans {loc}.", + "item_qty": "{name} : vous en avez {qty}.", + "no_expiry_count": "{n} produits n'ont pas de date de péremption.", + "furthest_expiry": "Le produit avec la date de péremption la plus lointaine est {name} : {months} mois.", + "high_qty": "Vous avez un beau stock de {name} : {qty} !", + "low_qty_item": "{name} est presque épuisé. L'ajouter à votre liste de courses ?", + "low_qty_count": "{n} produits sont presque épuisés.", + "morning_bread": "Bonjour ! Vous avez du pain pour le petit-déjeuner. 🍞", + "morning_milk": "Y a-t-il du lait dans le réfrigérateur pour un cappuccino ? ☕🥛", + "morning_fruit": "Bonjour ! Des fruits frais sont un excellent début de journée. 🍎", + "noon_pasta": "C'est l'heure du déjeuner… Et si on faisait un bon bol de pâtes ? 🍝", + "noon_salad": "Une salade fraîche pour le déjeuner ? Vous avez {n} légumes ! 🥗", + "evening_meat": "Pour le dîner, vous pourriez utiliser la viande que vous avez. 🥩", + "evening_fish": "Et si on mangeait du poisson ce soir ? 🐟", + "evening_expiring": "Vous avez {n} produits qui expirent cette semaine — utilisez-les ce soir !", + "night_reminder": "Bonne nuit ! N'oubliez pas d'utiliser demain : {names}.", + "weekly_balance": "Bilan hebdomadaire : +{in} ajoutés, −{out} consommés.", + "weekly_added": "Vous avez ajouté {n} produits cette semaine.", + "weekly_consumed": "Vous avez consommé {n} produits cette semaine. Bravo !", + "tip_freezer": "💡 Les produits surgelés durent bien plus longtemps que la date de péremption.", + "tip_bread": "💡 Le pain congelé garde sa fraîcheur pendant des semaines.", + "tip_fifo": "💡 Pour éviter le gaspillage, utilisez en premier les produits les plus proches de la péremption (FIFO).", + "tip_meat": "💡 La viande au congélateur peut se conserver jusqu'à 6 mois en toute sécurité.", + "tip_no_refreeze": "💡 Ne jamais recongeler un produit décongelé. Cuisinez-le immédiatement !", + "tip_fridge": "💡 Un réfrigérateur bien rangé vous fait gagner du temps et de l'argent.", + "tip_canned": "💡 Les conserves ouvertes doivent aller au réfrigérateur et être consommées en quelques jours.", + "top_brand": "La marque la plus courante dans votre garde-manger est {brand} avec {n} produits.", + "combo_pasta": "Vous avez des pâtes et des condiments : prêt pour un premier plat ! 🍝", + "combo_sandwich": "Pain et viande : un sandwich rapide est toujours une bonne idée ! 🥪", + "combo_balanced": "Légumes et viande : vous avez tout pour un repas équilibré ! 🥗🥩", + "pantry_empty": "Le garde-manger est vide ! Il est temps de faire les courses. 🛒", + "pantry_empty_scan": "Aucun produit enregistré. Scannez quelque chose pour commencer !", + "location_distribution": "Distribution : {parts}", + "day": "jour", + "days": "jours" + }, + "kiosk_session": { + "first_item": "Premier article : {name} !", + "items_two_four": "{n} articles — on démarre 🚀", + "items_five_nine": "{n} articles — bon rythme ! 💪", + "items_ten_twenty": "{n} articles — presque un record 🏆", + "items_twenty_plus": "{n} articles — courses épiques ! 🛒🔥", + "duplicates_one": "1 doublon (même article deux fois)", + "duplicates_many": "{n} doublons (pris plusieurs fois)", + "top_category": "Catégorie principale : {cat} ({count}×)", + "items_fallback": "{n} article{plural} ajouté{plural}" + }, + "kiosk": { + "check_btn": "🔍 Vérifier les mises à jour", + "checking": "⏳ Vérification…", + "error_check": "Erreur lors de la vérification des mises à jour", + "error_start_install": "Erreur au démarrage de l'installation", + "version_installed": "Installé : {v}", + "update_available": "⬆️ Nouvelle version disponible : {latest} (installée : {current})", + "up_to_date": "✅ Vous êtes à jour — version {v}", + "too_old": "⚠️ Le kiosque installé est trop ancien pour la vérification automatique des mises à jour.
    Appuyez sur le bouton ci-dessous pour télécharger et installer la nouvelle version directement.", + "manual_install": "⚠️ Ce kiosque ne supporte pas l'installation automatique.
    Procédure manuelle :
    1. Quittez le kiosque (bouton ✕ en haut à gauche)
    2. Désinstallez l'application EverShelf Kiosk
    3. Téléchargez et installez le nouvel APK depuis GitHub :", + "starting_download": "⏳ Démarrage du téléchargement…", + "install_btn": "⬇️ Installer la mise à jour", + "exit_title": "Quitter le kiosque", + "refresh_title": "Actualiser la page" + }, + "update": { + "new_version": "Nouvelle version", + "btn": "Mettre à jour" + }, + "gemini": { + "chat_title": "Discussion avec Gemini", + "not_configured": "🤖 Gemini non configuré — définissez GEMINI_API_KEY dans les paramètres" + }, + "appliances": { + "empty": "Aucun appareil ajouté" + }, + "about": { + "title": "À propos", + "version": "Version", + "report_bug": "Signaler un bug", + "report_bug_hint": "Quelque chose ne fonctionne pas ? Envoyez-nous un rapport directement depuis l'application.", + "report_bug_modal_title": "Signaler un bug", + "report_type_bug": "Bug", + "report_type_feature": "Fonctionnalité", + "report_type_question": "Question", + "report_field_title": "Titre", + "report_field_title_ph": "Brève description du problème", + "report_field_desc": "Description", + "report_field_desc_ph": "Décrivez le problème en détail…", + "report_field_steps": "Étapes pour reproduire (optionnel)", + "report_field_steps_ph": "1. Aller à…\n2. Appuyer sur…\n3. Voir l'erreur…", + "report_auto_info": "Joint automatiquement : version {version}, langue {lang}.", + "report_send_btn": "Envoyer le rapport", + "report_bug_sending": "Envoi…", + "report_bug_sent": "Rapport envoyé — merci !", + "report_bug_error": "Impossible d'envoyer le rapport. Vérifiez votre connexion.", + "changelog": "Journal des modifications", + "github": "Dépôt GitHub" + }, + "export": { + "title": "Exporter l'inventaire", + "hint": "Téléchargez l'inventaire actuel en CSV ou ouvrez la version imprimable (PDF).", + "btn_csv": "Télécharger CSV", + "btn_pdf": "PDF / Imprimer", + "btn_title": "Exporter" + }, + "startup": { + "connecting": "Connexion au serveur...", + "check_php_memory": "Mémoire PHP", + "check_php_timeout": "Délai PHP", + "check_php_upload": "Upload PHP", + "check_data_dir": "Dossier de données", + "check_rate_limits": "Dossier rate limits", + "check_backups": "Dossier sauvegardes", + "check_write_test": "Test d'écriture disque", + "check_disk_space": "Espace disque", + "check_db_connect": "Connexion base de données", + "check_db_tables": "Tables de la BDD", + "check_db_integrity": "Intégrité BDD", + "check_db_wal": "Mode WAL", + "check_db_size": "Taille de la BDD", + "check_db_rows": "Données inventaire", + "check_env": "Fichier .env", + "check_gemini": "Clé Gemini AI", + "check_bring_creds": "Identifiants Bring!", + "check_bring_token": "Token Bring!", + "check_curl_ssl": "cURL SSL", + "check_internet": "Connexion internet", + "fresh_install": "nouvelle installation", + "warnings_found": "avertissements détectés", + "all_ok": "Système OK", + "critical_error_short": "Erreur critique", + "critical_error": "Erreur critique : l'application ne peut pas démarrer. Vérifiez les logs.", + "error_network": "Impossible de contacter le serveur. Vérifiez votre connexion réseau.", + "retry": "Réessayer", + "syncing_local": "Synchronisation des données locales...", + "sync_done": "Données locales synchronisées" + }, + "stats_monthly": { + "title": "Statistiques Mensuelles", + "consumed": "produits utilisés", + "trend_up": "+{pct}% vs {prev}", + "trend_down": "-{pct}% vs {prev}", + "trend_same": "même rythme que le mois dernier", + "added": "ajoutés", + "wasted": "gaspillés", + "top_used": "le plus utilisé", + "top_cats": "Catégories principales", + "source": "Historique des transactions · mois en cours" + } } \ No newline at end of file diff --git a/translations/it.json b/translations/it.json index d725aff..b597cd2 100644 --- a/translations/it.json +++ b/translations/it.json @@ -1,1457 +1,1462 @@ { - "app": { - "name": "EverShelf", - "loading": "Caricamento..." - }, - "nav": { - "title": "EverShelf", - "home": "Home", - "inventory": "Dispensa", - "recipes": "Ricette", - "shopping": "Spesa", - "log": "Storico", - "settings": "Config" - }, - "btn": { - "back": "← Indietro", - "save": "💾 Salva", - "cancel": "✕ Annulla", - "close": "Chiudi", - "add": "✅ Aggiungi", - "delete": "Elimina", - "edit": "✏️ Modifica", - "use": "Usa", - "edit_item": "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", - "yes_short": "Sì", - "no_short": "No" - }, - "form": { - "select_placeholder": "-- Seleziona --" - }, - "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", - "millilitres": "Millilitri", - "from": "da" - }, - "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 da verificare", - "banner_prediction_hint": "La stima di consumo si adatta ai dati recenti: verifica solo se la quantità corrente è corretta.", - "banner_prediction_action_confirm": "Confermo {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_finished": "L'ho finito!", - "banner_expired_action_throw": "L'ho buttato", - "banner_expired_action_edit": "Correggi data", - "banner_expired_action_modify": "Modifica", - "banner_expired_action_vacuum": "Metti sottovuoto", - "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": "stima precedente: {expected} {unit}{time}; quantità attuale: {actual} {unit}.", - "banner_prediction_less": "stima: {expected} {unit}{time}; quantità attuale: {actual} {unit}. Se hai cambiato ritmo d'uso, la previsione si aggiorna automaticamente.", - "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_opened_detail": "{when} in {location} · 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", - "mode_shopping": "🛒 Modalità Spesa", - "mode_shopping_end": "✅ Fine spesa", - "spesa_btn": "🛒 Spesa", - "zoom": "Zoom", - "tab_barcode": "Barcode", - "tab_name": "Nome", - "tab_ai": "AI", - "recents_label": "Recenti", - "torch_hint": "Torcia", - "torch_on": "Torcia accesa", - "torch_off": "Torcia spenta", - "torch_unavailable": "Torcia non disponibile su questo dispositivo", - "flip_hint": "Cambia fotocamera", - "flip_front": "Fotocamera anteriore", - "flip_back": "Fotocamera posteriore", - "num_ocr_btn": "🔢 Leggi numeri con AI", - "num_ocr_searching": "Cerco il codice con AI...", - "num_ocr_found": "Codice trovato: {code}", - "num_ocr_not_found": "Nessun codice trovato nell'immagine", - "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", - "stock_in_pantry": "Hai gia in dispensa:", - "status_ready": "Inquadra il codice a barre", - "status_scanning": "Scansione in corso...", - "status_partial": "Letto: {code} — verifico...", - "status_invalid": "Non valido: {code} — riprovo", - "status_confirmed": "Confermato!", - "status_parallel": "Doppia scansione attiva..." - }, - "action": { - "title": "Cosa vuoi fare?", - "add_btn": "📥 AGGIUNGI", - "add_sub": "in dispensa/frigo", - "use_btn": "USA", - "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": "Ricetta", - "related_stock_title": "Hai anche in casa" - }, - "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", - "vacuum_question": "Messo sotto vuoto?", - "vacuum_saved": "🔒 Sotto vuoto registrato" - }, - "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}!", - "expiry_warning_opened": "⚠️ Quella{loc}, aperta da {when} — usala prima!", - "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", - "unknown_product": "Prodotto non riconosciuto", - "edit_name_brand": "Modifica nome/marca", - "weight_label": "Peso", - "origin_label": "Origine", - "labels_label": "Etichette", - "select_variant": "Seleziona la variante esatta o usa i dati AI:" - }, - "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", - "regen_choice_title": "Cosa vuoi fare con questa ricetta?", - "regen_replace": "🔄 Genera un'altra (scarta questa)", - "regen_save_new": "💾 Salva nell'archivio e genera una nuova", - "close_btn": "✅ Chiudi", - "ingredients_title": "🧾 Ingredienti", - "tools_title": "Strumenti necessari", - "steps_title": "👨‍🍳 Procedimento", - "no_steps": "Nessun procedimento disponibile", - "generate_error": "Errore nella generazione", - "stream_interrupted": "Generazione interrotta (risposta incompleta dal server). Controlla i log o riprova.", - "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", - "favorite": "Aggiungi ai preferiti", - "unfavorite": "Rimuovi dai preferiti", - "adjust_persons": "Persone" - }, - "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", - "pantry_hint": "Hai gia {qty} in dispensa" - }, - "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", - "recipe_prefix": "Ricetta" - }, - "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", - "quick_recipe_prompt": "Suggeriscimi una ricetta veloce PER UNA PERSONA usando i prodotti che scadono prima! Ignora i prodotti in freezer, concentrati su frigo e dispensa." - }, - "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", - "step_fallback": "Passo {n}", - "zerowaste_label": "♻️ Scarto", - "zerowaste_tip_title": "Consiglio anti-spreco" - }, - "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", - "reset_btn": "↺ Ripristina default" - }, - "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", - "token_title": "🔑 Token Impostazioni", - "token_label": "Token di accesso", - "token_hint": "Se `SETTINGS_TOKEN` è configurato nel `.env` server, inserisci qui il token prima di salvare le impostazioni. Lascia vuoto se non configurato.", - "token_placeholder": "(vuoto = nessuna protezione)", - "token_required_hint": "🔒 Questo server richiede un token per salvare le impostazioni.", - "cert_instructions": "Istruzioni per Chrome (Android):
    1. Scarica il certificato qui sopra
    2. Vai in Impostazioni → Sicurezza e privacy → Altre impostazioni di sicurezza → Installa da archivio dispositivo
    3. Seleziona il file EverShelf_CA.crt scaricato
    4. Scegli \"CA\" e conferma
    5. Riavvia Chrome

    Istruzioni per Chrome (PC):
    1. Scarica il certificato qui sopra
    2. Vai in chrome://settings/certificates (o Impostazioni → Privacy e sicurezza → Sicurezza → Gestisci certificati)
    3. Tab \"Autorità\" → Importa → seleziona il file
    4. Spunta \"Considera attendibile per identificare siti web\"
    5. Riavvia Chrome" - }, - "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", - "engine_label": "⚙️ Motore TTS", - "engine_browser": "🔇 Browser (offline, nessuna configurazione)", - "engine_server": "🌐 Server esterno (Home Assistant, API REST...)", - "voice_label": "🗣️ Voce", - "rate_label": "⚡ Velocità", - "pitch_label": "🎵 Tono", - "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_sound_btn": "🔔 Esegui Test Suono", - "test_btn": "🔊 Invia Test Vocale", - "voices_loading": "Caricamento voci…", - "voice_not_supported": "Voce non supportata dal browser", - "voices_none": "Nessuna voce disponibile su questo dispositivo", - "voices_hint": "Le voci disponibili dipendono dal sistema operativo e dal browser. Su macOS/iOS è disponibile la voce Paola (italiano). Premi ↺ se la lista non si carica.", - "url_missing": "⚠️ URL endpoint mancante.", - "test_sending": "⏳ Invio in corso…", - "test_ok": "✅ Risposta {code} — controlla che l'altoparlante abbia parlato.", - "heard_question": "Hai sentito la voce?", - "heard_yes": "Sì, ho sentito", - "heard_no": "No, non ho sentito", - "test_ok_kiosk": "TTS funzionante.", - "test_fail_steps": "Controlla: 1) volume media del dispositivo non sia 0; 2) Google Text-to-Speech installato e aggiornato; 3) pacchetto vocale italiano scaricato nelle impostazioni TTS Android." - }, - "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.", - "timeout_1": "1 minuto", - "timeout_2": "2 minuti", - "timeout_5": "5 minuti", - "timeout_10": "10 minuti", - "timeout_15": "15 minuti", - "timeout_30": "30 minuti", - "timeout_60": "1 ora", - "start_after": "⏱️ Avvia dopo" - }, - "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", - "live_weight": "peso in tempo reale", - "auto_reconnect": "🔁 Riconnessione: automatica", - "kiosk_title": "📡 Bilancia BLE integrata nel Kiosk", - "kiosk_hint": "La bilancia è gestita direttamente dal Gateway BLE interno al kiosk. Per abbinare un nuovo dispositivo usa il wizard di configurazione.", - "kiosk_reconfigure": "🔄 Riconfigura bilancia BLE", - "ble_protocols": "

    🔌 Protocolli BLE supportati:

    " - }, - "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/", - "native_title": "Configurazione Kiosk", - "native_hint": "URL server, bilancia BLE, salvaschermo e setup wizard.", - "native_btn": "Apri configurazione kiosk", - "native_tap_hint": "Tocca la rotella in alto a destra", - "native_update_hint": "Aggiorna l'app kiosk per usare questa funzione", - "update_title": "Aggiornamento Kiosk", - "check_updates_btn": "🔍 Cerca aggiornamenti", - "needs_update": "⚠️ Il kiosk installato non supporta questa funzione. Aggiorna l'app kiosk per abilitarla." - }, - "saved": "✅ Configurazione salvata!", - "saved_local": "✅ Configurazione salvata localmente", - "saved_local_error": "⚠️ Salvato localmente, errore server: {error}", - "theme": { - "title": "🌙 Tema / Aspetto", - "hint": "Scegli il tema dell interfaccia.", - "label": "🌙 Tema", - "off": "☀️ Chiaro", - "on": "🌙 Scuro", - "auto": "🔄 Automatico (orario)" - }, - "zerowaste": { - "card_title": "♻️ Suggerimenti zero-waste", - "card_hint": "Durante la cottura, mostra consigli su come riutilizzare gli scarti prodotti in ogni passo (bucce, acqua di cottura, ecc.). Disattivo per impostazione predefinita.", - "label": "Mostra suggerimenti durante la cottura" - }, - "backup": { - "tab": "Backup", - "local_title": "Backup Locale", - "local_hint": "Snapshot giornaliero del database. Configura quanti giorni di backup conservare.", - "enabled": "Backup automatico quotidiano", - "retention_days": "Giorni di retention", - "retention_info": "I backup vengono conservati per", - "backup_now": "Backup Ora", - "backing_up": "Backup in corso…", - "backed_up": "Backup completato", - "backup_error": "Errore backup", - "last_backup": "Ultimo backup", - "no_backup_yet": "Nessun backup ancora eseguito", - "list_empty": "Nessun backup disponibile", - "restore_btn": "Ripristina", - "restore_confirm": "Ripristinare il backup", - "delete_btn": "Elimina", - "delete_confirm": "Eliminare il backup", - "gdrive_title": "Google Drive", - "gdrive_hint": "Backup automatici su Google Drive via OAuth 2.0. Nessuna libreria esterna richiesta.", - "gdrive_enabled": "Abilita backup Google Drive", - "gdrive_folder_id": "ID Cartella Drive", - "gdrive_folder_id_hint": "Copia l'ID dalla URL della cartella Drive: …/folders/ID", - "gdrive_retention_days": "Retention Drive (giorni, 0=tutto)", - "gdrive_test": "Testa Connessione", - "gdrive_ok": "Connessione riuscita!", - "gdrive_error": "Connessione fallita", - "gdrive_push_now": "Carica Ora su Drive", - "gdrive_pushing": "Upload in corso…", - "gdrive_pushed": "Caricato su Drive", - "gdrive_wizard_hint": "Opzionale: backup giornaliero automatico su Google Drive via OAuth 2.0.", - "gdrive_skip": "Salta — configura dopo in Impostazioni", - "gdrive_client_id": "Client ID", - "gdrive_client_secret": "Client Secret", - "gdrive_redirect_uri_label": "Redirect URI (da aggiungere in Google Cloud Console):", - "gdrive_redirect_uri_hint": "Aggiungi http://localhost come URI di reindirizzamento autorizzato in Google Cloud Console. Funziona su qualsiasi server, anche senza dominio pubblico.", - "gdrive_oauth_authorize": "Autorizza con Google", - "gdrive_oauth_authorized": "Autorizzato", - "gdrive_oauth_not_authorized": "Non ancora autorizzato", - "gdrive_oauth_window_opened": "Finestra aperta — autorizza e torna qui", - "gdrive_oauth_how_to": "Come configurare OAuth 2.0 (passo dopo passo)", - "gdrive_oauth_steps": "
  • Vai su console.cloud.google.com e seleziona il progetto
  • Abilita la Google Drive API: API e servizi → Abilita API → Google Drive API
  • Vai su API e servizi → Credenziali → Crea credenziali → ID client OAuth 2.0
  • Tipo applicazione: Applicazione web; aggiungi http://localhost come URI di reindirizzamento autorizzato
  • Copia Client ID e Client Secret nei campi qui sopra e salva
  • Clicca Autorizza con Google, accedi e concedi l'accesso
  • Il browser aprirà http://localhost (possibile errore di connessione è normale): copia l'URL dalla barra degli indirizzi e incollalo nel campo che appare qui sotto
  • ", - "gdrive_code_title": "Incolla l'URL o il codice di autorizzazione", - "gdrive_code_hint": "Dopo aver autorizzato, il browser aprirà http://localhost e potrebbe mostrare un errore. Copia l'URL dalla barra degli indirizzi (es. http://localhost/?code=4%2F0A...) e incollalo qui.", - "gdrive_code_submit": "Conferma", - "gdrive_code_empty": "Incolla prima l'URL o il codice di autorizzazione" - }, - "info": { - "tab": "Info", - "ai_title": "Gemini AI — Utilizzo Token", - "ai_hint": "Consumo mensile e costo stimato per la chiave API corrente.", - "loading": "Caricamento…", - "total_tokens": "Token totali", - "est_cost": "Costo stimato", - "input_tok": "Token input", - "output_tok": "Token output", - "ai_calls": "Chiamate", - "by_action": "Dettaglio per funzione", - "by_model": "Dettaglio per modello", - "pricing_note": "Prezzi di riferimento Gemini: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.", - "system_title": "Sistema", - "db_size": "Database", - "log_size": "Log", - "log_level": "Livello log", - "ai_overview": "Prospetto utilizzo AI, inventario e stato del sistema", - "calls_unit": "call", - "inv_title": "Inventario", - "inv_active": "Attivi", - "inv_products": "Prodotti totali", - "inv_expiring": "In scadenza (7gg)", - "inv_expired": "Scaduti", - "inv_finished": "Finiti", - "act_title": "Attività del mese", - "act_tx_month": "Movimenti", - "act_restock": "Acquisti", - "act_use": "Consumi", - "act_new_products": "Nuovi prodotti", - "act_tx_year": "Movimenti anno", - "price_cache": "Cache prezzi", - "cache_entries": "prodotti", - "last_backup": "Ultimo backup", - "bring_days": "token scade tra {n} giorni", - "bring_expired": "token scaduto", - "year_label": "Anno {year}", - "currency_title": "Valuta", - "currency_hint": "La valuta usata per tutti i costi e i prezzi nell'app." - }, - "tab_general": "Generali", - "shopping": { - "tab": "Lista spesa", - "title": "Lista della spesa", - "hint": "Configura la lista della spesa integrata o collega Bring!.", - "enable_label": "Abilita lista della spesa", - "mode_label": "Provider", - "mode_internal": "Interno (senza Bring!)", - "mode_bring": "Bring! (app esterna)", - "bring_section_title": "Configurazione Bring!", - "ai_section_title": "Assistenza AI", - "smart_suggestions_label": "Suggerimenti AI", - "forecast_label": "Previsione prodotti in esaurimento", - "auto_add_label": "Aggiungi automaticamente quando", - "auto_add_suffix": "rimasto in magazzino (0 = solo quando esaurito)" - }, - "ha": { - "tab": "Home Assistant", - "title": "Home Assistant", - "hint": "Collega EverShelf a Home Assistant per automazioni, notifiche push e sensori REST.", - "enabled": "Abilita integrazione Home Assistant", - "connection_title": "Connessione", - "url_label": "URL Home Assistant", - "url_placeholder": "http://192.168.1.50:8123", - "url_hint": "URL del tuo server Home Assistant (es. http://homeassistant.local:8123).", - "token_label": "Long-Lived Access Token", - "token_hint": "Genera da Profilo HA → Sicurezza → Token di accesso a lungo termine.", - "token_placeholder": "eyJhbGci...", - "token_saved": "Token salvato (non mostrato per sicurezza)", - "test_btn": "Testa connessione", - "test_ok": "Connesso a {version}", - "test_fail": "Connessione fallita: {error}", - "test_bad_token": "HA raggiungibile ma token non valido", - "testing": "Test in corso…", - "error_no_url": "Inserisci prima l'URL di Home Assistant.", - "tts_title": "TTS su Speaker Smart", - "tts_hint": "Leggi i passi delle ricette su un media player di Home Assistant.", - "tts_entity_label": "Entity ID media player", - "tts_entity_placeholder": "media_player.living_room", - "tts_entity_hint": "Entity ID del media player su cui vuoi la voce. Puoi trovarlo in HA: Strumenti per sviluppatori → Stati.", - "tts_platform_label": "Piattaforma TTS", - "tts_platform_speak": "tts.speak (raccomandato)", - "tts_platform_notify": "notify.* (servizio notifiche)", - "tts_apply_btn": "Applica preset HA al tab TTS", - "tts_apply_hint": "Pre-compila il tab TTS con l'URL e il token di Home Assistant.", - "tts_preset_applied": "Preset HA applicato al tab TTS.", - "webhook_title": "Automazioni Webhook", - "webhook_hint": "Invia dati a Home Assistant quando avvengono eventi nella dispensa. Crea un'automazione in HA con trigger Webhook e copia l'ID generato.", - "webhook_id_label": "Webhook ID", - "webhook_id_placeholder": "evershelf_webhook_abc123", - "webhook_id_hint": "ID del webhook creato in HA. Copia da: HA → Impostazioni → Automazioni → Crea → Trigger Webhook.", - "webhook_events_label": "Notifica per questi eventi", - "event_expiry": "Prodotti in scadenza (giornaliero)", - "event_shopping": "Aggiunta alla lista della spesa", - "event_stock": "Aggiornamento scorte", - "expiry_days_label": "Anticipo scadenze (giorni)", - "expiry_days_hint": "Invia la notifica di scadenza N giorni prima della data di scadenza.", - "webhook_help": "In HA: Impostazioni → Automazioni → Crea automazione → Trigger: Webhook → copia l'ID generato qui sopra.", - "notify_title": "Notifiche Push", - "notify_hint": "Invia notifiche push al tuo telefono tramite il servizio notify di Home Assistant.", - "notify_service_label": "Servizio notify", - "notify_service_placeholder": "notify.mobile_app_mio_telefono", - "notify_service_hint": "Nome del servizio notify HA (es. notify.mobile_app_phone). Lascia vuoto per disabilitare.", - "sensor_title": "Sensori REST", - "sensor_hint": "Aggiungi a configuration.yaml per creare sensori EverShelf in Home Assistant.", - "sensor_copy_btn": "Copia YAML", - "sensor_copied": "YAML copiato negli appunti!", - "save_btn": "Salva impostazioni HA", - "ha_hint": "Se usi Home Assistant, usa il tab Home Assistant per configurare TTS, webhook e sensori." - } - }, - "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)", - "opened_ago_long": "Aperto da {n} giorni", - "opened_today_long": "Aperto oggi", - "opened_suffix": "— Aperto da troppo tempo!", - "opened_suffix_ok": "— Aperto (ancora ok)", - "opened_suffix_warning": "— Aperto (controlla)", - "days_compact": "{n}gg", - "badge_check_soon": "Controlla presto" - }, - "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}", - "finished_all": "📤 {name} terminato!", - "vacuum_sealed": "{name} salvato come sottovuoto", - "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", - "unknown": "Errore sconosciuto", - "prefix": "Errore", - "no_inventory_entry": "Nessuna voce di inventario trovata", - "offline_title": "Nessuna connessione", - "offline_subtitle": "L'app non riesce a raggiungere il server. Verifica la connessione Wi-Fi.", - "offline_checking": "Verifica connessione…", - "offline_restored": "Connessione ripristinata!", - "offline_continue": "Continua in modalità offline", - "offline_reading_cache": "Lettura dalla cache locale", - "offline_ops_pending": "{n} operazioni in attesa", - "offline_synced": "{n} operazioni sincronizzate", - "offline_ai_disabled": "Non disponibile offline", - "offline_cache_ready": "Offline — {n} prodotti in cache" - }, - "confirm": { - "remove_item": "Vuoi davvero rimuovere questo prodotto dall'inventario?", - "kiosk_exit": "Uscire dalla modalità kiosk?", - "cancel": "Annulla", - "proceed": "Conferma", - "discard_one": "Butta 1 pezzo" - }, - "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", - "choose_location_title": "Quale modifica?", - "choose_location_hint": "Scegli la posizione da modificare:", - "confirm_large_qty": "Stai impostando la quantità a {qty} {unit}. Questo sembra un valore insolitamente alto. Confermare?" - }, - "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", - "expiry_found": "Data trovata", - "expiry_read_fail": "Non riesco a leggere la data.", - "expiry_raw_label": "Letto" - }, - "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", - "vacuum_seal_rest": "Metti sotto vuoto il resto" - }, - "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", - "macros_title": "Macronutrienti stimati", - "macros_proteins": "Proteine", - "macros_carbs": "Carboidrati", - "macros_fat": "Grassi", - "macros_fiber": "Fibre", - "macros_source": "Stima basata su {n} prodotti in dispensa" - }, - "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" - }, + "app": { + "name": "EverShelf", + "loading": "Caricamento..." + }, + "nav": { + "title": "EverShelf", + "home": "Home", + "inventory": "Dispensa", + "recipes": "Ricette", + "shopping": "Spesa", + "log": "Storico", + "settings": "Config" + }, + "btn": { + "back": "← Indietro", + "save": "💾 Salva", + "cancel": "✕ Annulla", + "close": "Chiudi", + "add": "✅ Aggiungi", + "delete": "Elimina", + "edit": "✏️ Modifica", + "use": "Usa", + "edit_item": "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", + "yes_short": "Sì", + "no_short": "No" + }, + "form": { + "select_placeholder": "-- Seleziona --" + }, + "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", + "millilitres": "Millilitri", + "from": "da" + }, + "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 da verificare", + "banner_prediction_hint": "La stima di consumo si adatta ai dati recenti: verifica solo se la quantità corrente è corretta.", + "banner_prediction_action_confirm": "Confermo {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_finished": "L'ho finito!", + "banner_expired_action_throw": "L'ho buttato", + "banner_expired_action_edit": "Correggi data", + "banner_expired_action_modify": "Modifica", + "banner_expired_action_vacuum": "Metti sottovuoto", + "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": "stima precedente: {expected} {unit}{time}; quantità attuale: {actual} {unit}.", + "banner_prediction_less": "stima: {expected} {unit}{time}; quantità attuale: {actual} {unit}. Se hai cambiato ritmo d'uso, la previsione si aggiorna automaticamente.", + "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_opened_detail": "{when} in {location} · 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", + "mode_shopping": "🛒 Modalità Spesa", + "mode_shopping_end": "✅ Fine spesa", + "spesa_btn": "🛒 Spesa", + "zoom": "Zoom", + "tab_barcode": "Barcode", + "tab_name": "Nome", + "tab_ai": "AI", + "recents_label": "Recenti", + "torch_hint": "Torcia", + "torch_on": "Torcia accesa", + "torch_off": "Torcia spenta", + "torch_unavailable": "Torcia non disponibile su questo dispositivo", + "flip_hint": "Cambia fotocamera", + "flip_front": "Fotocamera anteriore", + "flip_back": "Fotocamera posteriore", + "num_ocr_btn": "🔢 Leggi numeri con AI", + "num_ocr_searching": "Cerco il codice con AI...", + "num_ocr_found": "Codice trovato: {code}", + "num_ocr_not_found": "Nessun codice trovato nell'immagine", + "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", + "stock_in_pantry": "Hai gia in dispensa:", + "status_ready": "Inquadra il codice a barre", + "status_scanning": "Scansione in corso...", + "status_partial": "Letto: {code} — verifico...", + "status_invalid": "Non valido: {code} — riprovo", + "status_confirmed": "Confermato!", + "status_parallel": "Doppia scansione attiva...", + "ai_fallback_searching": "Identificazione AI in corso...", + "ai_fallback_found": "Prodotto identificato dall'AI", + "ai_fallback_not_found": "AI: prodotto non riconosciuto" + }, + "action": { + "title": "Cosa vuoi fare?", + "add_btn": "📥 AGGIUNGI", + "add_sub": "in dispensa/frigo", + "use_btn": "USA", + "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": "Ricetta", + "related_stock_title": "Hai anche in casa" + }, + "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", + "vacuum_question": "Messo sotto vuoto?", + "vacuum_saved": "🔒 Sotto vuoto registrato" + }, + "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}!", + "expiry_warning_opened": "⚠️ Quella{loc}, aperta da {when} — usala prima!", + "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", + "unknown_product": "Prodotto non riconosciuto", + "edit_name_brand": "Modifica nome/marca", + "weight_label": "Peso", + "origin_label": "Origine", + "labels_label": "Etichette", + "select_variant": "Seleziona la variante esatta o usa i dati AI:" + }, + "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", + "regen_choice_title": "Cosa vuoi fare con questa ricetta?", + "regen_replace": "🔄 Genera un'altra (scarta questa)", + "regen_save_new": "💾 Salva nell'archivio e genera una nuova", + "close_btn": "✅ Chiudi", + "ingredients_title": "🧾 Ingredienti", + "tools_title": "Strumenti necessari", + "steps_title": "👨‍🍳 Procedimento", + "no_steps": "Nessun procedimento disponibile", + "generate_error": "Errore nella generazione", + "stream_interrupted": "Generazione interrotta (risposta incompleta dal server). Controlla i log o riprova.", + "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", + "favorite": "Aggiungi ai preferiti", + "unfavorite": "Rimuovi dai preferiti", + "adjust_persons": "Persone" + }, + "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", + "pantry_hint": "Hai gia {qty} in dispensa" + }, + "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", + "recipe_prefix": "Ricetta" + }, + "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", + "quick_recipe_prompt": "Suggeriscimi una ricetta veloce PER UNA PERSONA usando i prodotti che scadono prima! Ignora i prodotti in freezer, concentrati su frigo e dispensa." + }, + "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", + "step_fallback": "Passo {n}", + "zerowaste_label": "♻️ Scarto", + "zerowaste_tip_title": "Consiglio anti-spreco" + }, + "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": { - "chat_title": "Chat con Gemini", - "not_configured": "🤖 Gemini non configurato — imposta GEMINI_API_KEY nelle impostazioni" + "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", + "reset_btn": "↺ Ripristina default" }, "appliances": { - "empty": "Nessun elettrodomestico aggiunto" + "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" }, - "about": { - "title": "Informazioni", - "version": "Versione", - "report_bug": "Segnala un problema", - "report_bug_hint": "Qualcosa non funziona? Inviaci una segnalazione direttamente dall'app.", - "report_bug_modal_title": "Segnala un problema", - "report_type_bug": "Bug", - "report_type_feature": "Funzionalità", - "report_type_question": "Domanda", - "report_field_title": "Titolo", - "report_field_title_ph": "Breve descrizione del problema", - "report_field_desc": "Descrizione", - "report_field_desc_ph": "Descrivi il problema in dettaglio…", - "report_field_steps": "Passi per riprodurlo (opzionale)", - "report_field_steps_ph": "1. Vai su…\n2. Tocca…\n3. Vedi l'errore…", - "report_auto_info": "Saranno allegati automaticamente: versione {version}, lingua {lang}.", - "report_send_btn": "Invia segnalazione", - "report_bug_sending": "Invio in corso…", - "report_bug_sent": "Segnalazione inviata — grazie!", - "report_bug_error": "Impossibile inviare la segnalazione. Controlla la connessione.", - "changelog": "Changelog", - "github": "Repository GitHub" + "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}" }, - "export": { - "title": "Esporta inventario", - "hint": "Scarica l inventario corrente in CSV o apri la versione stampabile (PDF).", - "btn_csv": "Scarica CSV", - "btn_pdf": "PDF / Stampa", - "btn_title": "Esporta" + "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", + "ai_fallback_label": "Identificazione visiva AI (fallback 5s)", + "ai_fallback_hint": "Se il codice a barre non viene letto entro 5 secondi, un fotogramma viene inviato automaticamente all'AI per identificare il prodotto visivamente. Richiede Gemini configurato." }, - "startup": { - "connecting": "Connessione al server...", - "check_php_memory": "Memoria PHP", - "check_php_timeout": "Timeout PHP", - "check_php_upload": "Upload PHP", - "check_data_dir": "Cartella dati", - "check_rate_limits": "Dir rate limits", - "check_backups": "Dir backup", - "check_write_test": "Test scrittura disco", - "check_disk_space": "Spazio disco", - "check_db_legacy": "DB legacy (dispensa.db)", - "check_db_connect": "Connessione database", - "check_db_tables": "Tabelle database", - "check_db_integrity": "Integrità database", - "check_db_wal": "WAL mode", - "check_db_size": "Dimensione database", - "check_db_rows": "Dati inventario", - "check_env": "File .env", - "check_gemini": "Chiave Gemini AI", - "check_bring_creds": "Credenziali Bring!", - "check_bring_token": "Token Bring!", - "check_tts": "URL Text-to-Speech", - "check_scale": "Gateway bilancia", - "check_curl_ssl": "cURL SSL", - "check_internet": "Connessione internet", - "fresh_install": "nuovo impianto", - "warnings_found": "avvisi rilevati", - "all_ok": "Sistema OK", - "critical_error_short": "Errore critico", - "critical_error": "Errore critico: l'app non può avviarsi. Controlla i log del server.", - "critical_error_intro": "L'app non può avviarsi a causa dei seguenti problemi:", - "error_network": "Impossibile contattare il server.", - "error_network_detail": "Il browser non riesce a raggiungere il server PHP.\n\nPossibili cause:\n• Il server Apache/PHP non è in esecuzione\n• Problema di rete o firewall\n• URL dell'app non corretta\n\nControlla che il server sia avviato e riprova.", - "retry": "Riprova", - "syncing_local": "Sincronizzazione dati locali...", - "sync_done": "Dati locali aggiornati" + "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", + "token_title": "🔑 Token Impostazioni", + "token_label": "Token di accesso", + "token_hint": "Se `SETTINGS_TOKEN` è configurato nel `.env` server, inserisci qui il token prima di salvare le impostazioni. Lascia vuoto se non configurato.", + "token_placeholder": "(vuoto = nessuna protezione)", + "token_required_hint": "🔒 Questo server richiede un token per salvare le impostazioni.", + "cert_instructions": "Istruzioni per Chrome (Android):
    1. Scarica il certificato qui sopra
    2. Vai in Impostazioni → Sicurezza e privacy → Altre impostazioni di sicurezza → Installa da archivio dispositivo
    3. Seleziona il file EverShelf_CA.crt scaricato
    4. Scegli \"CA\" e conferma
    5. Riavvia Chrome

    Istruzioni per Chrome (PC):
    1. Scarica il certificato qui sopra
    2. Vai in chrome://settings/certificates (o Impostazioni → Privacy e sicurezza → Sicurezza → Gestisci certificati)
    3. Tab \"Autorità\" → Importa → seleziona il file
    4. Spunta \"Considera attendibile per identificare siti web\"
    5. Riavvia Chrome" }, - "stats_monthly": { - "title": "Statistiche Mensili", - "consumed": "prodotti usati", - "trend_up": "+{pct}% rispetto a {prev}", - "trend_down": "-{pct}% rispetto a {prev}", - "trend_same": "stesso ritmo del mese scorso", - "added": "aggiunti", - "wasted": "sprecati", - "top_used": "più usato", - "top_cats": "Categorie principali", - "source": "Storico transazioni · mese corrente" + "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", + "engine_label": "⚙️ Motore TTS", + "engine_browser": "🔇 Browser (offline, nessuna configurazione)", + "engine_server": "🌐 Server esterno (Home Assistant, API REST...)", + "voice_label": "🗣️ Voce", + "rate_label": "⚡ Velocità", + "pitch_label": "🎵 Tono", + "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_sound_btn": "🔔 Esegui Test Suono", + "test_btn": "🔊 Invia Test Vocale", + "voices_loading": "Caricamento voci…", + "voice_not_supported": "Voce non supportata dal browser", + "voices_none": "Nessuna voce disponibile su questo dispositivo", + "voices_hint": "Le voci disponibili dipendono dal sistema operativo e dal browser. Su macOS/iOS è disponibile la voce Paola (italiano). Premi ↺ se la lista non si carica.", + "url_missing": "⚠️ URL endpoint mancante.", + "test_sending": "⏳ Invio in corso…", + "test_ok": "✅ Risposta {code} — controlla che l'altoparlante abbia parlato.", + "heard_question": "Hai sentito la voce?", + "heard_yes": "Sì, ho sentito", + "heard_no": "No, non ho sentito", + "test_ok_kiosk": "TTS funzionante.", + "test_fail_steps": "Controlla: 1) volume media del dispositivo non sia 0; 2) Google Text-to-Speech installato e aggiornato; 3) pacchetto vocale italiano scaricato nelle impostazioni TTS Android." + }, + "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.", + "timeout_1": "1 minuto", + "timeout_2": "2 minuti", + "timeout_5": "5 minuti", + "timeout_10": "10 minuti", + "timeout_15": "15 minuti", + "timeout_30": "30 minuti", + "timeout_60": "1 ora", + "start_after": "⏱️ Avvia dopo" + }, + "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", + "live_weight": "peso in tempo reale", + "auto_reconnect": "🔁 Riconnessione: automatica", + "kiosk_title": "📡 Bilancia BLE integrata nel Kiosk", + "kiosk_hint": "La bilancia è gestita direttamente dal Gateway BLE interno al kiosk. Per abbinare un nuovo dispositivo usa il wizard di configurazione.", + "kiosk_reconfigure": "🔄 Riconfigura bilancia BLE", + "ble_protocols": "

    🔌 Protocolli BLE supportati:

    " + }, + "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/", + "native_title": "Configurazione Kiosk", + "native_hint": "URL server, bilancia BLE, salvaschermo e setup wizard.", + "native_btn": "Apri configurazione kiosk", + "native_tap_hint": "Tocca la rotella in alto a destra", + "native_update_hint": "Aggiorna l'app kiosk per usare questa funzione", + "update_title": "Aggiornamento Kiosk", + "check_updates_btn": "🔍 Cerca aggiornamenti", + "needs_update": "⚠️ Il kiosk installato non supporta questa funzione. Aggiorna l'app kiosk per abilitarla." + }, + "saved": "✅ Configurazione salvata!", + "saved_local": "✅ Configurazione salvata localmente", + "saved_local_error": "⚠️ Salvato localmente, errore server: {error}", + "theme": { + "title": "🌙 Tema / Aspetto", + "hint": "Scegli il tema dell interfaccia.", + "label": "🌙 Tema", + "off": "☀️ Chiaro", + "on": "🌙 Scuro", + "auto": "🔄 Automatico (orario)" + }, + "zerowaste": { + "card_title": "♻️ Suggerimenti zero-waste", + "card_hint": "Durante la cottura, mostra consigli su come riutilizzare gli scarti prodotti in ogni passo (bucce, acqua di cottura, ecc.). Disattivo per impostazione predefinita.", + "label": "Mostra suggerimenti durante la cottura" + }, + "backup": { + "tab": "Backup", + "local_title": "Backup Locale", + "local_hint": "Snapshot giornaliero del database. Configura quanti giorni di backup conservare.", + "enabled": "Backup automatico quotidiano", + "retention_days": "Giorni di retention", + "retention_info": "I backup vengono conservati per", + "backup_now": "Backup Ora", + "backing_up": "Backup in corso…", + "backed_up": "Backup completato", + "backup_error": "Errore backup", + "last_backup": "Ultimo backup", + "no_backup_yet": "Nessun backup ancora eseguito", + "list_empty": "Nessun backup disponibile", + "restore_btn": "Ripristina", + "restore_confirm": "Ripristinare il backup", + "delete_btn": "Elimina", + "delete_confirm": "Eliminare il backup", + "gdrive_title": "Google Drive", + "gdrive_hint": "Backup automatici su Google Drive via OAuth 2.0. Nessuna libreria esterna richiesta.", + "gdrive_enabled": "Abilita backup Google Drive", + "gdrive_folder_id": "ID Cartella Drive", + "gdrive_folder_id_hint": "Copia l'ID dalla URL della cartella Drive: …/folders/ID", + "gdrive_retention_days": "Retention Drive (giorni, 0=tutto)", + "gdrive_test": "Testa Connessione", + "gdrive_ok": "Connessione riuscita!", + "gdrive_error": "Connessione fallita", + "gdrive_push_now": "Carica Ora su Drive", + "gdrive_pushing": "Upload in corso…", + "gdrive_pushed": "Caricato su Drive", + "gdrive_wizard_hint": "Opzionale: backup giornaliero automatico su Google Drive via OAuth 2.0.", + "gdrive_skip": "Salta — configura dopo in Impostazioni", + "gdrive_client_id": "Client ID", + "gdrive_client_secret": "Client Secret", + "gdrive_redirect_uri_label": "Redirect URI (da aggiungere in Google Cloud Console):", + "gdrive_redirect_uri_hint": "Aggiungi http://localhost come URI di reindirizzamento autorizzato in Google Cloud Console. Funziona su qualsiasi server, anche senza dominio pubblico.", + "gdrive_oauth_authorize": "Autorizza con Google", + "gdrive_oauth_authorized": "Autorizzato", + "gdrive_oauth_not_authorized": "Non ancora autorizzato", + "gdrive_oauth_window_opened": "Finestra aperta — autorizza e torna qui", + "gdrive_oauth_how_to": "Come configurare OAuth 2.0 (passo dopo passo)", + "gdrive_oauth_steps": "
  • Vai su console.cloud.google.com e seleziona il progetto
  • Abilita la Google Drive API: API e servizi → Abilita API → Google Drive API
  • Vai su API e servizi → Credenziali → Crea credenziali → ID client OAuth 2.0
  • Tipo applicazione: Applicazione web; aggiungi http://localhost come URI di reindirizzamento autorizzato
  • Copia Client ID e Client Secret nei campi qui sopra e salva
  • Clicca Autorizza con Google, accedi e concedi l'accesso
  • Il browser aprirà http://localhost (possibile errore di connessione è normale): copia l'URL dalla barra degli indirizzi e incollalo nel campo che appare qui sotto
  • ", + "gdrive_code_title": "Incolla l'URL o il codice di autorizzazione", + "gdrive_code_hint": "Dopo aver autorizzato, il browser aprirà http://localhost e potrebbe mostrare un errore. Copia l'URL dalla barra degli indirizzi (es. http://localhost/?code=4%2F0A...) e incollalo qui.", + "gdrive_code_submit": "Conferma", + "gdrive_code_empty": "Incolla prima l'URL o il codice di autorizzazione" + }, + "info": { + "tab": "Info", + "ai_title": "Gemini AI — Utilizzo Token", + "ai_hint": "Consumo mensile e costo stimato per la chiave API corrente.", + "loading": "Caricamento…", + "total_tokens": "Token totali", + "est_cost": "Costo stimato", + "input_tok": "Token input", + "output_tok": "Token output", + "ai_calls": "Chiamate", + "by_action": "Dettaglio per funzione", + "by_model": "Dettaglio per modello", + "pricing_note": "Prezzi di riferimento Gemini: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.", + "system_title": "Sistema", + "db_size": "Database", + "log_size": "Log", + "log_level": "Livello log", + "ai_overview": "Prospetto utilizzo AI, inventario e stato del sistema", + "calls_unit": "call", + "inv_title": "Inventario", + "inv_active": "Attivi", + "inv_products": "Prodotti totali", + "inv_expiring": "In scadenza (7gg)", + "inv_expired": "Scaduti", + "inv_finished": "Finiti", + "act_title": "Attività del mese", + "act_tx_month": "Movimenti", + "act_restock": "Acquisti", + "act_use": "Consumi", + "act_new_products": "Nuovi prodotti", + "act_tx_year": "Movimenti anno", + "price_cache": "Cache prezzi", + "cache_entries": "prodotti", + "last_backup": "Ultimo backup", + "bring_days": "token scade tra {n} giorni", + "bring_expired": "token scaduto", + "year_label": "Anno {year}", + "currency_title": "Valuta", + "currency_hint": "La valuta usata per tutti i costi e i prezzi nell'app." + }, + "tab_general": "Generali", + "shopping": { + "tab": "Lista spesa", + "title": "Lista della spesa", + "hint": "Configura la lista della spesa integrata o collega Bring!.", + "enable_label": "Abilita lista della spesa", + "mode_label": "Provider", + "mode_internal": "Interno (senza Bring!)", + "mode_bring": "Bring! (app esterna)", + "bring_section_title": "Configurazione Bring!", + "ai_section_title": "Assistenza AI", + "smart_suggestions_label": "Suggerimenti AI", + "forecast_label": "Previsione prodotti in esaurimento", + "auto_add_label": "Aggiungi automaticamente quando", + "auto_add_suffix": "rimasto in magazzino (0 = solo quando esaurito)" + }, + "ha": { + "tab": "Home Assistant", + "title": "Home Assistant", + "hint": "Collega EverShelf a Home Assistant per automazioni, notifiche push e sensori REST.", + "enabled": "Abilita integrazione Home Assistant", + "connection_title": "Connessione", + "url_label": "URL Home Assistant", + "url_placeholder": "http://192.168.1.50:8123", + "url_hint": "URL del tuo server Home Assistant (es. http://homeassistant.local:8123).", + "token_label": "Long-Lived Access Token", + "token_hint": "Genera da Profilo HA → Sicurezza → Token di accesso a lungo termine.", + "token_placeholder": "eyJhbGci...", + "token_saved": "Token salvato (non mostrato per sicurezza)", + "test_btn": "Testa connessione", + "test_ok": "Connesso a {version}", + "test_fail": "Connessione fallita: {error}", + "test_bad_token": "HA raggiungibile ma token non valido", + "testing": "Test in corso…", + "error_no_url": "Inserisci prima l'URL di Home Assistant.", + "tts_title": "TTS su Speaker Smart", + "tts_hint": "Leggi i passi delle ricette su un media player di Home Assistant.", + "tts_entity_label": "Entity ID media player", + "tts_entity_placeholder": "media_player.living_room", + "tts_entity_hint": "Entity ID del media player su cui vuoi la voce. Puoi trovarlo in HA: Strumenti per sviluppatori → Stati.", + "tts_platform_label": "Piattaforma TTS", + "tts_platform_speak": "tts.speak (raccomandato)", + "tts_platform_notify": "notify.* (servizio notifiche)", + "tts_apply_btn": "Applica preset HA al tab TTS", + "tts_apply_hint": "Pre-compila il tab TTS con l'URL e il token di Home Assistant.", + "tts_preset_applied": "Preset HA applicato al tab TTS.", + "webhook_title": "Automazioni Webhook", + "webhook_hint": "Invia dati a Home Assistant quando avvengono eventi nella dispensa. Crea un'automazione in HA con trigger Webhook e copia l'ID generato.", + "webhook_id_label": "Webhook ID", + "webhook_id_placeholder": "evershelf_webhook_abc123", + "webhook_id_hint": "ID del webhook creato in HA. Copia da: HA → Impostazioni → Automazioni → Crea → Trigger Webhook.", + "webhook_events_label": "Notifica per questi eventi", + "event_expiry": "Prodotti in scadenza (giornaliero)", + "event_shopping": "Aggiunta alla lista della spesa", + "event_stock": "Aggiornamento scorte", + "expiry_days_label": "Anticipo scadenze (giorni)", + "expiry_days_hint": "Invia la notifica di scadenza N giorni prima della data di scadenza.", + "webhook_help": "In HA: Impostazioni → Automazioni → Crea automazione → Trigger: Webhook → copia l'ID generato qui sopra.", + "notify_title": "Notifiche Push", + "notify_hint": "Invia notifiche push al tuo telefono tramite il servizio notify di Home Assistant.", + "notify_service_label": "Servizio notify", + "notify_service_placeholder": "notify.mobile_app_mio_telefono", + "notify_service_hint": "Nome del servizio notify HA (es. notify.mobile_app_phone). Lascia vuoto per disabilitare.", + "sensor_title": "Sensori REST", + "sensor_hint": "Aggiungi a configuration.yaml per creare sensori EverShelf in Home Assistant.", + "sensor_copy_btn": "Copia YAML", + "sensor_copied": "YAML copiato negli appunti!", + "save_btn": "Salva impostazioni HA", + "ha_hint": "Se usi Home Assistant, usa il tab Home Assistant per configurare TTS, webhook e sensori." } + }, + "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)", + "opened_ago_long": "Aperto da {n} giorni", + "opened_today_long": "Aperto oggi", + "opened_suffix": "— Aperto da troppo tempo!", + "opened_suffix_ok": "— Aperto (ancora ok)", + "opened_suffix_warning": "— Aperto (controlla)", + "days_compact": "{n}gg", + "badge_check_soon": "Controlla presto" + }, + "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}", + "finished_all": "📤 {name} terminato!", + "vacuum_sealed": "{name} salvato come sottovuoto", + "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", + "unknown": "Errore sconosciuto", + "prefix": "Errore", + "no_inventory_entry": "Nessuna voce di inventario trovata", + "offline_title": "Nessuna connessione", + "offline_subtitle": "L'app non riesce a raggiungere il server. Verifica la connessione Wi-Fi.", + "offline_checking": "Verifica connessione…", + "offline_restored": "Connessione ripristinata!", + "offline_continue": "Continua in modalità offline", + "offline_reading_cache": "Lettura dalla cache locale", + "offline_ops_pending": "{n} operazioni in attesa", + "offline_synced": "{n} operazioni sincronizzate", + "offline_ai_disabled": "Non disponibile offline", + "offline_cache_ready": "Offline — {n} prodotti in cache" + }, + "confirm": { + "remove_item": "Vuoi davvero rimuovere questo prodotto dall'inventario?", + "kiosk_exit": "Uscire dalla modalità kiosk?", + "cancel": "Annulla", + "proceed": "Conferma", + "discard_one": "Butta 1 pezzo" + }, + "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", + "choose_location_title": "Quale modifica?", + "choose_location_hint": "Scegli la posizione da modificare:", + "confirm_large_qty": "Stai impostando la quantità a {qty} {unit}. Questo sembra un valore insolitamente alto. Confermare?" + }, + "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", + "expiry_found": "Data trovata", + "expiry_read_fail": "Non riesco a leggere la data.", + "expiry_raw_label": "Letto" + }, + "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", + "vacuum_seal_rest": "Metti sotto vuoto il resto" + }, + "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", + "macros_title": "Macronutrienti stimati", + "macros_proteins": "Proteine", + "macros_carbs": "Carboidrati", + "macros_fat": "Grassi", + "macros_fiber": "Fibre", + "macros_source": "Stima basata su {n} prodotti in dispensa" + }, + "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" + }, + "about": { + "title": "Informazioni", + "version": "Versione", + "report_bug": "Segnala un problema", + "report_bug_hint": "Qualcosa non funziona? Inviaci una segnalazione direttamente dall'app.", + "report_bug_modal_title": "Segnala un problema", + "report_type_bug": "Bug", + "report_type_feature": "Funzionalità", + "report_type_question": "Domanda", + "report_field_title": "Titolo", + "report_field_title_ph": "Breve descrizione del problema", + "report_field_desc": "Descrizione", + "report_field_desc_ph": "Descrivi il problema in dettaglio…", + "report_field_steps": "Passi per riprodurlo (opzionale)", + "report_field_steps_ph": "1. Vai su…\n2. Tocca…\n3. Vedi l'errore…", + "report_auto_info": "Saranno allegati automaticamente: versione {version}, lingua {lang}.", + "report_send_btn": "Invia segnalazione", + "report_bug_sending": "Invio in corso…", + "report_bug_sent": "Segnalazione inviata — grazie!", + "report_bug_error": "Impossibile inviare la segnalazione. Controlla la connessione.", + "changelog": "Changelog", + "github": "Repository GitHub" + }, + "export": { + "title": "Esporta inventario", + "hint": "Scarica l inventario corrente in CSV o apri la versione stampabile (PDF).", + "btn_csv": "Scarica CSV", + "btn_pdf": "PDF / Stampa", + "btn_title": "Esporta" + }, + "startup": { + "connecting": "Connessione al server...", + "check_php_memory": "Memoria PHP", + "check_php_timeout": "Timeout PHP", + "check_php_upload": "Upload PHP", + "check_data_dir": "Cartella dati", + "check_rate_limits": "Dir rate limits", + "check_backups": "Dir backup", + "check_write_test": "Test scrittura disco", + "check_disk_space": "Spazio disco", + "check_db_legacy": "DB legacy (dispensa.db)", + "check_db_connect": "Connessione database", + "check_db_tables": "Tabelle database", + "check_db_integrity": "Integrità database", + "check_db_wal": "WAL mode", + "check_db_size": "Dimensione database", + "check_db_rows": "Dati inventario", + "check_env": "File .env", + "check_gemini": "Chiave Gemini AI", + "check_bring_creds": "Credenziali Bring!", + "check_bring_token": "Token Bring!", + "check_tts": "URL Text-to-Speech", + "check_scale": "Gateway bilancia", + "check_curl_ssl": "cURL SSL", + "check_internet": "Connessione internet", + "fresh_install": "nuovo impianto", + "warnings_found": "avvisi rilevati", + "all_ok": "Sistema OK", + "critical_error_short": "Errore critico", + "critical_error": "Errore critico: l'app non può avviarsi. Controlla i log del server.", + "critical_error_intro": "L'app non può avviarsi a causa dei seguenti problemi:", + "error_network": "Impossibile contattare il server.", + "error_network_detail": "Il browser non riesce a raggiungere il server PHP.\n\nPossibili cause:\n• Il server Apache/PHP non è in esecuzione\n• Problema di rete o firewall\n• URL dell'app non corretta\n\nControlla che il server sia avviato e riprova.", + "retry": "Riprova", + "syncing_local": "Sincronizzazione dati locali...", + "sync_done": "Dati locali aggiornati" + }, + "stats_monthly": { + "title": "Statistiche Mensili", + "consumed": "prodotti usati", + "trend_up": "+{pct}% rispetto a {prev}", + "trend_down": "-{pct}% rispetto a {prev}", + "trend_same": "stesso ritmo del mese scorso", + "added": "aggiunti", + "wasted": "sprecati", + "top_used": "più usato", + "top_cats": "Categorie principali", + "source": "Storico transazioni · mese corrente" + } } \ No newline at end of file