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:
+47
-12
@@ -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) — Gewicht, Fett, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generisch — 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) — Gewicht, Fett, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generisch — 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
@@ -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) — weight, fat, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generic — 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) — weight, fat, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generic — 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
@@ -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) — peso, grasa, IMC</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Genérico — 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) — peso, grasa, IMC</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Genérico — 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
@@ -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) — poids, graisse, IMC</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Générique — 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) — poids, graisse, IMC</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Générique — 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
@@ -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) — peso, grasso, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generico — 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) — peso, grasso, BMI</li><li>Xiaomi Mi Body Composition Scale 2</li><li>Generico — 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user