Release v1.7.36: recipe stock hints, ghost products, and shopping total fix.

Adds pantry stock/remainder lines on recipe ingredients with zero-waste use-all on sealed package leftovers, ghost product restore in the dashboard, unified shopping totals, i18n sync, and maintenance scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dadaloop82
2026-06-04 17:22:59 +00:00
parent a0385cfb9b
commit cf65e79010
15 changed files with 1908 additions and 287 deletions
+47 -12
View File
@@ -143,8 +143,10 @@
"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_vanished": "Das Produkt erscheint nicht mehr im Bestand, aber die 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_finished_action_restore": "{qty} {unit} wiederherstellen",
"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",
@@ -164,7 +166,11 @@
"banner_opened_detail": "{when} in {location} · du hast noch <strong>{qty}</strong>.",
"banner_explain_title": "Gemini um eine Erklärung bitten",
"banner_explain_btn": "Erklären",
"banner_analyzing": "🤖 Analysiere…"
"banner_analyzing": "🤖 Analysiere…",
"banner_prediction_confirmed": "✅ Bestätigt — Prognosen werden aus den nächsten Einträgen neu berechnet",
"banner_anomaly_explain_fail": "KI-Erklärung konnte nicht abgerufen werden",
"banner_anomaly_dismissed": "Anomalie ignoriert",
"banner_finished_restore_prompt": "Wie viele {unit} {name} hast du noch? (Systemschätzung: {qty})"
},
"inventory": {
"title": "Vorrat",
@@ -243,7 +249,8 @@
"ai_match_none": "Keine ahnlichen Produkte in der Vorratskammer gefunden.",
"ai_match_use_btn": "Dieses nutzen",
"ai_match_add_btn": "\"{name}\" hinzufugen",
"ai_detected_label": "KI erkannt"
"ai_detected_label": "KI erkannt",
"mode_shopping_activated": "🛒 Einkaufsmodus aktiviert!"
},
"action": {
"title": "Was möchtest du tun?",
@@ -316,14 +323,17 @@
"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_one_conf": "<strong>1 Packung</strong> aufgebraucht ({qty})",
"disambiguation_all": "🗑️ ALLES verbraucht ({qty})",
"toast_one_conf_finished": "📦 1 Packung von {name} verbraucht!",
"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"
"throw_all_confirm_btn": "🗑️ Ja, entsorgen",
"locations_short": "Orte"
},
"product": {
"title_new": "Neues Produkt",
@@ -363,7 +373,9 @@
"weight_label": "Gewicht",
"origin_label": "Herkunft",
"labels_label": "Etiketten",
"select_variant": "Genaue Variante auswählen oder KI-Daten nutzen:"
"select_variant": "Genaue Variante auswählen oder KI-Daten nutzen:",
"history_badge": "📊 Verlauf",
"from_history": " (aus Verlauf)"
},
"products": {
"title": "📦 Alle Produkte",
@@ -425,7 +437,9 @@
"nutrition_per_serving": "Geschätzte Werte pro Portion",
"storage_title": "Aufbewahrung von Resten",
"storage_days": "{n} Tage",
"storage_immediately": "Am besten sofort verzehren"
"storage_immediately": "Am besten sofort verzehren",
"ing_stock_line": "Du hast {have} · {remain} bleiben nach Gebrauch",
"ing_use_all_note": "alles verwenden (<5% der Vollpackung)"
},
"shopping": {
"title": "🛒 Einkaufsliste",
@@ -512,6 +526,7 @@
"remove_error": "Fehler beim Entfernen",
"btn_fetch_prices": "Preise suchen",
"price_total_label": "💰 Geschätzter Gesamtpreis:",
"price_total_short": "geschätzte Ausgaben",
"price_loading": "Preise werden gesucht…",
"price_not_found": "Preis n/v",
"suggest_loading": "Analyse läuft...",
@@ -521,7 +536,8 @@
"priority_low": "Niedrig",
"smart_last_update": "Aktualisiert {time}",
"names_already_updated": "Alle Namen sind bereits aktuell",
"pantry_hint": "Bereits zuhause: {qty}"
"pantry_hint": "Bereits zuhause: {qty}",
"bring_names_migrated": "🔄 {n} Namen in Bring! verallgemeinert"
},
"ai": {
"title": "🤖 KI-Identifikation",
@@ -532,7 +548,8 @@
"no_api_key": "⚠️ Gemini API-Schlüssel nicht konfiguriert.\n<small>Füge GEMINI_API_KEY in der .env Datei auf dem Server hinzu.</small>",
"fields_filled": "✅ Felder von KI ausgefüllt",
"use_data": "✅ KI-Daten verwenden",
"use_data_no_barcode": "✅ KI-Daten verwenden (ohne Barcode)"
"use_data_no_barcode": "✅ KI-Daten verwenden (ohne Barcode)",
"conservation_hint": "🤖 KI: lagere in {location}"
},
"log": {
"title": "📒 Verlauf",
@@ -787,7 +804,13 @@
"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": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Unterstützte BLE-Protokolle:</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; Gewicht, Fett, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generisch &mdash; automatische Heuristik für 100+ Modelle</li></ul>"
"ble_protocols": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Unterstützte BLE-Protokolle:</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; Gewicht, Fett, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generisch &mdash; automatische Heuristik für 100+ Modelle</li></ul>",
"discover_scanning": "🔍 Lokales Netz wird nach Waagen-Gateway durchsucht…",
"discover_found": "✅ Gateway gefunden: {url}{more}",
"discover_not_found": "❌ Kein Gateway in {subnet}. Android-App auf demselben WLAN starten.",
"discover_failed": "❌ Suche fehlgeschlagen: {error}",
"discover_auto": "🔍 Auto",
"unknown_device": "Unbekanntes Gerät"
},
"kiosk": {
"hint": "Verwandeln Sie ein Android-Tablet in ein EverShelf-Panel mit integriertem BLE-Waagen-Gateway.",
@@ -974,7 +997,8 @@
"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."
}
},
"kiosk_update_required": "⚠️ Aktualisiere die Kiosk-App, um diese Funktion zu nutzen"
},
"expiry": {
"today": "HEUTE",
@@ -1048,6 +1072,7 @@
"finished_all": "📤 {name} aufgebraucht!",
"vacuum_sealed": "{name} als vakuumversiegelt gespeichert",
"product_finished_confirmed": "✅ Entfernt — wieder hinzufügen, wenn du nachkaufst",
"ghost_restored": "✅ {name}: {qty} {unit} im Bestand wiederhergestellt",
"appliance_added": "Gerät hinzugefügt",
"item_added": "{name} hinzugefügt"
},
@@ -1119,7 +1144,9 @@
"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"
"offline_cache_ready": "Offline — {n} Produkte im Cache",
"copy_failed": "Kopieren in die Zwischenablage fehlgeschlagen",
"invalid_quantity": "Ungültige Menge"
},
"confirm_placeholder_search": null,
"confirm": {
@@ -1241,7 +1268,8 @@
"stay_btn": "Nein, bleibt in {location}",
"moved_toast": "📦 Offene Packung bewegt nach {location}",
"vacuum_restore": "Vakuum wiederherstellen",
"vacuum_seal_rest": "Rest vakuumieren"
"vacuum_seal_rest": "Rest vakuumieren",
"moved_simple": "📦 Nach {location} verschoben"
},
"nova": {
"1": "Unverarbeitet",
@@ -1494,5 +1522,12 @@
"top_used": "meistbenutzt",
"top_cats": "Hauptkategorien",
"source": "Transaktionsverlauf · aktueller Monat"
},
"time": {
"just_now": "gerade eben",
"seconds_ago": "vor {n}s",
"minutes_ago": "vor {n} min",
"hours_ago": "vor {n} h",
"days_ago": "vor {n} T"
}
}
}
+47 -12
View File
@@ -143,8 +143,10 @@
"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_vanished": "This product no longer appears in inventory, 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_finished_action_restore": "Restore {qty} {unit}",
"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",
@@ -164,7 +166,11 @@
"banner_opened_detail": "{when} in {location} · you still have <strong>{qty}</strong>.",
"banner_explain_title": "Ask Gemini for an explanation",
"banner_explain_btn": "Explain",
"banner_analyzing": "🤖 Analyzing…"
"banner_analyzing": "🤖 Analyzing…",
"banner_prediction_confirmed": "✅ Confirmed — forecasts will recalculate from your next entries",
"banner_anomaly_explain_fail": "Could not get AI explanation",
"banner_anomaly_dismissed": "Anomaly dismissed",
"banner_finished_restore_prompt": "How many {unit} of {name} do you still have? (system estimate: {qty})"
},
"inventory": {
"title": "Pantry",
@@ -243,7 +249,8 @@
"ai_match_none": "No similar pantry products found.",
"ai_match_use_btn": "Use this",
"ai_match_add_btn": "Add \"{name}\"",
"ai_detected_label": "AI detected"
"ai_detected_label": "AI detected",
"mode_shopping_activated": "🛒 Shopping mode activated!"
},
"action": {
"title": "What do you want to do?",
@@ -316,14 +323,17 @@
"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_one_conf": "Finished <strong>1 package</strong> ({qty})",
"disambiguation_all": "🗑️ Finish EVERYTHING ({qty})",
"toast_one_conf_finished": "📦 1 package of {name} finished!",
"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"
"throw_all_confirm_btn": "🗑️ Yes, discard",
"locations_short": "places"
},
"product": {
"title_new": "New Product",
@@ -363,7 +373,9 @@
"weight_label": "Weight",
"origin_label": "Origin",
"labels_label": "Labels",
"select_variant": "Select the exact variant or use AI data:"
"select_variant": "Select the exact variant or use AI data:",
"history_badge": "📊 history",
"from_history": " (from history)"
},
"products": {
"title": "📦 All Products",
@@ -425,7 +437,9 @@
"nutrition_per_serving": "Estimated values per serving",
"storage_title": "How to store leftovers",
"storage_days": "{n} days",
"storage_immediately": "Best eaten immediately"
"storage_immediately": "Best eaten immediately",
"ing_stock_line": "You have {have} · {remain} left after use",
"ing_use_all_note": "use all (<5% of full package left)"
},
"shopping": {
"title": "🛒 Shopping List",
@@ -512,6 +526,7 @@
"remove_error": "Removal error",
"btn_fetch_prices": "Find prices",
"price_total_label": "💰 Estimated total:",
"price_total_short": "estimated total",
"price_loading": "Looking up prices…",
"price_not_found": "price n/a",
"suggest_loading": "Analyzing...",
@@ -521,7 +536,8 @@
"priority_low": "Low",
"smart_last_update": "Updated {time}",
"names_already_updated": "All names are already up to date",
"pantry_hint": "Already at home: {qty}"
"pantry_hint": "Already at home: {qty}",
"bring_names_migrated": "🔄 {n} names generalized in Bring!"
},
"ai": {
"title": "🤖 AI Identification",
@@ -532,7 +548,8 @@
"no_api_key": "⚠️ Gemini API key not configured.\n<small>Add GEMINI_API_KEY to the .env file on the server.</small>",
"fields_filled": "✅ Fields filled by AI",
"use_data": "✅ Use AI data",
"use_data_no_barcode": "✅ Use AI data (no barcode)"
"use_data_no_barcode": "✅ Use AI data (no barcode)",
"conservation_hint": "🤖 AI: store in {location}"
},
"log": {
"title": "📒 Operations Log",
@@ -787,7 +804,13 @@
"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": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Supported BLE protocols:</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; weight, fat, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generic &mdash; automatic heuristic for 100+ models</li></ul>"
"ble_protocols": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Supported BLE protocols:</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; weight, fat, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generic &mdash; automatic heuristic for 100+ models</li></ul>",
"discover_scanning": "🔍 Scanning local network for scale gateway…",
"discover_found": "✅ Gateway found: {url}{more}",
"discover_not_found": "❌ No gateway found on {subnet}. Make sure the Android app is running and on the same Wi-Fi.",
"discover_failed": "❌ Discovery failed: {error}",
"discover_auto": "🔍 Auto",
"unknown_device": "Unknown device"
},
"kiosk": {
"hint": "Turn an Android tablet into an always-on EverShelf panel with built-in BLE scale gateway.",
@@ -974,7 +997,8 @@
"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."
}
},
"kiosk_update_required": "⚠️ Update the kiosk app to use this feature"
},
"expiry": {
"today": "TODAY",
@@ -1048,6 +1072,7 @@
"finished_all": "📤 {name} finished!",
"vacuum_sealed": "{name} saved as vacuum sealed",
"product_finished_confirmed": "✅ Removed — add it again when you restock",
"ghost_restored": "✅ {name}: restored {qty} {unit} to inventory",
"appliance_added": "Appliance added",
"item_added": "{name} added"
},
@@ -1119,7 +1144,9 @@
"offline_ops_pending": "{n} operations pending",
"offline_synced": "{n} operations synced",
"offline_ai_disabled": "Not available offline",
"offline_cache_ready": "Offline — {n} items cached"
"offline_cache_ready": "Offline — {n} items cached",
"copy_failed": "Copy to clipboard failed",
"invalid_quantity": "Invalid quantity"
},
"confirm_placeholder_search": null,
"confirm": {
@@ -1241,7 +1268,8 @@
"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"
"vacuum_seal_rest": "Vacuum seal the rest",
"moved_simple": "📦 Moved to {location}"
},
"nova": {
"1": "Unprocessed",
@@ -1494,5 +1522,12 @@
"top_used": "top used",
"top_cats": "Top categories",
"source": "Transaction history · current month"
},
"time": {
"just_now": "just now",
"seconds_ago": "{n}s ago",
"minutes_ago": "{n} min ago",
"hours_ago": "{n} h ago",
"days_ago": "{n} d ago"
}
}
}
+109 -17
View File
@@ -141,8 +141,10 @@
"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_vanished": "Este producto ya no aparece en el inventario, 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_finished_action_restore": "Restaurar {qty} {unit}",
"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",
@@ -162,7 +164,13 @@
"banner_opened_detail": "{when} en {location} · aún tienes <strong>{qty}</strong>.",
"banner_explain_title": "Pedir explicación a Gemini",
"banner_explain_btn": "Explicar",
"banner_analyzing": "🤖 Analizando…"
"banner_analyzing": "🤖 Analizando…",
"banner_expired_action_modify": "Editar",
"banner_expired_action_vacuum": "Poner al vacío",
"banner_prediction_confirmed": "✅ Confirmado — las previsiones se recalcularán con tus próximos registros",
"banner_anomaly_explain_fail": "No se pudo obtener la explicación de IA",
"banner_anomaly_dismissed": "Anomalía descartada",
"banner_finished_restore_prompt": "¿Cuántas {unit} de {name} te quedan? (estimación del sistema: {qty})"
},
"inventory": {
"title": "Despensa",
@@ -240,7 +248,9 @@
"ai_match_none": "No se encontraron productos similares en despensa.",
"ai_match_use_btn": "Usar este",
"ai_match_add_btn": "Agregar \"{name}\"",
"ai_detected_label": "IA detecto"
"ai_detected_label": "IA detecto",
"stock_in_pantry": "Ya en despensa:",
"mode_shopping_activated": "🛒 ¡Modo compras activado!"
},
"action": {
"title": "¿Qué quieres hacer?",
@@ -254,7 +264,8 @@
"throw_btn": "🗑️ DESECHAR",
"throw_sub": "tirar",
"edit_sub": "caducidad, ubicación…",
"create_recipe_btn": "Receta"
"create_recipe_btn": "Receta",
"related_stock_title": "También en casa"
},
"add": {
"title": "Añadir a la despensa",
@@ -312,14 +323,17 @@
"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_one_conf": "Terminado <strong>1 envase</strong> ({qty})",
"disambiguation_all": "🗑️ Terminar TODO ({qty})",
"toast_one_conf_finished": "📦 1 envase de {name} terminado!",
"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"
"throw_all_confirm_btn": "🗑️ Sí, desechar",
"locations_short": "ubicaciones"
},
"product": {
"title_new": "Nuevo producto",
@@ -359,7 +373,9 @@
"weight_label": "Peso",
"origin_label": "Origen",
"labels_label": "Etiquetas",
"select_variant": "Selecciona la variante exacta o usa los datos de IA:"
"select_variant": "Selecciona la variante exacta o usa los datos de IA:",
"history_badge": "📊 historial",
"from_history": " (del historial)"
},
"products": {
"title": "📦 Todos los productos",
@@ -420,7 +436,10 @@
"nutrition_per_serving": "Valores estimados por ración",
"storage_title": "Cómo conservar las sobras",
"storage_days": "{n} días",
"storage_immediately": "Mejor consumir de inmediato"
"storage_immediately": "Mejor consumir de inmediato",
"stream_interrupted": "Generación interrumpida (respuesta del servidor incompleta). Revisa los logs o inténtalo de nuevo.",
"ing_stock_line": "Tienes {have} · quedan {remain} después del uso",
"ing_use_all_note": "usar todo (<5% del envase completo)"
},
"shopping": {
"title": "🛒 Lista de la compra",
@@ -507,6 +526,7 @@
"remove_error": "Error al eliminar",
"btn_fetch_prices": "Buscar precios",
"price_total_label": "💰 Total estimado:",
"price_total_short": "total estimado",
"price_loading": "Buscando precios…",
"price_not_found": "precio n/d",
"suggest_loading": "Analizando...",
@@ -515,7 +535,9 @@
"priority_medium": "Media",
"priority_low": "Baja",
"smart_last_update": "Actualizado {time}",
"names_already_updated": "Todos los nombres ya están actualizados"
"names_already_updated": "Todos los nombres ya están actualizados",
"pantry_hint": "Ya en casa: {qty}",
"bring_names_migrated": "🔄 {n} nombres generalizados en Bring!"
},
"ai": {
"title": "🤖 Identificación IA",
@@ -526,7 +548,8 @@
"no_api_key": "⚠️ Clave API de Gemini no configurada.\n<small>Añade GEMINI_API_KEY al archivo .env en el servidor.</small>",
"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)"
"use_data_no_barcode": "✅ Usar datos de IA (sin código de barras)",
"conservation_hint": "🤖 IA: conserva en {location}"
},
"log": {
"title": "📒 Registro de operaciones",
@@ -742,7 +765,8 @@
"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."
"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.",
"test_sound_btn": "🔔 Prueba de sonido"
},
"language": {
"title": "🌐 Idioma",
@@ -780,7 +804,13 @@
"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": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Protocolos BLE soportados:</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; peso, grasa, IMC</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Genérico &mdash; heurística automática para 100+ modelos</li></ul>"
"ble_protocols": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Protocolos BLE soportados:</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; peso, grasa, IMC</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Genérico &mdash; heurística automática para 100+ modelos</li></ul>",
"discover_scanning": "🔍 Buscando pasarela de báscula en la red local…",
"discover_found": "✅ Pasarela encontrada: {url}{more}",
"discover_not_found": "❌ Ninguna pasarela en {subnet}. Inicia la app Android en la misma Wi-Fi.",
"discover_failed": "❌ Búsqueda fallida: {error}",
"discover_auto": "🔍 Auto",
"unknown_device": "Dispositivo desconocido"
},
"kiosk": {
"hint": "Convierte una tableta Android en un panel EverShelf permanente con pasarela BLE integrada.",
@@ -926,7 +956,49 @@
"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."
}
},
"info": {
"tab": "Info",
"ai_title": "Gemini AI — Uso de tokens",
"ai_hint": "Consumo mensual y coste estimado para la clave API actual.",
"loading": "Cargando…",
"total_tokens": "Tokens totales",
"est_cost": "Coste est.",
"input_tok": "Tokens de entrada",
"output_tok": "Tokens de salida",
"ai_calls": "Llamadas",
"by_action": "Desglose por función",
"by_model": "Desglose por modelo",
"pricing_note": "Precios 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": "Base de datos",
"log_size": "Logs",
"log_level": "Nivel de log",
"ai_overview": "Resumen de IA, inventario y estado del sistema",
"calls_unit": "llamadas",
"inv_title": "Inventario",
"inv_active": "Activos",
"inv_products": "Productos totales",
"inv_expiring": "Caducan (7d)",
"inv_expired": "Caducados",
"inv_finished": "Agotados",
"act_title": "Actividad mensual",
"act_tx_month": "Movimientos",
"act_restock": "Reabastecimientos",
"act_use": "Usos",
"act_new_products": "Productos nuevos",
"act_tx_year": "Movimientos anuales",
"price_cache": "Caché de precios",
"cache_entries": "productos",
"last_backup": "Última copia",
"bring_days": "token expira en {n} días",
"bring_expired": "token expirado",
"year_label": "Año {year}",
"currency_title": "Moneda",
"currency_hint": "Moneda usada para todos los costes y precios en la app."
},
"tab_general": "General",
"kiosk_update_required": "⚠️ Actualiza la app kiosk para usar esta función"
},
"expiry": {
"today": "HOY",
@@ -999,8 +1071,10 @@
"thrown_away_partial": "🗑️ {qty} {unit} de {name} tirado(s)",
"finished_all": "📤 ¡{name} terminado!",
"product_finished_confirmed": "✅ Eliminado — añádelo de nuevo cuando reabastezcas",
"ghost_restored": "✅ {name}: restaurados {qty} {unit} en el inventario",
"appliance_added": "Electrodoméstico añadido",
"item_added": "{name} añadido"
"item_added": "{name} añadido",
"vacuum_sealed": "{name} guardado al vacío"
},
"antiwaste": {
"title": "🌱 Informe anti-desperdicio",
@@ -1070,7 +1144,9 @@
"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é"
"offline_cache_ready": "Offline — {n} productos en caché",
"copy_failed": "Error al copiar al portapapeles",
"invalid_quantity": "Cantidad no válida"
},
"confirm_placeholder_search": null,
"confirm": {
@@ -1171,7 +1247,10 @@
"retake_btn": "🔄 Repetir",
"camera_error_hint": "Asegúrate de usar HTTPS y haber concedido los permisos de cámara.<br>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"
"save_new_btn": "🆕 Ninguno de estos — guardar como nuevo",
"expiry_found": "Fecha encontrada",
"expiry_read_fail": "No se puede leer la fecha.",
"expiry_raw_label": "Leído"
},
"lowstock": {
"title": "⚠️ ¡Stock bajo!",
@@ -1189,7 +1268,8 @@
"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"
"vacuum_seal_rest": "🔒 Sellar el resto al vacío",
"moved_simple": "📦 Movido a {location}"
},
"nova": {
"1": "Sin procesar",
@@ -1424,7 +1504,12 @@
"token_autoconfig": "Configurando acceso...",
"token_prompt_title": "🔒 Token API",
"token_prompt_hint": "Introduce el valor API_TOKEN del archivo .env del servidor.",
"token_prompt_btn": "Continuar"
"token_prompt_btn": "Continuar",
"check_db_legacy": "BD antigua (dispensa.db)",
"check_tts": "URL texto a voz",
"check_scale": "Pasarela báscula",
"critical_error_intro": "La app no puede iniciarse por los siguientes problemas:",
"error_network_detail": "El navegador no puede conectar con el servidor PHP.\n\nPosibles causas:\n• Apache/PHP no está en ejecución\n• Problema de red o firewall\n• URL incorrecta\n\nInicia el servidor e inténtalo de nuevo."
},
"stats_monthly": {
"title": "Estadísticas Mensuales",
@@ -1437,5 +1522,12 @@
"top_used": "más usado",
"top_cats": "Categorías principales",
"source": "Historial de transacciones · mes actual"
},
"time": {
"just_now": "ahora",
"seconds_ago": "hace {n}s",
"minutes_ago": "hace {n} min",
"hours_ago": "hace {n} h",
"days_ago": "hace {n} d"
}
}
}
+109 -17
View File
@@ -141,8 +141,10 @@
"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_vanished": "Ce produit n'apparaît plus dans l'inventaire, 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_finished_action_restore": "Restaurer {qty} {unit}",
"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",
@@ -162,7 +164,13 @@
"banner_opened_detail": "{when} dans {location} · il vous reste encore <strong>{qty}</strong>.",
"banner_explain_title": "Demander une explication à Gemini",
"banner_explain_btn": "Expliquer",
"banner_analyzing": "🤖 Analyse en cours…"
"banner_analyzing": "🤖 Analyse en cours…",
"banner_expired_action_modify": "Modifier",
"banner_expired_action_vacuum": "Mettre sous vide",
"banner_prediction_confirmed": "✅ Confirmé — les prévisions seront recalculées à partir de vos prochains enregistrements",
"banner_anomaly_explain_fail": "Impossible d'obtenir l'explication IA",
"banner_anomaly_dismissed": "Anomalie ignorée",
"banner_finished_restore_prompt": "Combien de {unit} de {name} vous reste-t-il ? (estimation : {qty})"
},
"inventory": {
"title": "Garde-manger",
@@ -240,7 +248,9 @@
"ai_match_none": "Aucun produit similaire trouve dans le stock.",
"ai_match_use_btn": "Utiliser celui-ci",
"ai_match_add_btn": "Ajouter \"{name}\"",
"ai_detected_label": "IA a detecte"
"ai_detected_label": "IA a detecte",
"stock_in_pantry": "Déjà à la maison :",
"mode_shopping_activated": "🛒 Mode courses activé !"
},
"action": {
"title": "Que voulez-vous faire ?",
@@ -254,7 +264,8 @@
"throw_btn": "🗑️ JETER",
"throw_sub": "jeter",
"edit_sub": "péremption, emplacement…",
"create_recipe_btn": "Recette"
"create_recipe_btn": "Recette",
"related_stock_title": "Aussi à la maison"
},
"add": {
"title": "Ajouter au garde-manger",
@@ -312,14 +323,17 @@
"toast_bring": "🛒 Produit terminé → ajouté à Bring !",
"toast_opened_finished": "🔓 Emballage ouvert de {name} terminé !",
"disambiguation_hint": "Que voulez-vous dire par « tout fini » ?",
"disambiguation_one_conf": "Terminer <strong>1 emballage</strong> ({qty})",
"disambiguation_all": "🗑️ Tout finir ({qty})",
"toast_one_conf_finished": "📦 1 emballage de {name} terminé !",
"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"
"throw_all_confirm_btn": "🗑️ Oui, jeter",
"locations_short": "emplacements"
},
"product": {
"title_new": "Nouveau produit",
@@ -359,7 +373,9 @@
"weight_label": "Poids",
"origin_label": "Origine",
"labels_label": "Labels",
"select_variant": "Sélectionnez la variante exacte ou utilisez les données IA :"
"select_variant": "Sélectionnez la variante exacte ou utilisez les données IA :",
"history_badge": "📊 historique",
"from_history": " (historique)"
},
"products": {
"title": "📦 Tous les produits",
@@ -420,7 +436,10 @@
"nutrition_per_serving": "Valeurs estimées par portion",
"storage_title": "Comment conserver les restes",
"storage_days": "{n} jours",
"storage_immediately": "À consommer immédiatement"
"storage_immediately": "À consommer immédiatement",
"stream_interrupted": "Génération interrompue (réponse serveur incomplète). Vérifiez les logs ou réessayez.",
"ing_stock_line": "Vous avez {have} · il reste {remain} après usage",
"ing_use_all_note": "tout utiliser (<5% du conditionnement entier)"
},
"shopping": {
"title": "🛒 Liste de courses",
@@ -507,6 +526,7 @@
"remove_error": "Erreur de suppression",
"btn_fetch_prices": "Trouver les prix",
"price_total_label": "💰 Total estimé :",
"price_total_short": "total estimé",
"price_loading": "Recherche des prix…",
"price_not_found": "prix n/d",
"suggest_loading": "Analyse en cours...",
@@ -515,7 +535,9 @@
"priority_medium": "Moyenne",
"priority_low": "Faible",
"smart_last_update": "Mis à jour {time}",
"names_already_updated": "Tous les noms sont déjà à jour"
"names_already_updated": "Tous les noms sont déjà à jour",
"pantry_hint": "Déjà à la maison : {qty}",
"bring_names_migrated": "🔄 {n} noms généralisés dans Bring !"
},
"ai": {
"title": "🤖 Identification IA",
@@ -526,7 +548,8 @@
"no_api_key": "⚠️ Clé API Gemini non configurée.\n<small>Ajoutez GEMINI_API_KEY au fichier .env sur le serveur.</small>",
"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)"
"use_data_no_barcode": "✅ Utiliser les données IA (sans code-barres)",
"conservation_hint": "🤖 IA : conserve dans {location}"
},
"log": {
"title": "📒 Journal des opérations",
@@ -742,7 +765,8 @@
"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."
"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.",
"test_sound_btn": "🔔 Test sonore"
},
"language": {
"title": "🌐 Langue",
@@ -780,7 +804,13 @@
"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": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Protocoles BLE supportés :</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; poids, graisse, IMC</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Générique &mdash; heuristique automatique pour 100+ modèles</li></ul>"
"ble_protocols": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Protocoles BLE supportés :</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; poids, graisse, IMC</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Générique &mdash; heuristique automatique pour 100+ modèles</li></ul>",
"discover_scanning": "🔍 Recherche du gateway balance sur le réseau local…",
"discover_found": "✅ Gateway trouvé : {url}{more}",
"discover_not_found": "❌ Aucun gateway sur {subnet}. Lancez l'app Android sur le même Wi-Fi.",
"discover_failed": "❌ Échec de la recherche : {error}",
"discover_auto": "🔍 Auto",
"unknown_device": "Appareil inconnu"
},
"kiosk": {
"hint": "Transformez une tablette Android en panneau EverShelf permanent avec passerelle BLE intégrée.",
@@ -926,7 +956,49 @@
"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."
}
},
"info": {
"tab": "Info",
"ai_title": "Gemini AI — Utilisation des tokens",
"ai_hint": "Consommation mensuelle et coût estimé pour la clé API actuelle.",
"loading": "Chargement…",
"total_tokens": "Tokens totaux",
"est_cost": "Coût est.",
"input_tok": "Tokens entrée",
"output_tok": "Tokens sortie",
"ai_calls": "Appels",
"by_action": "Répartition par fonction",
"by_model": "Répartition par modèle",
"pricing_note": "Tarifs 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": "Système",
"db_size": "Base de données",
"log_size": "Logs",
"log_level": "Niveau de log",
"ai_overview": "Aperçu IA, inventaire et état du système",
"calls_unit": "appels",
"inv_title": "Inventaire",
"inv_active": "Actifs",
"inv_products": "Produits totaux",
"inv_expiring": "Expirent (7j)",
"inv_expired": "Expirés",
"inv_finished": "Terminés",
"act_title": "Activité mensuelle",
"act_tx_month": "Mouvements",
"act_restock": "Réapprovisionnements",
"act_use": "Utilisations",
"act_new_products": "Nouveaux produits",
"act_tx_year": "Mouvements annuels",
"price_cache": "Cache prix",
"cache_entries": "produits",
"last_backup": "Dernière sauvegarde",
"bring_days": "jeton expire dans {n} jours",
"bring_expired": "jeton expiré",
"year_label": "Année {year}",
"currency_title": "Devise",
"currency_hint": "Devise utilisée pour tous les coûts et prix dans l'app."
},
"tab_general": "Général",
"kiosk_update_required": "⚠️ Mettez à jour l'application kiosk pour utiliser cette fonction"
},
"expiry": {
"today": "AUJOURD'HUI",
@@ -999,8 +1071,10 @@
"thrown_away_partial": "🗑️ {qty} {unit} de {name} jeté(s)",
"finished_all": "📤 {name} terminé !",
"product_finished_confirmed": "✅ Supprimé — ajoutez-le à nouveau lors du réapprovisionnement",
"ghost_restored": "✅ {name} : {qty} {unit} restaurés dans l'inventaire",
"appliance_added": "Appareil ajouté",
"item_added": "{name} ajouté"
"item_added": "{name} ajouté",
"vacuum_sealed": "{name} enregistré sous vide"
},
"antiwaste": {
"title": "🌱 Rapport anti-gaspi",
@@ -1070,7 +1144,9 @@
"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"
"offline_cache_ready": "Offline — {n} produits en cache",
"copy_failed": "Échec de la copie dans le presse-papiers",
"invalid_quantity": "Quantité invalide"
},
"confirm_placeholder_search": null,
"confirm": {
@@ -1171,7 +1247,10 @@
"retake_btn": "🔄 Reprendre",
"camera_error_hint": "Assurez-vous d'utiliser HTTPS et d'avoir accordé les permissions caméra.<br>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"
"save_new_btn": "🆕 Aucun de ceux-ci — enregistrer comme nouveau",
"expiry_found": "Date trouvée",
"expiry_read_fail": "Impossible de lire la date.",
"expiry_raw_label": "Lu"
},
"lowstock": {
"title": "⚠️ Stock faible !",
@@ -1189,7 +1268,8 @@
"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"
"vacuum_seal_rest": "🔒 Mettre le reste sous vide",
"moved_simple": "📦 Déplacé vers {location}"
},
"nova": {
"1": "Non transformé",
@@ -1424,7 +1504,12 @@
"token_autoconfig": "Configuration de l'accès...",
"token_prompt_title": "🔒 Jeton API",
"token_prompt_hint": "Saisissez la valeur API_TOKEN du fichier .env du serveur.",
"token_prompt_btn": "Continuer"
"token_prompt_btn": "Continuer",
"check_db_legacy": "Ancienne BD (dispensa.db)",
"check_tts": "URL synthèse vocale",
"check_scale": "Passerelle balance",
"critical_error_intro": "L'application ne peut pas démarrer en raison des problèmes suivants :",
"error_network_detail": "Le navigateur ne peut pas joindre le serveur PHP.\n\nCauses possibles :\n• Apache/PHP n'est pas démarré\n• Problème réseau ou pare-feu\n• URL incorrecte\n\nDémarrez le serveur et réessayez."
},
"stats_monthly": {
"title": "Statistiques Mensuelles",
@@ -1437,5 +1522,12 @@
"top_used": "le plus utilisé",
"top_cats": "Catégories principales",
"source": "Historique des transactions · mois en cours"
},
"time": {
"just_now": "à l'instant",
"seconds_ago": "il y a {n}s",
"minutes_ago": "il y a {n} min",
"hours_ago": "il y a {n} h",
"days_ago": "il y a {n} j"
}
}
}
+47 -12
View File
@@ -143,8 +143,10 @@
"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_vanished": "Il prodotto non compare più in inventario, 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_finished_action_restore": "Ripristina {qty} {unit}",
"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",
@@ -164,7 +166,11 @@
"banner_opened_detail": "{when} in {location} · hai ancora <strong>{qty}</strong>.",
"banner_explain_title": "Chiedi a Gemini una spiegazione",
"banner_explain_btn": "Spiega",
"banner_analyzing": "🤖 Analizzo…"
"banner_analyzing": "🤖 Analizzo…",
"banner_prediction_confirmed": "✅ Confermato — il sistema ricalcolerà le previsioni dalle prossime registrazioni",
"banner_anomaly_explain_fail": "Impossibile ottenere spiegazione AI",
"banner_anomaly_dismissed": "Anomalia ignorata",
"banner_finished_restore_prompt": "Quante {unit} di {name} hai ancora? (stima sistema: {qty})"
},
"inventory": {
"title": "Dispensa",
@@ -243,7 +249,8 @@
"ai_match_none": "Nessun prodotto simile trovato in dispensa.",
"ai_match_use_btn": "Usa questo",
"ai_match_add_btn": "Aggiungi \"{name}\"",
"ai_detected_label": "AI ha trovato"
"ai_detected_label": "AI ha trovato",
"mode_shopping_activated": "🛒 Modalità Spesa attivata!"
},
"action": {
"title": "Cosa vuoi fare?",
@@ -316,14 +323,17 @@
"toast_bring": "🛒 Prodotto finito → aggiunto a Bring!",
"toast_opened_finished": "🔓 Confezione aperta di {name} finita!",
"disambiguation_hint": "Cosa intendi con \"finito tutto\"?",
"disambiguation_one_conf": "Finita <strong>1 confezione</strong> ({qty})",
"disambiguation_all": "🗑️ Finito TUTTO ({qty})",
"toast_one_conf_finished": "📦 1 confezione di {name} terminata!",
"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"
"throw_all_confirm_btn": "🗑️ Sì, butta",
"locations_short": "posti"
},
"product": {
"title_new": "Nuovo Prodotto",
@@ -363,7 +373,9 @@
"weight_label": "Peso",
"origin_label": "Origine",
"labels_label": "Etichette",
"select_variant": "Seleziona la variante esatta o usa i dati AI:"
"select_variant": "Seleziona la variante esatta o usa i dati AI:",
"history_badge": "📊 storico",
"from_history": " (da storico)"
},
"products": {
"title": "📦 Tutti i Prodotti",
@@ -425,7 +437,9 @@
"nutrition_per_serving": "Valori stimati per porzione",
"storage_title": "Come conservare gli avanzi",
"storage_days": "{n} giorni",
"storage_immediately": "Da consumare subito"
"storage_immediately": "Da consumare subito",
"ing_stock_line": "Hai {have} · restano {remain} dopo l'uso",
"ing_use_all_note": "uso totale (<5% della confezione intera)"
},
"shopping": {
"title": "🛒 Lista della Spesa",
@@ -512,6 +526,7 @@
"remove_error": "Errore nella rimozione",
"btn_fetch_prices": "Cerca i prezzi",
"price_total_label": "💰 Spesa stimata:",
"price_total_short": "spesa stimata",
"price_loading": "Ricerca prezzi…",
"price_not_found": "prezzo n/d",
"suggest_loading": "Analisi in corso...",
@@ -521,7 +536,8 @@
"priority_low": "Bassa",
"smart_last_update": "Aggiornato {time}",
"names_already_updated": "Tutti i nomi sono già aggiornati",
"pantry_hint": "Hai gia {qty} in dispensa"
"pantry_hint": "Hai gia {qty} in dispensa",
"bring_names_migrated": "🔄 {n} nomi generalizzati in Bring!"
},
"ai": {
"title": "🤖 Identificazione AI",
@@ -532,7 +548,8 @@
"no_api_key": "⚠️ Chiave API Gemini non configurata.\n<small>Aggiungi GEMINI_API_KEY nel file .env sul server.</small>",
"fields_filled": "✅ Campi compilati dall'AI",
"use_data": "✅ Usa dati AI",
"use_data_no_barcode": "✅ Usa dati AI (senza barcode)"
"use_data_no_barcode": "✅ Usa dati AI (senza barcode)",
"conservation_hint": "🤖 AI: conserva in {location}"
},
"log": {
"title": "📒 Storico",
@@ -787,7 +804,13 @@
"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": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Protocolli BLE supportati:</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; peso, grasso, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generico &mdash; heuristica automatica su 100+ modelli</li></ul>"
"ble_protocols": "<p style=\"margin:0 0 6px;font-weight:600\">🔌 Protocolli BLE supportati:</p><ul style=\"margin:0 0 0 16px;padding:0;font-size:0.8rem\"><li>Bluetooth SIG Weight Scale (0x181D)</li><li>Bluetooth SIG Body Composition (0x181B) &mdash; peso, grasso, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generico &mdash; heuristica automatica su 100+ modelli</li></ul>",
"discover_scanning": "🔍 Scansione rete locale per gateway bilancia…",
"discover_found": "✅ Gateway trovato: {url}{more}",
"discover_not_found": "❌ Nessun gateway su {subnet}. Avvia l'app Android sulla stessa Wi-Fi.",
"discover_failed": "❌ Ricerca fallita: {error}",
"discover_auto": "🔍 Auto",
"unknown_device": "Dispositivo sconosciuto"
},
"kiosk": {
"hint": "Trasforma un tablet Android in un pannello EverShelf sempre acceso, con bilancia BLE integrata.",
@@ -974,7 +997,8 @@
"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."
}
},
"kiosk_update_required": "⚠️ Aggiorna il kiosk per usare questa funzione"
},
"expiry": {
"today": "OGGI",
@@ -1048,6 +1072,7 @@
"finished_all": "📤 {name} terminato!",
"vacuum_sealed": "{name} salvato come sottovuoto",
"product_finished_confirmed": "✅ Rimosso — riaggiungi quando ne ricompri",
"ghost_restored": "✅ {name}: ripristinati {qty} {unit} in inventario",
"appliance_added": "Elettrodomestico aggiunto",
"item_added": "{name} aggiunto"
},
@@ -1119,7 +1144,9 @@
"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"
"offline_cache_ready": "Offline — {n} prodotti in cache",
"copy_failed": "Copia negli appunti non riuscita",
"invalid_quantity": "Quantità non valida"
},
"confirm": {
"remove_item": "Vuoi davvero rimuovere questo prodotto dall'inventario?",
@@ -1240,7 +1267,8 @@
"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"
"vacuum_seal_rest": "Metti sotto vuoto il resto",
"moved_simple": "📦 Spostato in {location}"
},
"nova": {
"1": "Non trasformato",
@@ -1493,5 +1521,12 @@
"top_used": "più usato",
"top_cats": "Categorie principali",
"source": "Storico transazioni · mese corrente"
},
"time": {
"just_now": "adesso",
"seconds_ago": "{n}s fa",
"minutes_ago": "{n} min fa",
"hours_ago": "{n} h fa",
"days_ago": "{n} gg fa"
}
}
}