diff --git a/CHANGELOG.md b/CHANGELOG.md
index d7aec84..37dd1a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Recipe scraps tips** — During cooking steps, detect "waste" generated (peels, cores, bones, eggshells, coffee grounds, citrus zest, etc.) and surface AI-powered tips on how to reuse them (compost, natural cleaner, broth, candied peel, etc.). Could be shown as an optional collapsible hint card below the step that generates the scrap.
+## [1.7.34] - 2026-05-30
+
+### Added
+- **AI visual barcode fallback** — When the barcode scanner fails to read a barcode within 5 seconds, EverShelf can now automatically capture a camera frame and send it to Gemini Vision to visually identify the product (name, brand, category). On success the product is saved and the inventory form opens just as if a barcode had been scanned. A new toggle in **Settings → Camera** (`AI visual identification (5s fallback)`) lets users enable or disable this feature at any time. Requires Gemini API key configured. Disabled by default.
+
## [1.7.33] - 2026-05-29
### Fixed
diff --git a/api/index.php b/api/index.php
index f28a7c4..775f626 100644
--- a/api/index.php
+++ b/api/index.php
@@ -604,7 +604,7 @@ function checkRateLimit(string $action): void {
}
// Determine limit based on action
- $aiActions = ['gemini_readExpiry', 'gemini_chat', 'gemini_identify', 'gemini_suggest_shopping', 'chat_to_recipe', 'recipe_from_ingredient', 'gemini_number_ocr'];
+ $aiActions = ['gemini_readExpiry', 'gemini_chat', 'gemini_identify', 'gemini_suggest_shopping', 'chat_to_recipe', 'recipe_from_ingredient', 'gemini_number_ocr', 'gemini_barcode_visual'];
$loginActions = [];
$recipeActions = ['generate_recipe', 'generate_recipe_stream'];
$errorActions = ['report_error', 'check_update'];
@@ -1109,6 +1109,10 @@ try {
geminiNumberOCR();
break;
+ case 'gemini_barcode_visual':
+ geminiBarcodeVisual();
+ break;
+
case 'get_shopping_price':
getShoppingPrice($db);
break;
@@ -4353,6 +4357,7 @@ function getServerSettings(): void {
'shopping_forecast' => env('SHOPPING_FORECAST', 'true') === 'true',
'shopping_auto_add_threshold' => (int)env('SHOPPING_AUTO_ADD_THRESHOLD', '0'),
'dark_mode' => env('DARK_MODE', 'auto'),
+ 'barcode_ai_fallback' => env('BARCODE_AI_FALLBACK', 'false') === 'true',
// Home Assistant Integration
'ha_enabled' => env('HA_ENABLED', 'false') === 'true',
'ha_url' => env('HA_URL', ''),
@@ -4455,6 +4460,7 @@ function saveSettings(): void {
'shopping_enabled' => 'SHOPPING_ENABLED',
'shopping_smart_suggestions' => 'SHOPPING_SMART_SUGGESTIONS',
'shopping_forecast' => 'SHOPPING_FORECAST',
+ 'barcode_ai_fallback' => 'BARCODE_AI_FALLBACK',
// Home Assistant
'ha_enabled' => 'HA_ENABLED',
];
@@ -10494,6 +10500,101 @@ function geminiNumberOCR(): void {
}
}
+// =============================================================================
+// ===== GEMINI AI: BARCODE VISUAL FALLBACK ====================================
+// =============================================================================
+/**
+ * POST /api/?action=gemini_barcode_visual
+ * Body: { image: base64-jpeg, lang: 'it'|'en'|'de'|... }
+ * Returns: { found, source, product } or { found: false, error }
+ * Uses Gemini vision to visually identify a product from a camera frame
+ * when the barcode scanner fails to read the barcode after 5 seconds.
+ */
+function geminiBarcodeVisual(): void {
+ EverLog::info('geminiBarcodeVisual');
+ $apiKey = env('GEMINI_API_KEY');
+ if (empty($apiKey)) {
+ echo json_encode(['found' => false, 'error' => 'no_api_key']);
+ return;
+ }
+
+ $input = json_decode(file_get_contents('php://input'), true) ?? [];
+ $imageBase64 = $input['image'] ?? '';
+ $lang = $input['lang'] ?? 'it';
+ if (empty($imageBase64)) {
+ echo json_encode(['found' => false, 'error' => 'no_image']);
+ return;
+ }
+
+ $langNote = match($lang) {
+ 'de' => 'Use the German product name if known.',
+ 'fr' => 'Use the French product name if known.',
+ 'es' => 'Use the Spanish product name if known.',
+ default => 'Use the Italian product name if known.',
+ };
+
+ $payload = [
+ 'contents' => [[
+ 'parts' => [
+ ['text' => "Identify the product shown in this image. {$langNote}\n" .
+ "Respond with ONLY valid JSON (no markdown, no backticks):\n" .
+ "{\"name\":\"...\",\"brand\":\"...\",\"category\":\"...\"}\n" .
+ "- name: the product name (as specific as possible, not just the brand)\n" .
+ "- brand: the brand/manufacturer, or empty string if not visible\n" .
+ "- category: one of: latticini, pasta, bevande, snack, carne, pesce, " .
+ "frutta, verdura, surgelati, condimenti, conserve, cereali, pane, " .
+ "igiene, pulizia, altro\n" .
+ "If you cannot identify the product at all, respond with: {\"unknown\":true}"],
+ ['inline_data' => ['mime_type' => 'image/jpeg', 'data' => $imageBase64]],
+ ],
+ ]],
+ 'generationConfig' => [
+ 'temperature' => 0,
+ 'maxOutputTokens' => 200,
+ 'responseMimeType' => 'application/json',
+ 'thinkingConfig' => ['thinkingBudget' => 0],
+ ],
+ ];
+
+ $result = callGeminiWithFallback($apiKey, $payload, 15, 'barcode_visual');
+ if ($result['http_code'] !== 200) {
+ echo json_encode(['found' => false, 'error' => 'gemini_error_' . $result['http_code']]);
+ return;
+ }
+
+ $text = trim($result['data']['candidates'][0]['content']['parts'][0]['text'] ?? '');
+ // Strip accidental markdown fences
+ $text = preg_replace('/^```json\s*/i', '', $text);
+ $text = preg_replace('/\s*```$/i', '', trim($text));
+
+ $data = json_decode($text, true);
+ if (!$data || !empty($data['unknown']) || empty($data['name'])) {
+ echo json_encode(['found' => false]);
+ return;
+ }
+
+ echo json_encode([
+ 'found' => true,
+ 'source' => 'gemini_visual',
+ 'product' => [
+ 'name' => $data['name'] ?? '',
+ 'brand' => $data['brand'] ?? '',
+ 'category' => $data['category'] ?? '',
+ 'image_url' => '',
+ 'quantity_info' => '',
+ 'nutriscore' => '',
+ 'ingredients' => '',
+ 'allergens' => '',
+ 'conservation' => '',
+ 'origin' => '',
+ 'nova_group' => '',
+ 'ecoscore' => '',
+ 'labels' => '',
+ 'stores' => '',
+ ],
+ ], JSON_UNESCAPED_UNICODE);
+}
+
// =============================================================================
// ===== GEMINI AI: ANOMALY EXPLANATION =======================================
// =============================================================================
diff --git a/assets/js/app.js b/assets/js/app.js
index 34d03d3..9c28947 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -1943,6 +1943,7 @@ let quaggaRunning = false;
let aiStream = null;
let _scanZoomLevel = 2; // always 2x
let _torchActive = false;
+let _aiFallbackTimer = null;
// Apply fixed 2x zoom (hardware if available, CSS fallback)
async function _applyFixedZoom() {
@@ -2111,6 +2112,85 @@ async function _tryGeminiNumberOCR() {
}
}
+// ===== AI VISUAL PRODUCT IDENTIFICATION (auto-fallback after 5s) =====
+let _aiBarcodeVisualRunning = false;
+async function _tryGeminiVisualBarcode() {
+ if (_aiBarcodeVisualRunning || !_requireGemini()) return;
+ const video = document.getElementById('scanner-video');
+ if (!video || !video.videoWidth) return;
+
+ _aiBarcodeVisualRunning = true;
+ stopScanner(); // stop scanner loop while AI processes
+ _setScanStatus(t('scan.ai_fallback_searching'), 'retry', 'Gemini Vision');
+ showLoading(true);
+
+ try {
+ const canvas = document.createElement('canvas');
+ canvas.width = video.videoWidth;
+ canvas.height = video.videoHeight;
+ canvas.getContext('2d').drawImage(video, 0, 0);
+ const imageBase64 = canvas.toDataURL('image/jpeg', 0.88).split(',')[1];
+
+ const result = await api('gemini_barcode_visual', {}, 'POST', {
+ image: imageBase64,
+ lang: _currentLang || 'it',
+ });
+
+ if (result.found && result.product) {
+ const p = result.product;
+ scanLog(`AI visual: found "${p.name}" (${p.brand})`);
+ showToast(t('scan.ai_fallback_found'), 'success');
+ // Build a synthetic product (no barcode) and show the inventory form
+ const saveResult = await api('product_save', {}, 'POST', {
+ barcode: '',
+ name: p.name || t('product.not_recognized'),
+ brand: p.brand || '',
+ category: p.category || '',
+ image_url: '',
+ unit: 'pz',
+ default_quantity: 1,
+ package_unit: '',
+ notes: '',
+ });
+ if (saveResult.id) {
+ currentProduct = {
+ id: saveResult.id,
+ barcode: '',
+ name: p.name || t('product.not_recognized'),
+ brand: p.brand || '',
+ category: p.category || '',
+ image_url: '',
+ unit: 'pz',
+ default_quantity: 1,
+ package_unit: '',
+ _confCount: 0,
+ weight_info: '',
+ };
+ addToScanRecents(currentProduct);
+ showLoading(false);
+ setTimeout(() => showProductAction(), 300);
+ } else {
+ showLoading(false);
+ showToast(t('error.connection'), 'error');
+ }
+ } else {
+ scanLog('AI visual: product not identified');
+ showLoading(false);
+ showToast(t('scan.ai_fallback_not_found'), 'warning');
+ _setScanStatus(t('scan.status_ready'), '', '');
+ // Restart scanner so user can try again
+ setTimeout(() => initScanner(), 300);
+ }
+ } catch (e) {
+ scanLog(`AI visual error: ${e.message}`);
+ showLoading(false);
+ showToast(t('error.connection'), 'error');
+ setTimeout(() => initScanner(), 300);
+ } finally {
+ _aiBarcodeVisualRunning = false;
+ }
+}
+
// ===== CAMERA HELPER =====
function getCameraConstraints(extraVideo = {}) {
const s = getSettings();
@@ -2296,6 +2376,7 @@ function _applySyncedSettings(serverSettings) {
'shopping_enabled','shopping_mode','shopping_smart_suggestions',
'shopping_forecast','shopping_auto_add_threshold',
'dark_mode',
+ 'barcode_ai_fallback',
// Home Assistant
'ha_enabled','ha_url','ha_tts_entity','ha_webhook_id','ha_webhook_events',
'ha_notify_service','ha_expiry_days'];
@@ -2887,6 +2968,8 @@ async function loadSettingsUI() {
const cameraSelect = document.getElementById('setting-camera-facing');
if (cameraSelect) cameraSelect.value = s.camera_facing || 'environment';
loadCameraDevices();
+ const baifEl = document.getElementById('setting-barcode-ai-fallback');
+ if (baifEl) baifEl.checked = s.barcode_ai_fallback === true;
renderAppliances(s.appliances || []);
const mealPlanEnabled = s.meal_plan_enabled !== false;
const mpEnabledEl = document.getElementById('setting-meal-plan-enabled');
@@ -3465,6 +3548,8 @@ async function saveSettings() {
s.dietary = document.getElementById('setting-dietary').value.trim();
// Camera
s.camera_facing = document.getElementById('setting-camera-facing').value;
+ const baifSave = document.getElementById('setting-barcode-ai-fallback');
+ if (baifSave) s.barcode_ai_fallback = baifSave.checked;
// Screensaver
const ssEl = document.getElementById('setting-screensaver-enabled');
if (ssEl) s.screensaver_enabled = ssEl.checked;
@@ -3608,6 +3693,7 @@ async function saveSettings() {
shopping_forecast: s.shopping_forecast !== false,
shopping_auto_add_threshold: s.shopping_auto_add_threshold || 0,
dark_mode: s.dark_mode || 'auto',
+ barcode_ai_fallback: !!s.barcode_ai_fallback,
// Home Assistant
ha_enabled: !!s.ha_enabled,
ha_url: s.ha_url || '',
@@ -6527,6 +6613,17 @@ async function initScanner() {
}
}, 4000);
}
+
+ // After 5s without a scan, auto-trigger AI visual identification (if enabled)
+ if (_geminiAvailable && getSettings().barcode_ai_fallback) {
+ clearTimeout(_aiFallbackTimer);
+ _aiFallbackTimer = setTimeout(() => {
+ if (scannerStream) { // still scanning — no barcode found yet
+ scanLog('5s elapsed without barcode — triggering AI visual fallback');
+ _tryGeminiVisualBarcode();
+ }
+ }, 5000);
+ }
} catch (err) {
scanLog(`CAMERA ERROR: ${err.name}: ${err.message}`);
@@ -6850,6 +6947,7 @@ function stopScanner() {
quaggaRunning = false;
_scanZoomLevel = 2; // always 2x on next start
_torchActive = false;
+ clearTimeout(_aiFallbackTimer); _aiFallbackTimer = null;
if (scannerStream) {
scannerStream.getTracks().forEach(t => t.stop());
scannerStream = null;
@@ -6871,6 +6969,7 @@ function stopScanner() {
}
async function onBarcodeDetected(barcode) {
+ clearTimeout(_aiFallbackTimer); _aiFallbackTimer = null;
showLoading(true);
// Vibrate if available
diff --git a/index.html b/index.html
index 74cb570..f142861 100644
--- a/index.html
+++ b/index.html
@@ -1183,6 +1183,16 @@
Se hai più fotocamere, puoi selezionarne una specifica dall'elenco sopra dopo aver concesso i permessi.
+
+
+
Se il codice a barre non viene letto entro 5 secondi, un fotogramma viene inviato automaticamente all'AI per identificare visivamente il prodotto. Richiede Gemini configurato.
+
diff --git a/translations/de.json b/translations/de.json
index 8570134..13492c0 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -1,1458 +1,1463 @@
{
- "app": {
- "name": "EverShelf",
- "loading": "Laden..."
- },
- "nav": {
- "title": "EverShelf",
- "home": "Home",
- "inventory": "Vorrat",
- "recipes": "Rezepte",
- "shopping": "Einkauf",
- "log": "Verlauf",
- "settings": "Einstellungen"
- },
- "btn": {
- "back": "← Zurück",
- "save": "💾 Speichern",
- "cancel": "✕ Abbrechen",
- "close": "Schließen",
- "add": "✅ Hinzufügen",
- "delete": "Löschen",
- "edit": "✏️ Bearbeiten",
- "use": "Verwenden",
- "edit_item": "Bearbeiten",
- "search": "🔍 Suchen",
- "go": "✅ Los",
- "toggle_password": "👁️ Anzeigen/Ausblenden",
- "load_more": "Mehr laden...",
- "save_config": "💾 Konfiguration speichern",
- "save_product": "💾 Produkt speichern",
- "restart": "↺ Neustart",
- "reset_default": "↺ Standard wiederherstellen",
- "save_info": "💾 Info speichern",
- "retry": "🔄 Erneut versuchen",
- "yes_short": "Ja",
- "no_short": "Nein"
- },
- "form": {
- "select_placeholder": "-- Auswählen --"
- },
- "locations": {
- "dispensa": "Vorratskammer",
- "frigo": "Kühlschrank",
- "freezer": "Gefrierschrank",
- "altro": "Sonstiges"
- },
- "categories": {
- "latticini": "Milchprodukte",
- "carne": "Fleisch",
- "pesce": "Fisch",
- "frutta": "Obst",
- "verdura": "Gemüse",
- "pasta": "Pasta & Reis",
- "pane": "Brot & Backwaren",
- "surgelati": "Tiefkühl",
- "bevande": "Getränke",
- "condimenti": "Gewürze",
- "snack": "Snacks & Süßes",
- "conserve": "Konserven",
- "cereali": "Getreide & Hülsenfrüchte",
- "igiene": "Hygiene",
- "pulizia": "Reinigung",
- "altro": "Sonstiges",
- "select": "-- Auswählen --"
- },
- "units": {
- "pz": "Stk",
- "conf": "Pkg",
- "g": "g",
- "ml": "ml",
- "pieces": "Stück",
- "grams": "Gramm",
- "box": "Packung",
- "boxes": "Packungen",
- "millilitres": "Milliliter",
- "from": "von"
- },
- "shopping_sections": {
- "frutta_verdura": "Obst & Gemüse",
- "carne_pesce": "Fleisch & Fisch",
- "latticini": "Milchprodukte & Frisches",
- "pane_dolci": "Brot & Süßes",
- "pasta": "Pasta & Getreide",
- "conserve": "Konserven & Soßen",
- "surgelati": "Tiefkühl",
- "bevande": "Getränke",
- "pulizia_igiene": "Reinigung & Hygiene",
- "altro": "Sonstiges"
- },
- "dashboard": {
- "expired_title": "🚫 Abgelaufen",
- "expiring_title": "⏰ Bald ablaufend",
- "stats_period": "📊 Letzte 30 Tage",
- "opened_title": "📦 Geöffnete Produkte",
- "review_title": "🔍 Zu prüfen",
- "review_hint": "Mengen, die ungewöhnlich erscheinen. Bestätigen oder ändern.",
- "quick_recipe": "Schnelles Rezept mit ablaufenden Produkten",
- "banner_review_title": "Ungewöhnliche Menge",
- "banner_review_action_ok": "Ist korrekt",
- "banner_review_action_finish": "🗑️ Alles aufgebraucht",
- "banner_review_action_edit": "Korrigieren",
- "banner_review_action_weigh": "Wiegen",
- "banner_review_dismiss": "Ignorieren",
- "banner_prediction_title": "Verbrauch zur Prüfung",
- "banner_prediction_hint": "Die Verbrauchsschätzung passt sich aktuellen Daten an: bitte nur die aktuelle Menge bestätigen.",
- "banner_prediction_action_confirm": "{qty} {unit} bestätigen",
- "banner_prediction_action_weigh": "Jetzt wiegen",
- "banner_prediction_action_edit": "Menge aktualisieren",
- "banner_expired_title": "Abgelaufenes Produkt",
- "banner_expired_today": "Heute abgelaufen",
- "banner_expired_days": "Seit {days} Tagen abgelaufen",
- "banner_expired_action_use": "Trotzdem verwenden",
- "banner_expired_action_finished": "Habe ich verbraucht!",
- "banner_expired_action_throw": "Habe ich weggeworfen",
- "banner_expired_action_edit": "Datum korrigieren",
- "banner_expired_action_modify": "Bearbeiten",
- "banner_expired_action_vacuum": "Vakuumieren",
- "banner_anomaly_action_edit": "Bestand korrigieren",
- "banner_anomaly_action_dismiss": "Menge ist korrekt",
- "banner_no_expiry_title": "Ablaufdatum fehlt: {name}",
- "banner_no_expiry_detail": "Dieses Produkt hat kein Ablaufdatum. Möchten Sie eines hinzufügen oder bestätigen, dass es nicht verfällt?",
- "banner_no_expiry_action_set": "Ablaufdatum setzen",
- "banner_no_expiry_action_dismiss": "Läuft nicht ab ✓",
- "banner_no_expiry_toast_dismissed": "Als 'läuft nicht ab' markiert",
- "banner_expiring_title": "Bald ablaufend",
- "banner_expiring_today": "Läuft heute ab!",
- "banner_expiring_tomorrow": "Läuft morgen ab",
- "banner_expiring_days": "Läuft in {days} Tagen ab",
- "banner_expiring_action_use": "Jetzt verwenden",
- "banner_finished_title": "aufgebraucht?",
- "banner_finished_detail": "Ich habe vermerkt, dass {name} auf null gesunken ist. Ist es wirklich leer, oder hast du noch welches?",
- "banner_finished_action_yes": "Ja, aufgebraucht",
- "banner_finished_action_no": "Nein, ich habe noch welches",
- "banner_review_unusual_pkg_title": "Ungewöhnliche Packungsgröße",
- "banner_review_unusual_pkg_detail": "Du hast eine Packung von {qty} {unit} eingestellt — die Größe scheint sehr groß. Überprüfe ob es korrekt ist.",
- "banner_review_low_qty_title": "Sehr geringe Menge",
- "banner_review_low_qty_detail": "Du hast nur {qty} im Bestand — das scheint sehr wenig, möglicherweise ein Eingabefehler. Bestätige wenn korrekt.",
- "banner_review_high_qty_title": "Ungewöhnlich hohe Menge",
- "banner_review_high_qty_detail": "Du hast {qty} im Bestand — die Zahl scheint sehr hoch. Bestätige wenn korrekt oder korrigiere.",
- "banner_prediction_rate_day": "Durchschnitt ~{n} {unit}/Tag",
- "banner_prediction_rate_week": "Durchschnitt ~{n} {unit}/Woche",
- "banner_prediction_days_ago": "Vor {n} Tagen aufgefüllt",
- "banner_prediction_more": "frühere Schätzung: {expected} {unit}{time}; aktuelle Menge: {actual} {unit}.",
- "banner_prediction_less": "Schätzung: {expected} {unit}{time}; aktuelle Menge: {actual} {unit}. Wenn sich dein Verbrauch geändert hat, aktualisiert sich die Prognose automatisch.",
- "banner_finished_zero": "Bestand zeigt null, aber gespeicherte Buchungen deuten an, dass es nicht leer sein sollte.",
- "banner_finished_expected": "Laut Aufzeichnungen solltest du noch {qty} {unit} haben.",
- "banner_finished_check": "Kannst du nachschauen?",
- "banner_anomaly_phantom_title": "mehr Bestand als erwartet",
- "banner_anomaly_phantom_detail": "Bestand zeigt {inv_qty} {unit}, aber laut Buchungen solltest du nur {expected_qty} {unit} haben. Hast du Bestand ohne Buchung hinzugefügt?",
- "banner_anomaly_untracked_title": "Anfangsbestand nicht als Eingang gebucht",
- "banner_anomaly_untracked_detail": "Du hast {inv_qty} {unit} im Bestand, aber die gebuchten Abgänge übersteigen die Eingänge — der Anfangsbestand wurde wahrscheinlich nie als \"Eingang\" erfasst. Bitte korrigiere die Menge oder trage die fehlenden Eingänge nach.",
- "banner_anomaly_ghost_title": "weniger Bestand als erwartet",
- "banner_anomaly_ghost_detail": "Laut Buchungen solltest du {expected_qty} {unit} von {name} haben, aber der Bestand zeigt nur {inv_qty} {unit}. Hast du etwas ohne Buchung entnommen?",
- "consumed": "Verbraucht: {n} ({pct}%)",
- "wasted": "Weggeworfen: {n} ({pct}%)",
- "more_opened": "und {n} weitere geöffnet...",
- "banner_expired_detail": "{when} · du hast noch {qty}.",
- "banner_opened_detail": "{when} in {location} · du hast noch {qty}.",
- "banner_explain_title": "Gemini um eine Erklärung bitten",
- "banner_explain_btn": "Erklären",
- "banner_analyzing": "🤖 Analysiere…"
- },
- "inventory": {
- "title": "Vorrat",
- "filter_all": "Alle",
- "search_placeholder": "🔍 Produkt suchen...",
- "recent_title": "🕐 Zuletzt verwendet",
- "popular_title": "⭐ Meistverwendet",
- "empty": "Keine Produkte hier.\nScanne ein Produkt, um es hinzuzufügen!",
- "no_items_found": "Keine Bestandseinträge gefunden",
- "qty_remainder_suffix": "übrig",
- "vacuum_badge": "🫙 Vakuumiert",
- "opened_badge": "📭 Geöffnet",
- "label_expiry": "📅 Ablaufdatum",
- "label_storage": "🫙 Aufbewahrung",
- "label_status": "📭 Status",
- "opened_since": "Geöffnet seit {date}",
- "label_position": "📍 Standort",
- "label_quantity": "📦 Menge",
- "label_added": "📅 Hinzugefügt",
- "empty_text": "Keine Produkte hier. Scanne ein Produkt, um es hinzuzufügen!",
- "empty_db": "Keine Produkte in der Datenbank. Scanne ein Produkt, um loszulegen!",
- "qty_trace": "< 1"
- },
- "scan": {
- "title": "Scannen",
- "mode_shopping": "🛒 Einkaufsmodus",
- "mode_shopping_end": "✅ Einkauf beenden",
- "spesa_btn": "🛒 Einkauf",
- "zoom": "Zoom",
- "tab_barcode": "Barcode",
- "tab_name": "Name",
- "tab_ai": "KI",
- "recents_label": "Zuletzt",
- "torch_hint": "Taschenlampe",
- "torch_on": "Taschenlampe an",
- "torch_off": "Taschenlampe aus",
- "torch_unavailable": "Taschenlampe auf diesem Gerät nicht verfügbar",
- "flip_hint": "Kamera wechseln",
- "flip_front": "Frontkamera",
- "flip_back": "Rückkamera",
- "num_ocr_btn": "🔢 Zahlen mit KI lesen",
- "num_ocr_searching": "Suche Barcode mit KI...",
- "num_ocr_found": "Code gefunden: {code}",
- "num_ocr_not_found": "Kein Barcode im Bild gefunden",
- "barcode_placeholder": "Barcode eingeben...",
- "quick_name_divider": "oder Name eingeben",
- "quick_name_placeholder": "z.B.: Äpfel, Zucchini, Brot...",
- "manual_entry": "✏️ Manuelle Eingabe",
- "ai_identify": "🤖 Mit KI identifizieren",
- "hint": "Barcode scannen, Produktname eingeben oder KI zur Identifikation nutzen",
- "debug_toggle": "🐛 Debug Log",
- "barcode_acquired": "🔖 Barcode gescannt: {code}",
- "scan_barcode": "🔖 Barcode scannen",
- "create_named": "{name} erstellen",
- "new_without_barcode": "Neues Produkt ohne Barcode",
- "stock_in_pantry": "Bereits im Vorrat:",
- "status_ready": "Kamera auf Barcode richten",
- "status_scanning": "Scanne...",
- "status_partial": "Erkannt: {code} — prüfe...",
- "status_invalid": "Ungültig: {code} — versuche erneut",
- "status_confirmed": "Bestätigt!",
- "status_parallel": "Kombinierter Scan aktiv..."
- },
- "action": {
- "title": "Was möchtest du tun?",
- "add_btn": "📥 HINZUFÜGEN",
- "add_sub": "in Vorrat/Kühlschrank",
- "use_btn": "VERWENDEN",
- "use_sub": "aus Vorrat/Kühlschrank",
- "have_title": "📦 Schon auf Lager!",
- "add_more_sub": "weitere Menge",
- "use_qty_sub": "wie viel verwendet",
- "throw_btn": "🗑️ ENTSORGEN",
- "throw_sub": "wegwerfen",
- "edit_sub": "Ablauf, Ort…",
- "create_recipe_btn": "Rezept",
- "related_stock_title": "Auch zuhause"
- },
- "add": {
- "title": "Zum Vorrat hinzufügen",
- "location_label": "📍 Wohin?",
- "quantity_label": "📦 Menge",
- "conf_size_label": "📦 Jede Packung enthält:",
- "conf_size_placeholder": "z.B. 300",
- "vacuum_label": "🫙 Vakuumiert",
- "vacuum_hint": "Ablaufdatum wird automatisch verlängert",
- "submit": "✅ Hinzufügen",
- "purchase_type_label": "🛒 Dieses Produkt ist...",
- "new_btn": "🆕 Gerade gekauft",
- "existing_btn": "📦 Hatte ich schon",
- "remaining_label": "📦 Verbleibende Menge",
- "remaining_hint": "Ungefähr wie viel ist noch übrig?",
- "remaining_full": "🟢 Voll",
- "remaining_half": "🟠 Halb",
- "estimated_expiry": "Geschätzte Haltbarkeit:",
- "suffix_freezer": "(Tiefkühler)",
- "suffix_vacuum": "(vakuumversiegelt)",
- "hint_modify": "📝 Du kannst das Datum ändern oder mit der Kamera scannen",
- "scan_expiry_title": "📷 Ablaufdatum scannen",
- "product_added": "✅ {name} hinzugefügt!{qty}",
- "suffix_freezer_vacuum": "(Tiefkühler + vakuumversiegelt)",
- "history_badge_tip": "Durchschnitt aus {n} früheren Einträgen",
- "vacuum_question": "Vakuumiert?",
- "vacuum_saved": "🔒 Als vakuumiert gespeichert"
- },
- "use": {
- "title": "Verwenden / Verbrauchen",
- "location_label": "📍 Woher?",
- "quantity_label": "Wie viel hast du benutzt?",
- "change": "ändern",
- "partial_hint": "Oder genaue Menge angeben:",
- "partial_piece_hint": "Hast du nur einen Teil verwendet?",
- "piece": "Stück",
- "one_whole": "1 ganzes",
- "use_all": "🗑️ ALLES verwendet / Aufgebraucht",
- "submit": "📤 Diese Menge verwenden",
- "available": "📦 Verfügbar:",
- "opened_badge": "GEOEFFNET",
- "not_in_inventory": "⚠️ Produkt nicht im Bestand.",
- "expiry_warning": "⚠️ Verwende zuerst die{loc}, die am {date} abläuft — {when}!",
- "expiry_warning_opened": "⚠️ Die{loc} ist seit {when} geöffnet — zuerst verwenden!",
- "throw_title": "🗑️ Produkt entsorgen",
- "throw_all": "🗑️ ALLES entsorgen ({qty})",
- "throw_qty_label": "Wie viel wegwerfen?",
- "throw_qty_hint": "oder Menge angeben:",
- "throw_partial_btn": "🗑️ Diese Menge entsorgen",
- "when_expired": "seit {n} Tagen abgelaufen",
- "when_today": "läuft heute ab",
- "when_tomorrow": "läuft morgen ab",
- "when_days": "läuft in {n} Tagen ab",
- "toast_used": "📤 {qty} von {name} verwendet",
- "toast_bring": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt",
- "toast_opened_finished": "🔓 Geöffnete Packung von {name} aufgebraucht!",
- "disambiguation_hint": "Was meinst du mit \"alles aufgebraucht\"?",
- "disambiguation_all": "🗑️ ALLES verbraucht ({qty})",
- "error_exceeds_stock": "⚠️ Du kannst nicht mehr verwenden als du verfügbar hast!",
- "use_all_confirm_title": "✅ Alles aufbrauchen",
- "use_all_confirm_msg": "Bestätige, dass du das Produkt vollständig aufgebraucht hast:",
- "use_all_confirm_btn": "✅ Ja, aufgebraucht",
- "throw_all_confirm_title": "🗑️ Alles entsorgen",
- "throw_all_confirm_msg": "Möchtest du wirklich das gesamte Produkt entsorgen?",
- "throw_all_confirm_btn": "🗑️ Ja, entsorgen"
- },
- "product": {
- "title_new": "Neues Produkt",
- "title_edit": "Produkt bearbeiten",
- "ai_fill": "📷 Foto machen und mit KI identifizieren",
- "ai_fill_hint": "KI füllt die Produktfelder automatisch aus",
- "name_label": "🏷️ Produktname *",
- "name_placeholder": "z.B.: Vollmilch, Penne Nudeln...",
- "brand_label": "🏢 Marke",
- "brand_placeholder": "z.B.: Barilla, Müller, Knorr...",
- "category_label": "📂 Kategorie",
- "unit_label": "📏 Maßeinheit",
- "default_qty_label": "🔢 Standardmenge",
- "conf_size_label": "📦 Jede Packung enthält:",
- "conf_size_placeholder": "z.B. 300",
- "notes_label": "📝 Notizen",
- "notes_placeholder": "z.B.: laktosefrei, bio, nach dem Öffnen im Kühlschrank aufbewahren...",
- "barcode_label": "🔖 Barcode",
- "barcode_placeholder": "Barcode (falls vorhanden)",
- "barcode_hint": "⚠️ Barcode hinzufügen, damit du beim nächsten Einkauf nur scannen musst!",
- "submit": "💾 Produkt speichern",
- "name_required": "Produktname eingeben",
- "conf_size_required": "Packungsinhalt angeben",
- "expiry_estimated": "Geschätztes Ablaufdatum:",
- "scan_expiry": "Ablaufdatum scannen",
- "expiry_hint": "📝 Du kannst das Datum ändern oder mit der Kamera scannen",
- "add_batch": "📦 + Charge mit anderem Ablaufdatum",
- "package_info": "📦 Packung: {info}",
- "edit_catalog": "⚙️ Produktinfo bearbeiten (Name, Marke, Kategorie…)",
- "not_recognized": "⚠️ Produkt nicht erkannt",
- "edit_info": "✏️ Informationen bearbeiten",
- "modify_details": "BEARBEITEN\nAblauf, Ort…",
- "already_in_pantry": "📋 Bereits im Vorratsschrank",
- "no_barcode": "Kein Barcode",
- "unknown_product": "Unbekanntes Produkt",
- "edit_name_brand": "Name/Marke bearbeiten",
- "weight_label": "Gewicht",
- "origin_label": "Herkunft",
- "labels_label": "Etiketten",
- "select_variant": "Genaue Variante auswählen oder KI-Daten nutzen:"
- },
- "products": {
- "title": "📦 Alle Produkte",
- "search_placeholder": "🔍 Produkt suchen...",
- "empty": "Keine Produkte in der Datenbank.\nScanne ein Produkt zum Starten!",
- "no_category": "Keine Produkte in dieser Kategorie"
- },
- "recipes": {
- "title": "🍳 Rezepte",
- "generate": "✨ Neues Rezept generieren",
- "archive_empty": "Keine Rezepte gespeichert. Erstelle dein erstes Rezept!",
- "dialog_title": "🍳 Rezept",
- "dialog_desc": "Ich erstelle ein gesundes Rezept mit Zutaten aus dem Vorrat und priorisiere Produkte mit nahendem Ablaufdatum.",
- "meal_label": "🕐 Für welche Mahlzeit?",
- "persons_label": "👥 Für wie viele Personen?",
- "meal_type_label": "🎯 Art der Mahlzeit",
- "opt_fast": "⚡ Schnelle Mahlzeit",
- "opt_light": "🥗 Kleiner Hunger",
- "opt_expiry": "⏰ Ablaufdaten priorisieren",
- "opt_healthy": "💚 Extra gesund",
- "opt_opened": "📦 Geöffnete Produkte priorisieren",
- "opt_zero_waste": "♻️ Zero Waste",
- "generate_btn": "✨ Rezept generieren",
- "loading_msg": "Rezept wird vorbereitet...",
- "start_cooking": "👨🍳 Kochmodus",
- "regenerate": "🔄 Noch eins generieren",
- "regen_choice_title": "Was möchtest du mit diesem Rezept machen?",
- "regen_replace": "🔄 Neues generieren (dieses verwerfen)",
- "regen_save_new": "💾 Im Archiv speichern & neues generieren",
- "close_btn": "✅ Schließen",
- "ingredients_title": "🧾 Zutaten",
- "tools_title": "Benötigte Geräte",
- "steps_title": "👨🍳 Zubereitung",
- "no_steps": "Keine Zubereitungsschritte verfügbar",
- "generate_error": "Fehler bei der Generierung",
- "stream_interrupted": "Generierung unterbrochen (unvollstaendige Antwort vom Server). Protokolle pruefen oder erneut versuchen.",
- "persons_short": "Pers.",
- "use_ingredient_title": "Zutat verwenden",
- "recipe_qty_label": "Rezept",
- "from_where_label": "Von wo?",
- "amount_label": "Wie viel",
- "use_amount_btn": "Diese Menge verwenden",
- "use_all_btn": "ALLES verwenden / Aufgebraucht",
- "packs_label": "Packungen",
- "quantity_in_total": "Menge in {unit} (gesamt: {total})",
- "packs_of_have": "Packungen à {size} (du hast {count} Pack.)",
- "scale_wait_stable": "10s stabiles Gewicht für Auto-Ausfüllen abwarten…",
- "ingredient_scaled_toast": "📦 Zutat vom Vorrat abgezogen!",
- "finished_added_bring_toast": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt!",
- "load_error": "Fehler beim Laden",
- "favorite": "Zu Favoriten hinzufügen",
- "unfavorite": "Aus Favoriten entfernen",
- "adjust_persons": "Personen"
- },
- "shopping": {
- "title": "🛒 Einkaufsliste",
- "bring_loading": "Verbindung zu Bring!...",
- "bring_not_configured": "Bring! ist nicht konfiguriert. Füge E-Mail und Passwort in den Einstellungen hinzu.",
- "tab_to_buy": "🛍️ Zu kaufen",
- "tab_forecast": "🧠 Vorhersage",
- "total_label": "💰 Geschätzter Gesamtbetrag",
- "section_to_buy": "🛍️ Zu kaufen",
- "suggestions_title": "💡 KI-Vorschläge",
- "suggestions_add": "✅ Ausgewählte zu Bring! hinzufügen",
- "search_prices": "🔍 Alle Preise suchen",
- "suggest_btn": "Einkaufsvorschläge",
- "smart_title": "🧠 Intelligente Vorhersagen",
- "smart_empty": "Keine Vorhersagen verfügbar. Füge Produkte zur Vorratskammer hinzu, um intelligente Vorhersagen zu erhalten.",
- "smart_filter_all": "Alle",
- "smart_filter_critical": "🔴 Dringend",
- "smart_filter_high": "🟠 Bald",
- "smart_filter_medium": "🟡 Planen",
- "smart_filter_low": "🟢 Vorhersage",
- "smart_add": "🛒 Ausgewählte zu Bring! hinzufügen",
- "empty": "Einkaufsliste leer!\nNutze den Button unten, um Vorschläge zu generieren.",
- "already_in_list": "🛒 \"{name}\" ist bereits in der Einkaufsliste",
- "already_in_list_short": "ℹ️ Bereits in der Einkaufsliste",
- "add_prompt": "Möchtest du es zur Einkaufsliste hinzufügen?",
- "smart_already": "📊 Intelligenter Einkauf sagt bereits {name} voraus",
- "all_searched": "Alle Produkte wurden bereits gesucht. Nutze 🔄 für einzelne Suchen.",
- "search_complete": "Suche abgeschlossen: {count} Produkte",
- "suggest_buy": "🛒 Kaufen: {qty} {unit}",
- "suggest_buy_approx": "🛒 Mindestens: {qty} {unit}",
- "suggest_buy_tip": "Empfohlene Menge basierend auf dem Verbrauch der letzten 14 Tage",
- "suggest_buy_approx_tip": "Mindestschätzung basierend auf Verbrauch (nächste Packungsgröße kaufen)",
- "removed_sufficient": "🧹 {removed} Produkt(e) mit ausreichendem Bestand von der Liste entfernt",
- "bring_badge": "🛒 Schon auf Bring!",
- "add_urgent_toast": "🔴 {n} dringende(s) Produkt(e) automatisch zu Bring! hinzugefügt",
- "migration_done": "✅ {migrated} aktualisiert, {skipped} bereits ok",
- "added_to_bring": "🛒 {n} Produkte zu Bring! hinzugefügt",
- "added_to_bring_skip": "{n} bereits vorhanden",
- "all_on_bring": "Alle Produkte waren bereits auf Bring!",
- "freq_high": "📈 Häufig",
- "freq_regular": "📊 Regelmäßig",
- "freq_occasional": "📉 Gelegentlich",
- "out_of_stock": "Ausverkauft",
- "scan_toast": "📷 Scannen: {name}",
- "empty_category": "Keine Produkte in dieser Kategorie",
- "session_empty": "🛒 Noch keine Produkte",
- "urgency_critical": "Dringend",
- "urgency_high": "Bald",
- "urgency_medium": "Planen",
- "urgency_low": "Vorschau",
- "urgency_medium_short": "Mittel",
- "urgency_low_short": "Ok",
- "tag_urgent": "🔴 Dringend",
- "tag_priority": "⭐ Priorität",
- "tag_check": "✅ Prüfen",
- "smart_already_predicted": "📊 Einkauf wird bereits vorhergesagt: {name}{urgency}.",
- "item_removed": "✅ {name} von der Liste entfernt!",
- "urgency_spec_critical": "⚡ Dringend",
- "urgency_spec_high": "🟠 Bald",
- "bring_add_n": "{n} zu Bring! hinzufügen",
- "bring_add_selected": "Ausgewählte zu Bring! hinzufügen",
- "bring_adding": "Wird hinzugefügt...",
- "bring_added_one": "1 Produkt zu Bring! hinzugefügt",
- "bring_added_many": "{n} Produkte zu Bring! hinzugefügt",
- "bring_skipped": "({n} bereits in Liste)",
- "force_sync": "Bring!-Synchronisierung erzwingen",
- "scan_target_label": "Du suchst",
- "scan_target_found": "Gefunden! Aus Liste entfernen",
- "bring_add_one": "1 Produkt zu Bring! hinzufügen",
- "bring_add_many": "{n} Produkte zu Bring! hinzufügen",
- "syncing": "Synchronisiere…",
- "sync_done": "Synchronisierung abgeschlossen",
- "price_searching": "Suche...",
- "search_action": "Suchen",
- "open_action": "Öffnen",
- "not_found": "Nicht gefunden",
- "search_price": "Preis suchen",
- "tap_to_scan": "Zum Scannen tippen",
- "tag_title": "Tag",
- "remove_title": "Entfernen",
- "found_count": "{found}/{total} Produkte gefunden",
- "savings_offers": "· 🏷️ Du sparst €{amount} mit Angeboten",
- "searching_progress": "Suche {current}/{total}...",
- "remove_error": "Fehler beim Entfernen",
- "btn_fetch_prices": "Preise suchen",
- "price_total_label": "💰 Geschätzter Gesamtpreis:",
- "price_loading": "Preise werden gesucht…",
- "price_not_found": "Preis n/v",
- "suggest_loading": "Analyse läuft...",
- "suggest_error": "Fehler bei der Vorschlagserstellung",
- "priority_high": "Hoch",
- "priority_medium": "Mittel",
- "priority_low": "Niedrig",
- "smart_last_update": "Aktualisiert {time}",
- "names_already_updated": "Alle Namen sind bereits aktuell",
- "pantry_hint": "Bereits zuhause: {qty}"
- },
- "ai": {
- "title": "🤖 KI-Identifikation",
- "capture": "📸 Foto aufnehmen",
- "retake": "🔄 Neu aufnehmen",
- "hint": "Mache ein Foto des Produkts und die KI versucht es zu identifizieren",
- "identifying": "🤖 Identifiziere Produkt...",
- "no_api_key": "⚠️ Gemini API-Schlüssel nicht konfiguriert.\nFüge GEMINI_API_KEY in der .env Datei auf dem Server hinzu.",
- "fields_filled": "✅ Felder von KI ausgefüllt",
- "use_data": "✅ KI-Daten verwenden",
- "use_data_no_barcode": "✅ KI-Daten verwenden (ohne Barcode)"
- },
- "log": {
- "title": "📒 Verlauf",
- "type_added": "Hinzugefügt",
- "type_waste": "Entsorgt",
- "type_used": "Verwendet",
- "type_bring": "Zu Bring! hinzugefügt",
- "undone_badge": "Rückgängig",
- "undo_title": "Diese Operation rückgängig machen",
- "load_error": "Fehler beim Laden des Verlaufs",
- "empty": "Keine Operationen aufgezeichnet.",
- "undo_action_remove": "Entfernen von",
- "undo_action_restore": "Wiederherstellen von",
- "undo_confirm": "Vorgang rückgängig machen?\n→ {action} {name}",
- "undo_success": "↩ Vorgang rückgängig gemacht für {name}",
- "already_undone": "Vorgang bereits rückgängig gemacht",
- "too_old": "Vorgänge älter als 24 Stunden können nicht rückgängig gemacht werden",
- "undo_error": "Fehler beim Rückgängigmachen",
- "recipe_prefix": "Rezept"
- },
- "chat": {
- "title": "Gemini Chef",
- "welcome": "Hallo! Ich bin dein Küchenassistent",
- "welcome_desc": "Frag mich, dir einen Saft, einen Snack, ein schnelles Gericht zu machen... Ich kenne deinen Vorrat, deine Geräte und deine Vorlieben!",
- "suggestion_snack": "🍿 Schneller Snack",
- "suggestion_juice": "🥤 Saft/Smoothie",
- "suggestion_light": "🥗 Etwas Leichtes",
- "suggestion_expiry": "⏰ Ablaufende nutzen",
- "clear": "Neues Gespräch",
- "placeholder": "Frag etwas...",
- "cleared": "Chat geleert",
- "suggestion_snack_text": "Was kann ich als schnellen Snack machen?",
- "suggestion_juice_text": "Mach mir einen Saft oder Smoothie mit dem was ich habe",
- "suggestion_light_text": "Ich habe Hunger, möchte aber etwas Leichtes",
- "suggestion_expiry_text": "Was läuft bald ab und wie kann ich es verwenden?",
- "transfer_to_recipes": "Zu Rezepten hinzufügen",
- "transferring": "Übertrage...",
- "transferred": "Zu Rezepten hinzugefügt!",
- "open_recipe": "Rezept öffnen",
- "quick_recipe_prompt": "Schlage mir ein schnelles Rezept FÜR EINE PERSON vor, das die Produkte mit dem nächsten Ablaufdatum verwendet! Ignoriere Tiefkühlprodukte, konzentriere dich auf Kühlschrank und Vorratsschrank."
- },
- "cooking": {
- "close": "Schließen",
- "tts_btn": "Vorlesen",
- "restart": "↺ Neustart",
- "replay": "🔊 Nochmal",
- "timer": "⏱️ {time} · Timer",
- "prev": "◀ Zurück",
- "next": "Weiter ▶",
- "ingredient_used": "✔️ Abgezogen",
- "ingredient_use_btn": "Usa",
- "ingredient_deduct_title": "Von Vorrat abziehen",
- "timer_expired_tts": "Timer {label} abgelaufen!",
- "timer_warning_tts": "Achtung! {label}: noch 10 Sekunden!",
- "recipe_done_tts": "Rezept abgeschlossen! Guten Appetit!",
- "expires_chip": "läuft ab {date}",
- "finish": "✅ Fertig",
- "step_fallback": "Schritt {n}",
- "zerowaste_label": "♻️ Abfall",
- "zerowaste_tip_title": "Zero-Waste-Tipp"
- },
- "settings": {
- "title": "⚙️ Einstellungen",
- "tab_api": "API Keys",
- "tab_bring": "Bring!",
- "tab_recipe": "Rezepte",
- "tab_mealplan": "Wochenplan",
- "tab_appliances": "Geräte",
- "tab_spesa": "Online-Einkauf",
- "tab_camera": "Kamera",
- "tab_security": "Sicherheit",
- "tab_tts": "Sprache (TTS)",
- "tab_language": "Sprache",
- "tab_scale": "Smart-Waage",
- "gemini": {
- "title": "🤖 Google Gemini AI",
- "hint": "API-Schlüssel für Produkterkennung, Ablaufdaten und Rezepte.",
- "key_label": "Gemini API Key"
- },
- "bring": {
- "title": "🛒 Bring! Einkaufsliste",
- "hint": "Zugangsdaten für die Bring! Einkaufslisten-Integration.",
- "email_label": "📧 Bring! E-Mail",
- "password_label": "🔒 Bring! Passwort"
- },
- "price": {
- "title": "💰 Preisschätzung (KI)",
- "hint": "Zeigt geschätzte Kosten pro Produkt in der Einkaufsliste mithilfe von KI an.",
- "enabled_label": "Preisschätzung aktivieren",
- "country_label": "🌍 Referenzland",
- "currency_label": "💱 Währung",
- "update_label": "🔄 Preise aktualisieren alle",
- "update_suffix": "Monate"
- },
- "recipe": {
- "title": "🍳 Rezept-Einstellungen",
- "hint": "Konfiguriere die Standardoptionen für die Rezeptgenerierung.",
- "persons_label": "👥 Standard-Portionen",
- "options_label": "🎯 Standard-Rezeptoptionen",
- "fast": "⚡ Schnelles Gericht",
- "light": "🥗 Leichte Mahlzeit",
- "expiry": "⏰ Ablauf-Priorität",
- "healthy": "💚 Extra Gesund",
- "opened": "📦 Offene Produkte zuerst",
- "zerowaste": "♻️ Keine Verschwendung",
- "dietary_label": "🚫 Unverträglichkeiten / Einschränkungen",
- "dietary_placeholder": "z.B.: glutenfrei, laktosefrei, vegetarisch..."
- },
- "mealplan": {
- "title": "📅 Wöchentlicher Essensplan",
- "hint": "Lege die Mahlzeitenart für jeden Tag fest. Wird als Leitfaden bei der Rezeptgenerierung verwendet.",
- "enabled": "✅ Wöchentlichen Essensplan aktivieren",
- "legend": "🌤️ = Mittagessen · 🌙 = Abendessen · Tippe auf ein Badge, um es zu ändern.",
- "types_title": "📋 Verfügbare Typen",
- "reset_btn": "↺ Standard wiederherstellen"
- },
- "appliances": {
- "title": "🔌 Verfügbare Geräte",
- "hint": "Gib an, welche Geräte du hast. Sie werden bei der Rezeptgenerierung berücksichtigt.",
- "new_placeholder": "z.B.: Brotbackmaschine, Thermomix, Heißluftfritteuse...",
- "quick_title": "Schnell hinzufügen:",
- "oven": "🔥 Backofen",
- "microwave": "📡 Mikrowelle",
- "air_fryer": "🍟 Heißluftfritteuse",
- "bread_maker": "🍞 Brotbackmaschine",
- "bimby": "🤖 Thermomix/Cookeo",
- "mixer": "🌀 Küchenmaschine",
- "steamer": "♨️ Dampfgarer",
- "pressure_cooker": "🫕 Schnellkochtopf",
- "toaster": "🍞 Toaster",
- "blender": "🍹 Mixer",
- "empty": "Keine Geräte hinzugefügt"
- },
- "spesa": {
- "title": "🛍️ Online-Einkauf",
- "hint": "Online-Einkaufsanbieter konfigurieren.",
- "provider_label": "🏪 Anbieter",
- "email_label": "📧 E-Mail",
- "password_label": "🔒 Passwort",
- "login_btn": "🔐 Anmelden",
- "ai_prompt_label": "🤖 KI-Produktauswahl Prompt",
- "ai_prompt_placeholder": "Anweisungen für die KI bei der Auswahl zwischen mehreren Produkten...",
- "ai_prompt_hint": "Die KI verwendet diesen Prompt zur Auswahl des passendsten Produkts. Leer lassen für Standardverhalten.",
- "configure_first": "Konfiguriere zuerst den Online-Einkauf in den Einstellungen",
- "missing_credentials": "E-Mail und Passwort eingeben",
- "login_in_progress": "Anmeldung läuft...",
- "login_error_prefix": "Fehler:",
- "login_network_error_prefix": "Netzwerkfehler:",
- "login_success_default": "Anmeldung erfolgreich!",
- "result_name_label": "Name",
- "result_card_label": "Karte",
- "result_pickup_label": "Abholpunkt",
- "result_points_label": "Treuepunkte",
- "connected_relogin": "✅ Verbunden — Erneut anmelden",
- "connected_as": "Verbunden als {name}"
- },
- "camera": {
- "title": "📷 Kamera",
- "hint": "Wähle die Kamera für Barcode-Scanning und KI-Identifikation.",
- "device_label": "📸 Standardkamera",
- "back": "📱 Rückkamera (Standard)",
- "front": "🤳 Frontkamera",
- "devices_hint": "Bei mehreren Kameras kannst du nach Freigabe der Berechtigungen eine bestimmte aus der Liste oben wählen.",
- "detect_btn": "🔄 Kameras erkennen"
- },
- "security": {
- "title": "🔒 HTTPS-Zertifikat",
- "hint": "Wenn der Browser den Fehler \"Verbindung nicht sicher\" (ERR_CERT_AUTHORITY_INVALID) zeigt, installiere das CA-Zertifikat auf dem Gerät.",
- "download_btn": "📥 CA-Zertifikat herunterladen",
- "token_title": "🔑 Einstellungs-Token",
- "token_label": "Zugriffstoken",
- "token_hint": "Falls `SETTINGS_TOKEN` in der Server-`.env` konfiguriert ist, gib hier den Token ein, bevor du die Einstellungen speicherst. Leer lassen, wenn nicht konfiguriert.",
- "token_placeholder": "(leer = kein Schutz)",
- "token_required_hint": "🔒 Dieser Server benötigt einen Token zum Speichern der Einstellungen.",
- "cert_instructions": "Anleitung für Chrome (Android): 1. Zertifikat oben herunterladen 2. Gehe zu Einstellungen → Sicherheit & Datenschutz → Weitere Sicherheitseinstellungen → Vom Gerätespeicher installieren 3. Wähle die heruntergeladene EverShelf_CA.crt Datei 4. Wähle \"CA\" und bestätige 5. Chrome neu starten
Anleitung für Chrome (PC): 1. Zertifikat oben herunterladen 2. Gehe zu chrome://settings/certificates (oder Einstellungen → Datenschutz und Sicherheit → Sicherheit → Zertifikate verwalten) 3. Tab \"Zertifizierungsstellen\" → Importieren → Datei auswählen 4. Häkchen bei \"Dieser Zertifizierungsstelle für die Identifikation von Webseiten vertrauen\" 5. Chrome neu starten"
- },
- "tts": {
- "title": "🔊 Sprache & TTS",
- "hint": "Sprachsynthese über externe REST-API konfigurieren. Rezeptschritte und abgelaufene Timer werden an den Endpunkt gesendet.",
- "enabled": "✅ TTS aktivieren",
- "engine_label": "⚙️ TTS-Engine",
- "engine_browser": "🔇 Browser (offline, keine Konfiguration erforderlich)",
- "engine_server": "🌐 Externer Server (Home Assistant, REST API...)",
- "voice_label": "🗣️ Stimme",
- "rate_label": "⚡ Geschwindigkeit",
- "pitch_label": "🎵 Tonhöhe",
- "url_label": "🌐 Endpunkt-URL",
- "method_label": "📡 HTTP-Methode",
- "auth_label": "🔐 Authentifizierung",
- "auth_bearer": "Bearer Token",
- "auth_custom": "Benutzerdefinierter Header",
- "auth_none": "Keine",
- "token_label": "🔑 Bearer Token",
- "custom_header_name": "📋 Header-Name",
- "custom_header_value": "📋 Header-Wert",
- "content_type_label": "📄 Content-Type",
- "payload_key_label": "🗝️ Textfeld im Payload",
- "payload_key_hint": "Name des JSON-Feldes für den zu lesenden Text (z.B.: message, text).",
- "extra_fields_label": "➕ Zusätzliche Felder (JSON)",
- "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}",
- "extra_fields_hint": "Zusätzliche Felder im Payload, im JSON-Format. Leer lassen wenn nicht benötigt.",
- "test_sound_btn": "🔔 Klangtest ausführen",
- "test_btn": "🔊 Testansage senden",
- "voices_loading": "Stimmen werden geladen…",
- "voice_not_supported": "Stimme vom Browser nicht unterstützt",
- "voices_none": "Keine Stimmen auf diesem Gerät verfügbar",
- "voices_hint": "Verfügbare Stimmen hängen vom Betriebssystem und Browser ab. Auf macOS/iOS ist die Stimme Paola (Italienisch) verfügbar. Drücken Sie ↺ wenn die Liste nicht lädt.",
- "url_missing": "⚠️ Endpunkt-URL fehlt.",
- "test_sending": "⏳ Wird gesendet…",
- "test_ok": "✅ Antwort {code} — prüfe ob der Lautsprecher gesprochen hat.",
- "heard_question": "Hast du die Stimme gehört?",
- "heard_yes": "Ja, ich habe es gehört",
- "heard_no": "Nein, ich habe nichts gehört",
- "test_ok_kiosk": "TTS funktioniert.",
- "test_fail_steps": "Prüfe: 1) Medienvolume ist nicht 0; 2) Google Text-to-Speech installiert und aktualisiert; 3) Deutsches Sprachpaket in den Android TTS-Einstellungen heruntergeladen."
- },
- "language": {
- "title": "🌐 Sprache",
- "hint": "Wähle die Sprache der Benutzeroberfläche.",
- "label": "🌐 Sprache",
- "restart_notice": "Die Seite wird neu geladen, um die neue Sprache anzuwenden."
- },
- "screensaver": {
- "label": "Bildschirmschoner aktivieren",
- "card_title": "🌙 Bildschirmschoner",
- "card_hint": "Zeigt nach 5 Minuten Inaktivität eine Uhr mit nützlichen Fakten. Standardmäßig deaktiviert.",
- "timeout_1": "1 Minute",
- "timeout_2": "2 Minuten",
- "timeout_5": "5 Minuten",
- "timeout_10": "10 Minuten",
- "timeout_15": "15 Minuten",
- "timeout_30": "30 Minuten",
- "timeout_60": "1 Stunde",
- "start_after": "⏱️ Starten nach"
- },
- "scale": {
- "title": "⚖️ Smart-Waage",
- "hint": "Verbinde eine Bluetooth-Waage über das Android-Gateway, um das Gewicht automatisch auszulesen.",
- "tab": "Smart-Waage",
- "enabled": "✅ Smart-Waage aktivieren",
- "url_label": "🌐 WebSocket-Gateway-URL",
- "url_placeholder": "ws://192.168.1.x:8765",
- "url_hint": "URL der Android-App (gleiches WLAN). z.B.:",
- "test_btn": "🔗 Verbindung testen",
- "download_btn": "📥 Android-Gateway herunterladen (APK)",
- "download_hint": "Android-App als Brücke zwischen BLE-Waage und EverShelf.",
- "download_sub": "Quellcode: evershelf-scale-gateway/ im Projektstamm",
- "live_weight": "Echtzeit-Gewicht",
- "auto_reconnect": "🔁 Verbindung: automatisch",
- "kiosk_title": "📡 BLE-Waage im Kiosk integriert",
- "kiosk_hint": "Die Waage wird direkt vom internen BLE-Gateway des Kiosks verwaltet. Um ein neues Gerät zu koppeln, verwende den Konfigurationsassistenten.",
- "kiosk_reconfigure": "🔄 BLE-Waage neu konfigurieren",
- "ble_protocols": "
🔌 Unterstützte BLE-Protokolle:
Bluetooth SIG Weight Scale (0x181D)
Bluetooth SIG Body Composition (0x181B) — Gewicht, Fett, BMI
Xiaomi Mi Body Composition Scale 2
Generisch — automatische Heuristik für 100+ Modelle
"
- },
- "kiosk": {
- "hint": "Verwandeln Sie ein Android-Tablet in ein EverShelf-Panel mit integriertem BLE-Waagen-Gateway.",
- "download_btn": "📥 EverShelf Kiosk herunterladen (APK)",
- "download_sub": "Vollbild-Kioskmodus + integriertes Waagen-Gateway. Quellcode: evershelf-kiosk/",
- "native_title": "Kiosk-Konfiguration",
- "native_hint": "Server-URL, BLE-Waage, Bildschirmschoner und Einrichtungsassistent.",
- "native_btn": "Kiosk-Konfiguration öffnen",
- "native_tap_hint": "Zahnrad oben rechts antippen",
- "native_update_hint": "Kiosk-App aktualisieren, um diese Funktion zu nutzen",
- "update_title": "Kiosk-Aktualisierung",
- "check_updates_btn": "🔍 Nach Updates suchen",
- "needs_update": "⚠️ Das installierte Kiosk unterstützt diese Funktion nicht. Aktualisiere die Kiosk-App, um sie zu aktivieren."
- },
- "saved": "✅ Konfiguration gespeichert!",
- "saved_local": "✅ Konfiguration lokal gespeichert",
- "saved_local_error": "⚠️ Lokal gespeichert, Serverfehler: {error}",
- "theme": {
- "title": "🌙 Erscheinungsbild",
- "hint": "Wähle das Interface-Design.",
- "label": "🌙 Design",
- "off": "☀️ Hell",
- "on": "🌙 Dunkel",
- "auto": "🔄 Automatisch (Tageszeit)"
- },
- "zerowaste": {
- "card_title": "♻️ Zero-Waste-Tipps",
- "card_hint": "Zeige während des Kochens Tipps zur Wiederverwendung von Abfällen (Schalen, Kochwasser usw.). Standardmäßig deaktiviert.",
- "label": "Tipps beim Kochen anzeigen"
- },
- "backup": {
- "tab": "Backup",
- "local_title": "Lokales Backup",
- "local_hint": "Täglicher Datenbank-Snapshot. Konfiguriere, wie viele Tage Backups aufbewahrt werden.",
- "enabled": "Tägliches automatisches Backup aktivieren",
- "retention_days": "Aufbewahrung (Tage)",
- "retention_info": "Backups werden aufbewahrt für",
- "backup_now": "Jetzt sichern",
- "backing_up": "Sicherung läuft…",
- "backed_up": "Sicherung abgeschlossen",
- "backup_error": "Sicherungsfehler",
- "last_backup": "Letztes Backup",
- "no_backup_yet": "Noch kein Backup erstellt",
- "list_empty": "Keine Backups verfügbar",
- "restore_btn": "Wiederherstellen",
- "restore_confirm": "Backup wiederherstellen",
- "delete_btn": "Löschen",
- "delete_confirm": "Backup löschen",
- "gdrive_title": "Google Drive",
- "gdrive_hint": "Backups automatisch via OAuth 2.0 auf Google Drive hochladen. Keine externen Bibliotheken erforderlich.",
- "gdrive_enabled": "Google Drive Backup aktivieren",
- "gdrive_folder_id": "Drive-Ordner-ID",
- "gdrive_folder_id_hint": "Kopiere die ID aus der Drive-Ordner-URL: …/folders/ID",
- "gdrive_retention_days": "Drive-Aufbewahrung (Tage, 0=alles behalten)",
- "gdrive_test": "Verbindung testen",
- "gdrive_ok": "Verbindung erfolgreich!",
- "gdrive_error": "Verbindung fehlgeschlagen",
- "gdrive_push_now": "Jetzt auf Drive hochladen",
- "gdrive_pushing": "Wird hochgeladen…",
- "gdrive_pushed": "Auf Drive hochgeladen",
- "gdrive_wizard_hint": "Optional: täglich automatisch via OAuth 2.0 auf Google Drive sichern.",
- "gdrive_skip": "Überspringen — später in Einstellungen konfigurieren",
- "gdrive_client_id": "Client-ID",
- "gdrive_client_secret": "Client-Secret",
- "gdrive_redirect_uri_hint": "Füge http://localhost als autorisierten Weiterleitungs-URI in der Google Cloud Console hinzu. Funktioniert auf jedem Server, auch ohne öffentliche Domain.",
- "gdrive_code_title": "Autorisierungs-URL oder Code einfügen",
- "gdrive_code_hint": "Nach der Autorisierung öffnet der Browser http://localhost und zeigt möglicherweise einen Verbindungsfehler — das ist normal. Kopiere die URL aus der Adressleiste (z.B. http://localhost/?code=4%2F0A...) und füge sie hier ein.",
- "gdrive_code_submit": "Bestätigen",
- "gdrive_code_empty": "Bitte zuerst die URL oder den Autorisierungscode einfügen",
- "gdrive_redirect_uri_label": "Redirect-URI (in Google Cloud Console eintragen):",
- "gdrive_oauth_authorize": "Mit Google autorisieren",
- "gdrive_oauth_authorized": "Autorisiert",
- "gdrive_oauth_not_authorized": "Noch nicht autorisiert",
- "gdrive_oauth_window_opened": "Browserfenster geöffnet — autorisieren und zurückkehren",
- "gdrive_oauth_how_to": "OAuth 2.0 einrichten (Schritt für Schritt)",
- "gdrive_oauth_steps": "
Anwendungstyp: Webanwendung; füge die unten angezeigte URL als Autorisierter Weiterleitungs-URI hinzu
Kopiere Client-ID und Client-Secret in die Felder oben und speichere
Klicke auf Mit Google autorisieren: melde dich an und erteile den Zugriff
Das Fenster schließt sich automatisch und Backups sind bereit
"
+ },
+ "info": {
+ "tab": "Info",
+ "ai_title": "Gemini AI — Token-Nutzung",
+ "ai_hint": "Monatlicher Verbrauch und geschätzte Kosten für den aktuellen API-Schlüssel.",
+ "loading": "Laden…",
+ "total_tokens": "Token gesamt",
+ "est_cost": "Gesch. Kosten",
+ "input_tok": "Eingabe-Token",
+ "output_tok": "Ausgabe-Token",
+ "ai_calls": "Aufrufe",
+ "by_action": "Aufschlüsselung nach Funktion",
+ "by_model": "Aufschlüsselung nach Modell",
+ "pricing_note": "Gemini Referenzpreise: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.",
+ "system_title": "System",
+ "db_size": "Datenbank",
+ "log_size": "Protokolle",
+ "log_level": "Log-Level",
+ "ai_overview": "KI-Nutzungsübersicht, Inventar und Systemstatus",
+ "calls_unit": "Aufrufe",
+ "inv_title": "Inventar",
+ "inv_active": "Aktiv",
+ "inv_products": "Produkte gesamt",
+ "inv_expiring": "Ablaufend (7T)",
+ "inv_expired": "Abgelaufen",
+ "inv_finished": "Leer",
+ "act_title": "Monatliche Aktivität",
+ "act_tx_month": "Bewegungen",
+ "act_restock": "Einkäufe",
+ "act_use": "Verbrauch",
+ "act_new_products": "Neue Produkte",
+ "act_tx_year": "Jährl. Bewegungen",
+ "price_cache": "Preiscache",
+ "cache_entries": "Produkte",
+ "last_backup": "Letztes Backup",
+ "bring_days": "Token läuft in {n} Tagen ab",
+ "bring_expired": "Token abgelaufen",
+ "year_label": "Jahr {year}",
+ "currency_title": "Währung",
+ "currency_hint": "Die Währung, die für alle Kosten und Preise in der App verwendet wird."
+ },
+ "tab_general": "Allgemein",
+ "shopping": {
+ "tab": "Einkaufsliste",
+ "title": "Einkaufsliste",
+ "hint": "Konfiguriere die integrierte Einkaufsliste oder verbinde Bring!.",
+ "enable_label": "Einkaufsliste aktivieren",
+ "mode_label": "Anbieter",
+ "mode_internal": "Intern (ohne Bring!)",
+ "mode_bring": "Bring! (externe App)",
+ "bring_section_title": "Bring!-Konfiguration",
+ "ai_section_title": "KI-Unterstützung",
+ "smart_suggestions_label": "KI-Vorschläge",
+ "forecast_label": "Prognose für bald leere Produkte",
+ "auto_add_label": "Automatisch hinzufügen wenn",
+ "auto_add_suffix": "im Lager verbleibend (0 = nur wenn leer)"
+ },
+ "ha": {
+ "tab": "Home Assistant",
+ "title": "Home Assistant",
+ "hint": "Verbinde EverShelf mit Home Assistant für Automationen, Push-Benachrichtigungen und REST-Sensoren.",
+ "enabled": "Home Assistant-Integration aktivieren",
+ "connection_title": "Verbindung",
+ "url_label": "Home Assistant URL",
+ "url_placeholder": "http://192.168.1.50:8123",
+ "url_hint": "Basis-URL deiner Home Assistant-Instanz (z.B. http://homeassistant.local:8123).",
+ "token_label": "Long-Lived Access Token",
+ "token_hint": "Erstelle unter HA-Profil → Sicherheit → Langlebige Zugangstoken.",
+ "token_placeholder": "eyJhbGci...",
+ "token_saved": "Token gespeichert (aus Sicherheitsgründen verborgen)",
+ "test_btn": "Verbindung testen",
+ "test_ok": "Verbunden mit {version}",
+ "test_fail": "Verbindung fehlgeschlagen: {error}",
+ "test_bad_token": "HA erreichbar, aber Token ist ungültig",
+ "testing": "Teste…",
+ "error_no_url": "Bitte zuerst die Home Assistant URL eingeben.",
+ "tts_title": "TTS auf Smart Speaker",
+ "tts_hint": "Rezeptschritte auf einem Home Assistant Media Player vorlesen.",
+ "tts_entity_label": "Media Player Entity ID",
+ "tts_entity_placeholder": "media_player.wohnzimmer",
+ "tts_entity_hint": "Entity-ID des HA-Media-Players. Zu finden unter HA: Entwicklertools → Zustände.",
+ "tts_platform_label": "TTS-Plattform",
+ "tts_platform_speak": "tts.speak (empfohlen)",
+ "tts_platform_notify": "notify.* (Benachrichtigungsdienst)",
+ "tts_apply_btn": "HA-Voreinstellung auf TTS-Tab anwenden",
+ "tts_apply_hint": "Füllt den TTS-Tab mit der Home Assistant URL und dem Token aus.",
+ "tts_preset_applied": "HA-Voreinstellung auf TTS-Tab angewendet.",
+ "webhook_title": "Webhook-Automationen",
+ "webhook_hint": "Sende Daten an Home Assistant, wenn Ereignisse in der Vorratskammer auftreten.",
+ "webhook_id_label": "Webhook-ID",
+ "webhook_id_placeholder": "evershelf_webhook_abc123",
+ "webhook_id_hint": "ID des in HA erstellten Webhooks. Kopiere aus: HA → Einstellungen → Automationen → Erstellen → Webhook-Auslöser.",
+ "webhook_events_label": "Benachrichtige bei diesen Ereignissen",
+ "event_expiry": "Ablaufende Produkte (täglich)",
+ "event_shopping": "Artikel zur Einkaufsliste hinzugefügt",
+ "event_stock": "Lagerbestand aktualisiert",
+ "expiry_days_label": "Ablaufwarnung im Voraus (Tage)",
+ "expiry_days_hint": "Sende die Ablaufwarnung N Tage vor dem Ablaufdatum.",
+ "webhook_help": "In HA: Einstellungen → Automationen → Automation erstellen → Auslöser: Webhook → ID kopieren.",
+ "notify_title": "Push-Benachrichtigungen",
+ "notify_hint": "Sende Push-Benachrichtigungen über einen Home Assistant notify-Dienst.",
+ "notify_service_label": "Notify-Dienst",
+ "notify_service_placeholder": "notify.mobile_app_mein_handy",
+ "notify_service_hint": "Name des HA-notify-Dienstes (z.B. notify.mobile_app_phone). Leer lassen zum Deaktivieren.",
+ "sensor_title": "REST-Sensoren",
+ "sensor_hint": "Zur configuration.yaml hinzufügen, um EverShelf-Sensoren in Home Assistant zu erstellen.",
+ "sensor_copy_btn": "YAML kopieren",
+ "sensor_copied": "YAML in die Zwischenablage kopiert!",
+ "save_btn": "HA-Einstellungen speichern",
+ "ha_hint": "Wenn du Home Assistant verwendest, nutze den Home Assistant-Tab für TTS, Webhooks und Sensoren."
}
+ },
+ "expiry": {
+ "today": "HEUTE",
+ "tomorrow": "Morgen",
+ "days": "{days} Tage",
+ "expired_days": "Seit {days}T",
+ "expired_yesterday": "Seit gestern",
+ "expired_today": "Heute",
+ "badge_today": "⚠️ Läuft heute ab!",
+ "badge_tomorrow": "⏰ Morgen",
+ "badge_tomorrow_long": "⏰ Läuft morgen ab",
+ "badge_days": "⏰ {n} Tage",
+ "badge_expired_ago": "⚠️ Seit {n}T abgel.",
+ "badge_expired": "⛔ Abgelaufen!",
+ "badge_stable": "✅ Stabil",
+ "badge_expiring_short": "⏰ Läuft in {n}T ab",
+ "badge_ok_still": "✅ Noch {n}T",
+ "badge_expires_red": "🔴 In {n}T",
+ "badge_expires_yellow": "🟡 In {n}T",
+ "badge_expired_bare": "⚠️ Abgelaufen",
+ "badge_expires_warn": "⚠️ In {n}T",
+ "badge_days_left": "⏳ ~{n}T übrig",
+ "days_approx": "~{n} Tage",
+ "weeks_approx": "~{n} Wochen",
+ "months_approx": "~{n} Monate",
+ "years_approx": "~{n} Jahre",
+ "expired_today_long": "Heute abgelaufen",
+ "expired_ago_long": "Seit {n} Tagen abgelaufen",
+ "expired_suffix": "— Abgelaufen!",
+ "expired_suffix_ok": "— Abgelaufen (noch ok)",
+ "expired_suffix_warning": "— Abgelaufen (erst prüfen)",
+ "opened_ago_long": "Seit {n} Tagen geöffnet",
+ "opened_today_long": "Heute geöffnet",
+ "opened_suffix": "— Zu lange geöffnet!",
+ "opened_suffix_ok": "— Geöffnet (noch ok)",
+ "opened_suffix_warning": "— Geöffnet (erst prüfen)",
+ "days_compact": "{n}T",
+ "badge_check_soon": "Bald prüfen"
+ },
+ "status": {
+ "ok": "OK",
+ "check": "Prüfen",
+ "discard": "Entsorgen",
+ "tip_freezer_ok": "Im Gefrierschrank: noch sicher (~{n}T Puffer)",
+ "tip_freezer_check": "Seit langem im Gefrierschrank, könnte an Qualität verloren haben. Bald verbrauchen",
+ "tip_freezer_danger": "Zu lange im Gefrierschrank, Gefrierbrand- und Qualitätsverlust-Risiko",
+ "tip_highRisk_check": "Kürzlich abgelaufen, Geruch und Aussehen vor dem Verzehr prüfen",
+ "tip_highRisk_danger": "Verderbliches Produkt abgelaufen: aus Sicherheitsgründen entsorgen",
+ "tip_medRisk_check1": "Aussehen und Geruch vor dem Verzehr prüfen",
+ "tip_medRisk_check2": "Schon eine Weile abgelaufen, vor dem Verzehr gut prüfen",
+ "tip_medRisk_danger": "Zu lange seit dem Ablaufdatum, lieber entsorgen",
+ "tip_lowRisk_ok": "Haltbares Produkt, noch sicher zu verzehren",
+ "tip_lowRisk_check": "Seit über einem Monat abgelaufen, Verpackungsintegrität prüfen",
+ "tip_lowRisk_danger": "Zu lange abgelaufen, besser kein Risiko eingehen"
+ },
+ "toast": {
+ "product_saved": "Produkt gespeichert!",
+ "product_created": "Produkt erstellt!",
+ "product_updated": "✅ Produkt aktualisiert!",
+ "product_removed": "Produkt entfernt",
+ "updated": "Aktualisiert!",
+ "quantity_confirmed": "✓ Menge bestätigt",
+ "added_to_inventory": "✅ {name} hinzugefügt!",
+ "removed_from_list": "✅ {name} von der Liste entfernt!",
+ "removed_from_list_short": "Von der Liste entfernt",
+ "added_to_shopping": "🛒 Zur Einkaufsliste hinzugefügt!",
+ "removed_from_shopping": "🛒 Von der Einkaufsliste entfernt",
+ "finished_to_bring": "🛒 Produkt aufgebraucht → zu Bring! hinzugefügt",
+ "thrown_away": "🗑️ {name} weggeworfen!",
+ "thrown_away_partial": "🗑️ {qty} {unit} von {name} weggeworfen",
+ "finished_all": "📤 {name} aufgebraucht!",
+ "vacuum_sealed": "{name} als vakuumversiegelt gespeichert",
+ "product_finished_confirmed": "✅ Entfernt — wieder hinzufügen, wenn du nachkaufst",
+ "appliance_added": "Gerät hinzugefügt",
+ "item_added": "{name} hinzugefügt"
+ },
+ "antiwaste": {
+ "title": "🌱 Anti-Verschwendungs-Bericht",
+ "grade_label": "Note",
+ "you": "Du",
+ "avg_label": "Ø",
+ "better": "🎉 Du verlierst {diff}% weniger als der {country}!",
+ "worse": "⚠️ Du verlierst mehr als der {country}. Verbesserungspotenzial!",
+ "on_par": "→ Du liegst beim {country}. Du kannst noch besser werden!",
+ "saved_money": "~{amount}/Monat gespart",
+ "saved_meals": "~{n} Mahlzeiten gerettet",
+ "saved_co2": "{n} kg CO₂ eingespart",
+ "trend_title": "Trend (letzte 3 Monate)",
+ "months_ago_2": "-60 Tage",
+ "months_ago_1": "-30 Tage",
+ "this_month": "Jetzt",
+ "country_it": "ital. Durchschnitt",
+ "country_de": "dt. Durchschnitt",
+ "country_en": "US-Durchschnitt",
+ "source": "Quellen: REDUCE, Eurostat, USDA 2021",
+ "live_on": "Live-Daten",
+ "live_off": "Offline",
+ "meals": "Mahlzeiten",
+ "annual_info": "📅 Du ~{you} kg/Jahr · Ø ~{avg} kg/Jahr",
+ "badge_rate": "Verlustquote",
+ "badge_saved_money": "gespart vs Ø",
+ "badge_wasted": "verloren",
+ "badge_better": "weniger als Ø"
+ },
+ "error": {
+ "generic": "Fehler",
+ "network": "Netzwerkfehler",
+ "no_api_key": "API-Schluessel in den Einstellungen konfigurieren",
+ "loading": "Fehler beim Laden des Produkts",
+ "not_found": "Produkt nicht gefunden",
+ "not_found_manual": "Produkt nicht gefunden. Manuell eingeben.",
+ "search": "Suchfehler. Nochmal versuchen.",
+ "search_short": "Suchfehler",
+ "save": "Fehler beim Speichern",
+ "connection": "Verbindungsfehler",
+ "camera": "Kamera nicht verfügbar",
+ "bring_add": "Fehler beim Hinzufügen zu Bring!",
+ "bring_connection": "Bring! Verbindungsfehler",
+ "identification": "Identifikationsfehler",
+ "ai_quota": "KI-Kontingent erschöpft. Bitte in ein paar Minuten erneut versuchen.",
+ "barcode_empty": "Barcode eingeben",
+ "barcode_format": "Barcode darf nur Zahlen enthalten (4-14 Ziffern)",
+ "min_chars": "Mindestens 2 Zeichen eingeben",
+ "not_in_inventory": "Produkt nicht im Bestand",
+ "appliance_exists": "Gerät bereits vorhanden",
+ "already_exists": "Bereits vorhanden",
+ "network_retry": "Verbindungsfehler. Erneut versuchen.",
+ "select_items": "Wähle mindestens ein Produkt aus",
+ "server_offline": "Serververbindung unterbrochen",
+ "server_restored": "Serververbindung wiederhergestellt",
+ "server_retry": "Erneut versuchen",
+ "unknown": "Unbekannter Fehler",
+ "prefix": "Fehler",
+ "no_inventory_entry": "Kein Inventareintrag gefunden",
+ "offline_title": "Keine Verbindung",
+ "offline_subtitle": "Die App kann den Server nicht erreichen. Überprüfe deine WLAN-Verbindung.",
+ "offline_checking": "Verbindung prüfen…",
+ "offline_restored": "Verbindung wiederhergestellt!",
+ "offline_continue": "Im Offline-Modus fortfahren",
+ "offline_reading_cache": "Lese aus lokalem Cache",
+ "offline_ops_pending": "{n} Aktionen ausstehend",
+ "offline_synced": "{n} Aktionen synchronisiert",
+ "offline_ai_disabled": "Offline nicht verfügbar",
+ "offline_cache_ready": "Offline — {n} Produkte im Cache"
+ },
+ "confirm_placeholder_search": null,
+ "confirm": {
+ "remove_item": "Möchtest du dieses Produkt wirklich aus dem Bestand entfernen?",
+ "kiosk_exit": "Kioskmodus verlassen?",
+ "cancel": "Abbrechen",
+ "proceed": "Bestätigen",
+ "discard_one": "1 Stück wegwerfen"
+ },
+ "location": {
+ "dispensa": "Vorratskammer",
+ "frigo": "Kühlschrank",
+ "freezer": "Gefrierschrank"
+ },
+ "edit": {
+ "title": "{name} bearbeiten",
+ "unknown_hint": "Produktname und Informationen eingeben",
+ "label_name": "🏷️ Produktname",
+ "choose_location_title": "Welchen Ort?",
+ "choose_location_hint": "Wähle den zu bearbeitenden Ort:",
+ "confirm_large_qty": "Du setzt die Menge auf {qty} {unit}. Das scheint ungewöhnlich hoch zu sein. Bestätigen?"
+ },
+ "screensaver": {
+ "recipe_btn": "Rezepte",
+ "scan_btn": "Produkt scannen"
+ },
+ "days": {
+ "mon": "Montag",
+ "tue": "Dienstag",
+ "wed": "Mittwoch",
+ "thu": "Donnerstag",
+ "fri": "Freitag",
+ "sat": "Samstag",
+ "sun": "Sonntag",
+ "mon_short": "Mo",
+ "tue_short": "Di",
+ "wed_short": "Mi",
+ "thu_short": "Do",
+ "fri_short": "Fr",
+ "sat_short": "Sa",
+ "sun_short": "So"
+ },
+ "meal_types": {
+ "lunch": "Mittagessen",
+ "dinner": "Abendessen",
+ "colazione": "Frühstück",
+ "merenda": "Nachmittagssnack",
+ "dolce": "Dessert",
+ "succo": "Fruchtsaft",
+ "pranzo": "Mittagessen",
+ "cena": "Abendessen"
+ },
+ "scale": {
+ "status_connected": "Waage verbunden",
+ "status_searching": "Gateway verbunden, warte auf Waage…",
+ "status_disconnected": "Waagen-Gateway nicht erreichbar",
+ "status_error": "Verbindungsfehler zum Gateway",
+ "not_connected": "Waagen-Gateway nicht verbunden",
+ "read_btn": "⚖️ Von Waage lesen",
+ "reading_title": "Waage lesen",
+ "place_on_scale": "Produkt auf die Waage legen…",
+ "waiting_stable": "Das Gewicht wird automatisch erfasst, wenn die Messung stabil ist.",
+ "no_url": "Gateway-URL eingeben",
+ "testing": "⏳ Verbindung wird getestet…",
+ "connected_ok": "Gateway-Verbindung erfolgreich!",
+ "timeout": "Timeout: keine Antwort vom Gateway",
+ "error_connect": "Verbindung zum Gateway nicht möglich",
+ "tab": "Smart-Waage",
+ "low_weight": "Gewicht < 10 g · manuell eingeben\n(Auto-Erkennung erfordert mind. 10 g)",
+ "density_hint": "(Dichte {density} g/ml)",
+ "ml_hint": "(wird in ml umgerechnet)",
+ "weight_detected": "Gewicht erkannt — 10s Stabilität abwarten…",
+ "weight_too_low": "Gewicht zu niedrig — warten…",
+ "stable": "✓ Stabil",
+ "auto_confirm": "✅ {val} {unit} — Auto-Bestätigung in 5s (tippen zum Abbrechen)",
+ "cancelled_replace": "Abgebrochen — lege die Zutat wieder auf die Waage, um fortzufahren"
+ },
+ "prediction": {
+ "expected_qty": "Erwartet: {expected} {unit}",
+ "actual_qty": "Aktuell: {actual} {unit}",
+ "check_suggestion": "Überprüfe oder wiege die Restmenge"
+ },
+ "date": {
+ "today": "📅 Heute",
+ "yesterday": "📅 Gestern"
+ },
+ "scanner": {
+ "title_barcode": "🔖 Barcode scannen",
+ "barcode_hint": "Produktbarcode einrahmen",
+ "barcode_manual_placeholder": "Oder manuell eingeben...",
+ "barcode_use_btn": "✅ Diesen Code verwenden",
+ "ai_identifying": "🤖 Produkt wird erkannt...",
+ "ai_analyzing": "🤖 KI-Analyse läuft...",
+ "product_label_hint": "Produktetikett einrahmen",
+ "expiry_label_hint": "Ablaufdatum auf dem Produkt einrahmen",
+ "capture_btn": "📸 Aufnehmen",
+ "capture_photo_btn": "📸 Foto aufnehmen",
+ "retake_btn": "🔄 Erneut aufnehmen",
+ "camera_error_hint": "Stelle sicher, dass du HTTPS verwendest und Kameraberechtigungen erteilt hast. Du kannst den Barcode manuell eingeben oder die KI-Identifikation verwenden.",
+ "no_barcode": "Kein Barcode",
+ "save_new_btn": "🆕 Keines davon — als neu speichern",
+ "expiry_found": "Datum gefunden",
+ "expiry_read_fail": "Datum konnte nicht gelesen werden.",
+ "expiry_raw_label": "Erkannt"
+ },
+ "lowstock": {
+ "title": "⚠️ Wird knapp!",
+ "message": "{name} wird knapp — nur noch {qty} übrig.",
+ "question": "Möchtest du es zur Einkaufsliste hinzufügen?",
+ "yes": "🛒 Ja, zu Bring! hinzufügen",
+ "no": "Nein, passt für jetzt"
+ },
+ "move": {
+ "title": "📦 Den Rest bewegen?",
+ "question": "Möchtest du {thing} von {name} an einen anderen Ort bewegen?",
+ "question_short": "Möchtest du {thing} an einen anderen Ort bewegen?",
+ "thing_opened": "die offene Packung",
+ "thing_rest": "den Rest",
+ "stay_btn": "Nein, bleibt in {location}",
+ "moved_toast": "📦 Offene Packung bewegt nach {location}",
+ "vacuum_restore": "Vakuum wiederherstellen",
+ "vacuum_seal_rest": "Rest vakuumieren"
+ },
+ "nova": {
+ "1": "Unverarbeitet",
+ "2": "Kulinarische Zutat",
+ "3": "Verarbeitet",
+ "4": "Hochverarbeitet"
+ },
+ "meal_plan_types": {
+ "pasta": "Pasta",
+ "riso": "Reis",
+ "carne": "Fleisch",
+ "pesce": "Fisch",
+ "legumi": "Hülsenfrüchte",
+ "uova": "Eier",
+ "formaggio": "Käse",
+ "pizza": "Pizza",
+ "affettati": "Aufschnitt",
+ "verdure": "Gemüse",
+ "zuppa": "Suppe",
+ "insalata": "Salat",
+ "pane": "Brot/Sandwich",
+ "dolce": "Dessert",
+ "libero": "Frei"
+ },
+ "meal_sub": {
+ "dolce_torta": "Kuchen",
+ "dolce_crema": "Creme / Pudding",
+ "dolce_crumble": "Crumble / Tarte",
+ "dolce_biscotti": "Kekse / Gebäck",
+ "dolce_frutta": "Fruchtdessert",
+ "succo_dolce": "Süß / Fruchtig",
+ "succo_energizzante": "Energetisierend",
+ "succo_detox": "Detox / Grün",
+ "succo_rinfrescante": "Erfrischend",
+ "succo_vitaminico": "Vitamin / Zitrus"
+ },
+ "meal_plan": {
+ "reset_success": "Wochenplan zurückgesetzt",
+ "not_available": "nicht im Vorrat verfügbar",
+ "suggested_by": "vom Wochenplan vorgeschlagen"
+ },
+ "nutrition": {
+ "title": "🥗 Ernährungsanalyse",
+ "score_excellent": "😄 Ausgezeichnet",
+ "score_good": "🙂 Gut",
+ "score_improve": "😬 Verbesserbar",
+ "label_health": "🌿 Gesundheit",
+ "label_variety": "🎨 Vielfalt",
+ "label_fresh": "❄️ Frisch",
+ "source": "Basierend auf {n} Produkten in deiner Vorratskammer · EverShelf",
+ "products_count": "Produkte",
+ "today_title": "🥗 Deine Vorratskammer heute",
+ "products_n": "{n} Produkte",
+ "macros_title": "Geschätzte Makronährstoffe",
+ "macros_proteins": "Proteine",
+ "macros_carbs": "Kohlenhydrate",
+ "macros_fat": "Fett",
+ "macros_fiber": "Ballaststoffe",
+ "macros_source": "Schätzung basierend auf {n} Vorratsprodukten"
+ },
+ "facts": {
+ "greeting_morning": "Guten Morgen",
+ "greeting_afternoon": "Guten Tag",
+ "greeting_evening": "Guten Abend",
+ "pantry_waiting": "{greeting}! Deine Vorratskammer wartet.",
+ "expired_one": "Du hast 1 abgelaufenes Produkt in der Vorratskammer. Bitte überprüfen!",
+ "expired_many": "Du hast {n} abgelaufene Produkte in der Vorratskammer. Bitte überprüfen!",
+ "expired_list": "Abgelaufene Produkte: {names}",
+ "expired_list_more": "und {n} weitere",
+ "freezer_expired_ok": "{name} ist abgelaufen, aber im Gefrierschrank könnte es noch gut sein! Überprüfe es.",
+ "freezer_expired_old": "{name} im Gefrierschrank ist zu lange abgelaufen. Besser wegwerfen.",
+ "fridge_expired_one": "Du hast 1 abgelaufenes Produkt im Kühlschrank!",
+ "fridge_expired_many": "Du hast {n} abgelaufene Produkte im Kühlschrank!",
+ "expiring_today": "{name} läuft heute ab! Sofort verbrauchen.",
+ "expiring_tomorrow": "{name} läuft morgen ab. Denk daran!",
+ "expiring_days": "{name} läuft in {days} Tagen ab.",
+ "expiring_many": "Du hast {n} Produkte, die bald ablaufen.",
+ "expiring_this_week": "{n} Produkte laufen diese Woche ab. Plane deine Mahlzeiten entsprechend!",
+ "expiring_item_loc": "{name} ({loc}) läuft in {days} {dayslabel} ab.",
+ "expiring_this_month": "{n} Produkte laufen diesen Monat ab.",
+ "shopping_add": "Zur Liste: {names} 🛒",
+ "shopping_more": "und {n} weitere",
+ "shopping_empty": "Einkaufsliste leer. Alles aufgefüllt! ✅",
+ "in_fridge": "Im Kühlschrank: {name}.",
+ "in_freezer": "Im Gefrierschrank: {name}. Vergiss es nicht!",
+ "top_category": "Häufigste Kategorie: {icon} {cat} mit {n} Produkten.",
+ "cat_meat": "Du hast {n} Fleischprodukte. 🥩",
+ "cat_dairy": "Du hast {n} Milchprodukte zu Hause. 🥛",
+ "cat_veggies": "Du hast {n} Gemüsesorten. Super für die Gesundheit! 🥬",
+ "cat_fruit": "Du hast {n} Obstsorten. 🍎",
+ "cat_drinks": "Du hast {n} Getränke verfügbar. 🥤",
+ "cat_frozen": "Du hast {n} Tiefkühlprodukte. ❄️",
+ "cat_pasta": "Du hast {n} Nudelsorten. 🍝 Wie wäre es mit einer Carbonara?",
+ "cat_canned": "Du hast {n} Konserven in der Vorratskammer. 🥫",
+ "cat_snacks": "Du hast {n} Snacks. Widerstand leisten! 🍪",
+ "cat_condiments": "Du hast {n} Gewürze zur Verfügung. 🧂",
+ "item_random": "Wusstest du? Du hast {name} in {loc}.",
+ "item_qty": "{name}: du hast {qty}.",
+ "no_expiry_count": "{n} Produkte haben kein Ablaufdatum.",
+ "furthest_expiry": "Das Produkt mit dem spätesten Ablaufdatum ist {name}: {months} Monate.",
+ "high_qty": "Du hast einen guten Vorrat von {name}: {qty}!",
+ "low_qty_item": "{name} geht zur Neige. Auf die Einkaufsliste?",
+ "low_qty_count": "{n} Produkte sind fast aufgebraucht.",
+ "morning_bread": "Guten Morgen! Du hast Brot für das Frühstück. 🍞",
+ "morning_milk": "Gibt es Milch im Kühlschrank für den Cappuccino? ☕🥛",
+ "morning_fruit": "Guten Morgen! Frisches Obst ist ein guter Start. 🍎",
+ "noon_pasta": "Mittagszeit… Wie wäre es mit Pasta? 🍝",
+ "noon_salad": "Ein frischer Salat zum Mittagessen? Du hast {n} Gemüsesorten! 🥗",
+ "evening_meat": "Zum Abendessen könntest du das Fleisch verwenden. 🥩",
+ "evening_fish": "Wie wäre es mit Fisch zum Abendessen? 🐟",
+ "evening_expiring": "Du hast {n} Produkte, die diese Woche ablaufen — heute Abend verwenden!",
+ "night_reminder": "Gute Nacht! Denk morgen daran zu verwenden: {names}.",
+ "weekly_balance": "Wochenbilanz: +{in} hinzugefügt, −{out} verbraucht.",
+ "weekly_added": "Du hast diese Woche {n} Produkte hinzugefügt.",
+ "weekly_consumed": "Du hast diese Woche {n} Produkte verbraucht. Gut gemacht!",
+ "tip_freezer": "💡 Tiefkühlprodukte halten viel länger als das Ablaufdatum.",
+ "tip_bread": "💡 Gefrorenes Brot behält seinen Duft wochenlang.",
+ "tip_fifo": "💡 Um Verschwendung zu vermeiden, zuerst Produkte mit nahem Ablaufdatum verwenden (FIFO).",
+ "tip_meat": "💡 Fleisch im Gefrierschrank hält bis zu 6 Monate problemlos.",
+ "tip_no_refreeze": "💡 Niemals ein aufgetautes Lebensmittel wieder einfrieren. Sofort zubereiten!",
+ "tip_fridge": "💡 Ein ordentlicher Kühlschrank spart Zeit und Geld.",
+ "tip_canned": "💡 Geöffnete Konserven in den Kühlschrank und in wenigen Tagen verbrauchen.",
+ "top_brand": "Die häufigste Marke in deiner Vorratskammer ist {brand} mit {n} Produkten.",
+ "combo_pasta": "Du hast Pasta und Gewürze: bereit für ein Erstgericht! 🍝",
+ "combo_sandwich": "Brot und Fleisch: ein schnelles Sandwich ist immer eine gute Idee! 🥪",
+ "combo_balanced": "Gemüse und Fleisch: du hast alles für eine ausgewogene Mahlzeit! 🥗🥩",
+ "pantry_empty": "Die Vorratskammer ist leer! Zeit zum Einkaufen. 🛒",
+ "pantry_empty_scan": "Keine Produkte erfasst. Scanne etwas um zu beginnen!",
+ "location_distribution": "Verteilung: {parts}",
+ "day": "Tag",
+ "days": "Tage"
+ },
+ "kiosk_session": {
+ "first_item": "Erstes Produkt: {name}!",
+ "items_two_four": "{n} Artikel — Trägheit überwinden 🚀",
+ "items_five_nine": "{n} Artikel — super Tempo! 💪",
+ "items_ten_twenty": "{n} Artikel — fast Rekord 🏆",
+ "items_twenty_plus": "{n} Artikel — epischer Einkauf! 🛒🔥",
+ "duplicates_one": "1 Duplikat (gleiches Produkt zweimal)",
+ "duplicates_many": "{n} Duplikate (mehrfach genommen)",
+ "top_category": "Top-Kategorie: {cat} ({count}×)",
+ "items_fallback": "{n} Artikel hinzugefügt"
+ },
+ "kiosk": {
+ "check_btn": "🔍 Nach Updates suchen",
+ "checking": "⏳ Prüfe…",
+ "error_check": "Fehler bei der Update-Prüfung",
+ "error_start_install": "Fehler beim Starten der Installation",
+ "version_installed": "Installiert: {v}",
+ "update_available": "⬆️ Neue Version verfügbar: {latest} (installiert: {current})",
+ "up_to_date": "✅ Du bist auf dem neuesten Stand — Version {v}",
+ "too_old": "⚠️ Der installierte Kiosk ist zu alt für die automatische Update-Prüfung. Drücke den Knopf unten, um die neue Version direkt herunterzuladen.",
+ "manual_install": "⚠️ Dieser Kiosk unterstützt keine automatische Installation. Manuelle Vorgehensweise: 1. Kiosk verlassen (✕ oben links) 2. EverShelf Kiosk App deinstallieren 3. Neue APK von GitHub herunterladen und installieren:",
+ "starting_download": "⏳ Download startet…",
+ "install_btn": "⬇️ Update installieren",
+ "exit_title": "Kiosk beenden",
+ "refresh_title": "Seite aktualisieren"
+ },
+ "update": {
+ "new_version": "Neue Version",
+ "btn": "Aktualisieren"
+ },
+ "gemini": {
+ "chat_title": "Mit Gemini chatten",
+ "not_configured": "🤖 Gemini nicht konfiguriert — GEMINI_API_KEY in den Einstellungen setzen"
+ },
+ "appliances": {
+ "empty": "Kein Haushaltsgerät hinzugefügt"
+ },
+ "about": {
+ "title": "Über",
+ "version": "Version",
+ "report_bug": "Fehler melden",
+ "report_bug_hint": "Etwas funktioniert nicht? Sende uns direkt aus der App eine Meldung.",
+ "report_bug_modal_title": "Fehler melden",
+ "report_type_bug": "Fehler",
+ "report_type_feature": "Funktion",
+ "report_type_question": "Frage",
+ "report_field_title": "Titel",
+ "report_field_title_ph": "Kurze Beschreibung des Problems",
+ "report_field_desc": "Beschreibung",
+ "report_field_desc_ph": "Problem detailliert beschreiben…",
+ "report_field_steps": "Schritte zum Reproduzieren (optional)",
+ "report_field_steps_ph": "1. Gehe zu…\n2. Tippe auf…\n3. Fehler erscheint…",
+ "report_auto_info": "Automatisch beigefügt: Version {version}, Sprache {lang}.",
+ "report_send_btn": "Bericht senden",
+ "report_bug_sending": "Wird gesendet…",
+ "report_bug_sent": "Bericht gesendet — danke!",
+ "report_bug_error": "Bericht konnte nicht gesendet werden. Verbindung prüfen.",
+ "changelog": "Changelog",
+ "github": "GitHub-Repository"
+ },
+ "export": {
+ "title": "Inventar exportieren",
+ "hint": "Lade das aktuelle Inventar als CSV herunter oder öffne die druckfertige Version (PDF).",
+ "btn_csv": "CSV herunterladen",
+ "btn_pdf": "PDF / Drucken",
+ "btn_title": "Exportieren"
+ },
+ "startup": {
+ "connecting": "Serververbindung wird hergestellt...",
+ "check_php_memory": "PHP-Speicher",
+ "check_php_timeout": "PHP-Timeout",
+ "check_php_upload": "PHP-Upload",
+ "check_data_dir": "Datenverzeichnis",
+ "check_rate_limits": "Rate-Limits-Verzeichnis",
+ "check_backups": "Backup-Verzeichnis",
+ "check_write_test": "Schreibtest",
+ "check_disk_space": "Speicherplatz",
+ "check_db_legacy": "Legacy-DB (dispensa.db)",
+ "check_db_connect": "Datenbankverbindung",
+ "check_db_tables": "Datenbanktabellen",
+ "check_db_integrity": "Datenbankintegrität",
+ "check_db_wal": "WAL-Modus",
+ "check_db_size": "Datenbankgröße",
+ "check_db_rows": "Inventardaten",
+ "check_env": ".env-Datei",
+ "check_gemini": "Gemini-AI-Schlüssel",
+ "check_bring_creds": "Bring!-Anmeldedaten",
+ "check_bring_token": "Bring!-Token",
+ "check_tts": "Text-to-Speech-URL",
+ "check_scale": "Waagen-Gateway",
+ "check_curl_ssl": "cURL-SSL",
+ "check_internet": "Internetverbindung",
+ "fresh_install": "Neuinstallation",
+ "warnings_found": "Warnungen",
+ "all_ok": "System OK",
+ "critical_error_short": "Kritischer Fehler",
+ "critical_error": "Kritischer Fehler: Die App kann nicht gestartet werden. Prüfe die Serverlogs.",
+ "critical_error_intro": "Die App kann aufgrund folgender Probleme nicht gestartet werden:",
+ "error_network": "Server nicht erreichbar.",
+ "error_network_detail": "Der Browser kann den PHP-Server nicht erreichen.\n\nMögliche Ursachen:\n• Apache/PHP-Server läuft nicht\n• Netzwerk- oder Firewall-Problem\n• Falsche App-URL\n\nBitte Server starten und erneut versuchen.",
+ "retry": "Erneut versuchen",
+ "syncing_local": "Lokale Daten synchronisieren...",
+ "sync_done": "Lokale Daten aktualisiert"
+ },
+ "stats_monthly": {
+ "title": "Monatsstatistik",
+ "consumed": "Produkte verbraucht",
+ "trend_up": "+{pct}% vs. {prev}",
+ "trend_down": "-{pct}% vs. {prev}",
+ "trend_same": "gleiches Tempo wie letzten Monat",
+ "added": "hinzugefügt",
+ "wasted": "verschwendet",
+ "top_used": "meistbenutzt",
+ "top_cats": "Hauptkategorien",
+ "source": "Transaktionsverlauf · aktueller Monat"
+ }
}
\ No newline at end of file
diff --git a/translations/en.json b/translations/en.json
index bd8ebb7..1536460 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -1,1458 +1,1463 @@
{
- "app": {
- "name": "EverShelf",
- "loading": "Loading..."
- },
- "nav": {
- "title": "EverShelf",
- "home": "Home",
- "inventory": "Pantry",
- "recipes": "Recipes",
- "shopping": "Shopping",
- "log": "Log",
- "settings": "Settings"
- },
- "btn": {
- "back": "← Back",
- "save": "💾 Save",
- "cancel": "✕ Cancel",
- "close": "Close",
- "add": "✅ Add",
- "delete": "Delete",
- "edit": "✏️ Edit",
- "use": "Use",
- "edit_item": "Edit",
- "search": "🔍 Search",
- "go": "✅ Go",
- "toggle_password": "👁️ Show/Hide",
- "load_more": "Load more...",
- "save_config": "💾 Save Configuration",
- "save_product": "💾 Save Product",
- "restart": "↺ Restart",
- "reset_default": "↺ Reset to default",
- "save_info": "💾 Save information",
- "retry": "🔄 Retry",
- "yes_short": "Yes",
- "no_short": "No"
- },
- "form": {
- "select_placeholder": "-- Select --"
- },
- "locations": {
- "dispensa": "Pantry",
- "frigo": "Fridge",
- "freezer": "Freezer",
- "altro": "Other"
- },
- "categories": {
- "latticini": "Dairy",
- "carne": "Meat",
- "pesce": "Fish",
- "frutta": "Fruit",
- "verdura": "Vegetables",
- "pasta": "Pasta & Rice",
- "pane": "Bread & Bakery",
- "surgelati": "Frozen",
- "bevande": "Beverages",
- "condimenti": "Condiments",
- "snack": "Snacks & Sweets",
- "conserve": "Canned Goods",
- "cereali": "Cereals & Legumes",
- "igiene": "Hygiene",
- "pulizia": "Household",
- "altro": "Other",
- "select": "-- Select --"
- },
- "units": {
- "pz": "pcs",
- "conf": "pkg",
- "g": "g",
- "ml": "ml",
- "pieces": "Pieces",
- "grams": "Grams",
- "box": "Package",
- "boxes": "Packages",
- "millilitres": "Millilitres",
- "from": "of"
- },
- "shopping_sections": {
- "frutta_verdura": "Fruits & Vegetables",
- "carne_pesce": "Meat & Fish",
- "latticini": "Dairy & Fresh",
- "pane_dolci": "Bread & Sweets",
- "pasta": "Pasta & Cereals",
- "conserve": "Canned & Sauces",
- "surgelati": "Frozen",
- "bevande": "Beverages",
- "pulizia_igiene": "Cleaning & Hygiene",
- "altro": "Other"
- },
- "dashboard": {
- "expired_title": "🚫 Expired",
- "expiring_title": "⏰ Expiring Soon",
- "stats_period": "📊 Last 30 days",
- "opened_title": "📦 Opened Products",
- "review_title": "🔍 To Review",
- "review_hint": "Quantities that seem unusual. Confirm if correct or modify.",
- "quick_recipe": "Quick recipe with expiring products",
- "banner_review_title": "Anomalous quantity",
- "banner_review_action_ok": "It's correct",
- "banner_review_action_finish": "🗑️ All gone",
- "banner_review_action_edit": "Correct",
- "banner_review_action_weigh": "Weigh",
- "banner_review_dismiss": "Dismiss",
- "banner_prediction_title": "Consumption to review",
- "banner_prediction_hint": "The consumption estimate adapts to recent data: confirm only if the current quantity is correct.",
- "banner_prediction_action_confirm": "Confirm {qty} {unit}",
- "banner_prediction_action_weigh": "Weigh now",
- "banner_prediction_action_edit": "Update quantity",
- "banner_expired_title": "Expired product",
- "banner_expired_today": "Expired today",
- "banner_expired_days": "Expired {days} days ago",
- "banner_expired_action_use": "Use anyway",
- "banner_expired_action_finished": "I finished it!",
- "banner_expired_action_throw": "I threw it away",
- "banner_expired_action_edit": "Fix date",
- "banner_expired_action_modify": "Edit",
- "banner_expired_action_vacuum": "Put in vacuum seal",
- "banner_anomaly_action_edit": "Fix inventory",
- "banner_anomaly_action_dismiss": "Quantity is correct",
- "banner_no_expiry_title": "Missing expiry: {name}",
- "banner_no_expiry_detail": "This product has no expiry date. Would you like to add one, or confirm it doesn't expire?",
- "banner_no_expiry_action_set": "Set expiry date",
- "banner_no_expiry_action_dismiss": "Doesn't expire ✓",
- "banner_no_expiry_toast_dismissed": "Marked as 'no expiry'",
- "banner_expiring_title": "Expiring soon",
- "banner_expiring_today": "Expires today!",
- "banner_expiring_tomorrow": "Expires tomorrow",
- "banner_expiring_days": "Expires in {days} days",
- "banner_expiring_action_use": "Use now",
- "banner_finished_title": "finished?",
- "banner_finished_detail": "I recorded that {name} reached zero stock. Is it really gone, or do you still have some?",
- "banner_finished_action_yes": "Yes, it's done",
- "banner_finished_action_no": "No, I still have some",
- "banner_review_unusual_pkg_title": "Unusual package size",
- "banner_review_unusual_pkg_detail": "You set a package of {qty} {unit} — the size seems very large. Check if correct or edit.",
- "banner_review_low_qty_title": "Very low quantity",
- "banner_review_low_qty_detail": "You only have {qty} in stock — seems very little, could be a typo. Confirm if correct.",
- "banner_review_high_qty_title": "Unusually high quantity",
- "banner_review_high_qty_detail": "You have {qty} in stock — the figure seems very high. Confirm if correct or edit.",
- "banner_prediction_rate_day": "Average ~{n} {unit}/day",
- "banner_prediction_rate_week": "Average ~{n} {unit}/week",
- "banner_prediction_days_ago": "{n} days ago you restocked",
- "banner_prediction_more": "previous estimate: {expected} {unit}{time}; current quantity: {actual} {unit}.",
- "banner_prediction_less": "estimate: {expected} {unit}{time}; current quantity: {actual} {unit}. If your usage pace changed, the forecast updates automatically.",
- "banner_finished_zero": "Inventory shows zero, but recorded movements suggest it shouldn't be empty.",
- "banner_finished_expected": "According to records you should still have {qty} {unit}.",
- "banner_finished_check": "Can you check?",
- "banner_anomaly_phantom_title": "you have more stock than expected",
- "banner_anomaly_phantom_detail": "Inventory shows {inv_qty} {unit}, but based on records you should only have {expected_qty} {unit}. Did you add stock without recording it?",
- "banner_anomaly_untracked_title": "stock not recorded as an entry",
- "banner_anomaly_untracked_detail": "You have {inv_qty} {unit} in inventory, but recorded outflows exceed inflows — the initial stock was likely never added as an \"in\" transaction. You can correct the quantity or log the missing entries.",
- "banner_anomaly_ghost_title": "you have less stock than expected",
- "banner_anomaly_ghost_detail": "Based on recorded operations you should have {expected_qty} {unit} of {name}, but inventory shows only {inv_qty} {unit}. Did you take stock without recording it?",
- "consumed": "Consumed: {n} ({pct}%)",
- "wasted": "Wasted: {n} ({pct}%)",
- "more_opened": "and {n} more opened...",
- "banner_expired_detail": "{when} · you still have {qty}.",
- "banner_opened_detail": "{when} in {location} · you still have {qty}.",
- "banner_explain_title": "Ask Gemini for an explanation",
- "banner_explain_btn": "Explain",
- "banner_analyzing": "🤖 Analyzing…"
- },
- "inventory": {
- "title": "Pantry",
- "filter_all": "All",
- "search_placeholder": "🔍 Search product...",
- "recent_title": "🕐 Recently used",
- "popular_title": "⭐ Most used",
- "empty": "No products here.\nScan a product to add it!",
- "no_items_found": "No inventory items found",
- "qty_remainder_suffix": "left",
- "vacuum_badge": "🫙 Vacuum sealed",
- "opened_badge": "📭 Opened",
- "label_expiry": "📅 Expiry",
- "label_storage": "🫙 Storage",
- "label_status": "📭 Status",
- "opened_since": "Opened since {date}",
- "label_position": "📍 Location",
- "label_quantity": "📦 Quantity",
- "label_added": "📅 Added",
- "empty_text": "No products here. Scan a product to add it!",
- "empty_db": "No products in the database. Scan a product to get started!",
- "qty_trace": "< 1"
- },
- "scan": {
- "title": "Scan",
- "mode_shopping": "🛒 Shopping Mode",
- "mode_shopping_end": "✅ End shopping",
- "spesa_btn": "🛒 Shopping",
- "zoom": "Zoom",
- "tab_barcode": "Barcode",
- "tab_name": "Name",
- "tab_ai": "AI",
- "recents_label": "Recent",
- "torch_hint": "Torch",
- "torch_on": "Torch on",
- "torch_off": "Torch off",
- "torch_unavailable": "Torch not available on this device",
- "flip_hint": "Flip camera",
- "flip_front": "Front camera",
- "flip_back": "Rear camera",
- "num_ocr_btn": "🔢 Read numbers with AI",
- "num_ocr_searching": "Looking for barcode with AI...",
- "num_ocr_found": "Code found: {code}",
- "num_ocr_not_found": "No barcode found in image",
- "barcode_placeholder": "Enter barcode...",
- "quick_name_divider": "or type the name",
- "quick_name_placeholder": "E.g.: Apples, Zucchini, Bread...",
- "manual_entry": "✏️ Manual Entry",
- "ai_identify": "🤖 Identify with AI",
- "hint": "Scan the barcode, type the product name, or use AI to identify it",
- "debug_toggle": "🐛 Debug Log",
- "barcode_acquired": "🔖 Barcode scanned: {code}",
- "scan_barcode": "🔖 Scan Barcode",
- "create_named": "Create {name}",
- "new_without_barcode": "New product without barcode",
- "stock_in_pantry": "Already in pantry:",
- "status_ready": "Point camera at barcode",
- "status_scanning": "Scanning...",
- "status_partial": "Detected: {code} — verifying...",
- "status_invalid": "Invalid: {code} — retrying",
- "status_confirmed": "Confirmed!",
- "status_parallel": "Using combined scan methods..."
- },
- "action": {
- "title": "What do you want to do?",
- "add_btn": "📥 ADD",
- "add_sub": "to pantry/fridge",
- "use_btn": "USE",
- "use_sub": "from pantry/fridge",
- "have_title": "📦 Already in stock!",
- "add_more_sub": "add more",
- "use_qty_sub": "how much you used",
- "throw_btn": "🗑️ DISCARD",
- "throw_sub": "throw away",
- "edit_sub": "expiry, location…",
- "create_recipe_btn": "Recipe",
- "related_stock_title": "Also at home"
- },
- "add": {
- "title": "Add to Pantry",
- "location_label": "📍 Where do you put it?",
- "quantity_label": "📦 Quantity",
- "conf_size_label": "📦 Each package contains:",
- "conf_size_placeholder": "e.g. 300",
- "vacuum_label": "🫙 Vacuum sealed",
- "vacuum_hint": "Expiry date will be extended automatically",
- "submit": "✅ Add",
- "purchase_type_label": "🛒 This product is...",
- "new_btn": "🆕 Just bought",
- "existing_btn": "📦 I already had it",
- "remaining_label": "📦 Remaining quantity",
- "remaining_hint": "Approximately how much is left?",
- "remaining_full": "🟢 Full",
- "remaining_half": "🟠 Half",
- "estimated_expiry": "Estimated expiry:",
- "suffix_freezer": "(freezer)",
- "suffix_vacuum": "(vacuum sealed)",
- "hint_modify": "📝 You can change the date or scan it with the camera",
- "scan_expiry_title": "📷 Scan Expiry Date",
- "product_added": "✅ {name} added!{qty}",
- "suffix_freezer_vacuum": "(freezer + vacuum sealed)",
- "history_badge_tip": "Average from {n} previous entries",
- "vacuum_question": "Vacuum sealed?",
- "vacuum_saved": "🔒 Vacuum sealed!"
- },
- "use": {
- "title": "Use / Consume",
- "location_label": "📍 From where?",
- "quantity_label": "How much did you use?",
- "change": "change",
- "partial_hint": "Or specify the quantity used:",
- "partial_piece_hint": "Did you use only a part?",
- "piece": "piece",
- "one_whole": "1 whole",
- "use_all": "🗑️ Used ALL / Finished",
- "submit": "📤 Use this quantity",
- "available": "📦 Available:",
- "opened_badge": "OPENED",
- "not_in_inventory": "⚠️ Product not in inventory.",
- "expiry_warning": "⚠️ Use first the one{loc} that expires on {date} — {when}!",
- "expiry_warning_opened": "⚠️ The one{loc} has been open for {when} — use it first!",
- "throw_title": "🗑️ Discard Product",
- "throw_all": "🗑️ Discard ALL ({qty})",
- "throw_qty_label": "How much to discard?",
- "throw_qty_hint": "or enter a quantity:",
- "throw_partial_btn": "🗑️ Discard this quantity",
- "when_expired": "expired {n} days ago",
- "when_today": "expires today",
- "when_tomorrow": "expires tomorrow",
- "when_days": "expires in {n} days",
- "toast_used": "📤 Used {qty} of {name}",
- "toast_bring": "🛒 Product finished → added to Bring!",
- "toast_opened_finished": "🔓 Opened package of {name} finished!",
- "disambiguation_hint": "What do you mean by \"all done\"?",
- "disambiguation_all": "🗑️ Finish EVERYTHING ({qty})",
- "error_exceeds_stock": "⚠️ You cannot use more than you have available!",
- "use_all_confirm_title": "✅ Finish everything",
- "use_all_confirm_msg": "Confirm that you have finished the product:",
- "use_all_confirm_btn": "✅ Yes, finished",
- "throw_all_confirm_title": "🗑️ Discard everything",
- "throw_all_confirm_msg": "Do you really want to throw away the whole product?",
- "throw_all_confirm_btn": "🗑️ Yes, discard"
- },
- "product": {
- "title_new": "New Product",
- "title_edit": "Edit Product",
- "ai_fill": "📷 Take photo and identify with AI",
- "ai_fill_hint": "AI will automatically fill in the product fields",
- "name_label": "🏷️ Product Name *",
- "name_placeholder": "E.g.: Whole milk, Penne pasta...",
- "brand_label": "🏢 Brand",
- "brand_placeholder": "E.g.: Barilla, Granarolo, Mutti...",
- "category_label": "📂 Category",
- "unit_label": "📏 Unit of measure",
- "default_qty_label": "🔢 Default quantity",
- "conf_size_label": "📦 Each package contains:",
- "conf_size_placeholder": "e.g. 300",
- "notes_label": "📝 Notes",
- "notes_placeholder": "E.g.: lactose free, organic, store in fridge after opening...",
- "barcode_label": "🔖 Barcode",
- "barcode_placeholder": "Barcode (if available)",
- "barcode_hint": "⚠️ Add the barcode so next time you just need to scan it!",
- "submit": "💾 Save Product",
- "name_required": "Enter the product name",
- "conf_size_required": "Specify the package content",
- "expiry_estimated": "Estimated expiry:",
- "scan_expiry": "Scan expiry date",
- "expiry_hint": "📝 You can edit the date or scan it with the camera",
- "add_batch": "📦 + Batch with different expiry",
- "package_info": "📦 Package: {info}",
- "edit_catalog": "⚙️ Edit product info (name, brand, category…)",
- "not_recognized": "⚠️ Product not recognized",
- "edit_info": "✏️ Edit information",
- "modify_details": "EDIT\nexpiry, location…",
- "already_in_pantry": "📋 Already in pantry",
- "no_barcode": "No barcode",
- "unknown_product": "Unrecognized product",
- "edit_name_brand": "Edit name/brand",
- "weight_label": "Weight",
- "origin_label": "Origin",
- "labels_label": "Labels",
- "select_variant": "Select the exact variant or use AI data:"
- },
- "products": {
- "title": "📦 All Products",
- "search_placeholder": "🔍 Search product...",
- "empty": "No products in database.\nScan a product to get started!",
- "no_category": "No products in this category"
- },
- "recipes": {
- "title": "🍳 Recipes",
- "generate": "✨ Generate new recipe",
- "archive_empty": "No recipes saved. Generate your first recipe!",
- "dialog_title": "🍳 Recipe",
- "dialog_desc": "I will generate a healthy recipe using pantry ingredients, prioritizing expiring items.",
- "meal_label": "🕐 Which meal?",
- "persons_label": "👥 How many people?",
- "meal_type_label": "🎯 Meal type",
- "opt_fast": "⚡ Quick meal",
- "opt_light": "🥗 Light appetite",
- "opt_expiry": "⏰ Prioritize expiring items",
- "opt_healthy": "💚 Extra healthy",
- "opt_opened": "📦 Prioritize opened items",
- "opt_zero_waste": "♻️ Zero waste",
- "generate_btn": "✨ Generate Recipe",
- "loading_msg": "Preparing your recipe...",
- "start_cooking": "👨🍳 Cooking Mode",
- "regenerate": "🔄 Generate another one",
- "regen_choice_title": "What do you want to do with this recipe?",
- "regen_replace": "🔄 Generate another (discard this one)",
- "regen_save_new": "💾 Save to archive & generate a new one",
- "close_btn": "✅ Close",
- "ingredients_title": "🧾 Ingredients",
- "tools_title": "Equipment needed",
- "steps_title": "👨🍳 Steps",
- "no_steps": "No steps available",
- "generate_error": "Generation error",
- "stream_interrupted": "Generation interrupted (incomplete server response). Check logs or try again.",
- "persons_short": "serv.",
- "use_ingredient_title": "Use ingredient",
- "recipe_qty_label": "Recipe",
- "from_where_label": "From where?",
- "amount_label": "How much",
- "use_amount_btn": "Use this amount",
- "use_all_btn": "Use ALL / Finished",
- "packs_label": "Packs",
- "quantity_in_total": "Quantity in {unit} (total: {total})",
- "packs_of_have": "Packs of {size} (you have {count} packs)",
- "scale_wait_stable": "Wait 10s of stable weight for auto-fill…",
- "ingredient_scaled_toast": "📦 Ingredient deducted from pantry!",
- "finished_added_bring_toast": "🛒 Finished product → added to Bring!",
- "load_error": "Loading error",
- "favorite": "Add to favourites",
- "unfavorite": "Remove from favourites",
- "adjust_persons": "Persons"
- },
- "shopping": {
- "title": "🛒 Shopping List",
- "bring_loading": "Connecting to Bring!...",
- "bring_not_configured": "Bring! is not configured. Add your email and password in settings.",
- "tab_to_buy": "🛍️ To buy",
- "tab_forecast": "🧠 Forecast",
- "total_label": "💰 Estimated total",
- "section_to_buy": "🛍️ To buy",
- "suggestions_title": "💡 AI Suggestions",
- "suggestions_add": "✅ Add selected to Bring!",
- "search_prices": "🔍 Search all prices",
- "suggest_btn": "Suggest what to buy",
- "smart_title": "🧠 Smart Predictions",
- "smart_empty": "No predictions available. Add products to your pantry to receive smart predictions.",
- "smart_filter_all": "All",
- "smart_filter_critical": "🔴 Urgent",
- "smart_filter_high": "🟠 Soon",
- "smart_filter_medium": "🟡 Plan",
- "smart_filter_low": "🟢 Forecast",
- "smart_add": "🛒 Add selected to Bring!",
- "empty": "Shopping list empty!\nUse the button below to generate suggestions.",
- "already_in_list": "🛒 \"{name}\" is already in the shopping list",
- "already_in_list_short": "ℹ️ Already in the shopping list",
- "add_prompt": "Do you want to add it to the shopping list?",
- "smart_already": "📊 Smart shopping already predicts {name}",
- "all_searched": "All products have already been searched. Use 🔄 to search individual ones.",
- "search_complete": "Search complete: {count} products",
- "removed_sufficient": "🧹 {removed} product(s) with sufficient stock removed from the list",
- "suggest_buy": "🛒 Buy: {qty} {unit}",
- "suggest_buy_approx": "🛒 At least: {qty} {unit}",
- "suggest_buy_tip": "Suggested quantity based on your last 14 days of consumption",
- "suggest_buy_approx_tip": "Minimum estimate based on consumption (buy the nearest package size)",
- "bring_badge": "🛒 Already on Bring!",
- "add_urgent_toast": "🔴 {n} urgent product(s) automatically added to Bring!",
- "migration_done": "✅ {migrated} updated, {skipped} already ok",
- "added_to_bring": "🛒 {n} products added to Bring!",
- "added_to_bring_skip": "{n} already present",
- "all_on_bring": "All products were already on Bring!",
- "freq_high": "📈 Frequent",
- "freq_regular": "📊 Regular",
- "freq_occasional": "📉 Occasional",
- "out_of_stock": "Out of stock",
- "scan_toast": "📷 Scan: {name}",
- "empty_category": "No products in this category",
- "session_empty": "🛒 No products yet",
- "urgency_critical": "Urgent",
- "urgency_high": "Soon",
- "urgency_medium": "Plan",
- "urgency_low": "Forecast",
- "urgency_medium_short": "Medium",
- "urgency_low_short": "Ok",
- "tag_urgent": "🔴 Urgent",
- "tag_priority": "⭐ Priority",
- "tag_check": "✅ Check",
- "smart_already_predicted": "📊 Smart shopping already predicts {name}{urgency}.",
- "item_removed": "✅ {name} removed from list!",
- "urgency_spec_critical": "⚡ Urgent",
- "urgency_spec_high": "🟠 Soon",
- "bring_add_n": "Add {n} to Bring!",
- "bring_add_selected": "Add selected to Bring!",
- "bring_adding": "Adding...",
- "bring_added_one": "1 product added to Bring!",
- "bring_added_many": "{n} products added to Bring!",
- "bring_skipped": "({n} already in list)",
- "force_sync": "Force Bring! sync",
- "scan_target_label": "You are looking for",
- "scan_target_found": "Found! Remove from list",
- "bring_add_one": "Add 1 product to Bring!",
- "bring_add_many": "Add {n} products to Bring!",
- "syncing": "Syncing…",
- "sync_done": "Sync completed",
- "price_searching": "Searching...",
- "search_action": "Search",
- "open_action": "Open",
- "not_found": "Not found",
- "search_price": "Search price",
- "tap_to_scan": "Tap to scan",
- "tag_title": "Tag",
- "remove_title": "Remove",
- "found_count": "{found}/{total} products found",
- "savings_offers": "· 🏷️ You save €{amount} with offers",
- "searching_progress": "Searching {current}/{total}...",
- "remove_error": "Removal error",
- "btn_fetch_prices": "Find prices",
- "price_total_label": "💰 Estimated total:",
- "price_loading": "Looking up prices…",
- "price_not_found": "price n/a",
- "suggest_loading": "Analyzing...",
- "suggest_error": "Suggestion generation error",
- "priority_high": "High",
- "priority_medium": "Medium",
- "priority_low": "Low",
- "smart_last_update": "Updated {time}",
- "names_already_updated": "All names are already up to date",
- "pantry_hint": "Already at home: {qty}"
- },
- "ai": {
- "title": "🤖 AI Identification",
- "capture": "📸 Take Photo",
- "retake": "🔄 Retake",
- "hint": "Take a photo of the product and AI will try to identify it",
- "identifying": "🤖 Identifying product...",
- "no_api_key": "⚠️ Gemini API key not configured.\nAdd GEMINI_API_KEY to the .env file on the server.",
- "fields_filled": "✅ Fields filled by AI",
- "use_data": "✅ Use AI data",
- "use_data_no_barcode": "✅ Use AI data (no barcode)"
- },
- "log": {
- "title": "📒 Operations Log",
- "type_added": "Added",
- "type_waste": "Discarded",
- "type_used": "Used",
- "type_bring": "Added to Bring!",
- "undone_badge": "Undone",
- "undo_title": "Undo this operation",
- "load_error": "Error loading log",
- "empty": "No operations recorded.",
- "undo_action_remove": "removal of",
- "undo_action_restore": "restock of",
- "undo_confirm": "Undo this operation?\n→ {action} {name}",
- "undo_success": "↩ Operation undone for {name}",
- "already_undone": "Operation already undone",
- "too_old": "Cannot undo operations older than 24 hours",
- "undo_error": "Error during undo",
- "recipe_prefix": "Recipe"
- },
- "chat": {
- "title": "Gemini Chef",
- "welcome": "Hi! I'm your kitchen assistant",
- "welcome_desc": "Ask me to make you a juice, a snack, a quick dish... I know your pantry, your appliances and your preferences!",
- "suggestion_snack": "🍿 Quick snack",
- "suggestion_juice": "🥤 Juice/Smoothie",
- "suggestion_light": "🥗 Something light",
- "suggestion_expiry": "⏰ Use expiring items",
- "clear": "New conversation",
- "placeholder": "Ask something...",
- "cleared": "Chat cleared",
- "suggestion_snack_text": "What can I make for a quick snack?",
- "suggestion_juice_text": "Make me a juice or smoothie with what I have",
- "suggestion_light_text": "I'm hungry but want something light",
- "suggestion_expiry_text": "What's about to expire and how can I use it?",
- "transfer_to_recipes": "Transfer to Recipes",
- "transferring": "Transferring...",
- "transferred": "Added to Recipes!",
- "open_recipe": "Open recipe",
- "quick_recipe_prompt": "Suggest a quick recipe FOR ONE PERSON using the products that expire first! Ignore freezer items, focus on fridge and pantry."
- },
- "cooking": {
- "close": "Close",
- "tts_btn": "Read aloud",
- "restart": "↺ Restart",
- "replay": "🔊 Replay",
- "timer": "⏱️ {time} · Timer",
- "prev": "◀ Previous",
- "next": "Next ▶",
- "ingredient_used": "✔️ Deducted",
- "ingredient_use_btn": "Use",
- "ingredient_deduct_title": "Deduct from pantry",
- "timer_expired_tts": "Timer {label} expired!",
- "timer_warning_tts": "Heads up! {label}: 10 seconds left!",
- "recipe_done_tts": "Recipe complete! Enjoy your meal!",
- "expires_chip": "exp. {date}",
- "finish": "✅ Finish",
- "step_fallback": "Step {n}",
- "zerowaste_label": "♻️ Scrap",
- "zerowaste_tip_title": "Zero-waste tip"
- },
- "settings": {
- "title": "⚙️ Settings",
- "tab_api": "API Keys",
- "tab_bring": "Bring!",
- "tab_recipe": "Recipes",
- "tab_mealplan": "Weekly Plan",
- "tab_appliances": "Appliances",
- "tab_spesa": "Online Shopping",
- "tab_camera": "Camera",
- "tab_security": "Security",
- "tab_tts": "Voice (TTS)",
- "tab_language": "Language",
- "tab_scale": "Smart Scale",
- "gemini": {
- "title": "🤖 Google Gemini AI",
- "hint": "API key for product identification, expiry dates and recipes.",
- "key_label": "Gemini API Key"
- },
- "bring": {
- "title": "🛒 Bring! Shopping List",
- "hint": "Credentials for the Bring! shopping list integration.",
- "email_label": "📧 Bring! Email",
- "password_label": "🔒 Bring! Password"
- },
- "price": {
- "title": "💰 Price Estimation (AI)",
- "hint": "Show estimated cost per product in the shopping list using AI.",
- "enabled_label": "Enable price estimation",
- "country_label": "🌍 Reference country",
- "currency_label": "💱 Currency",
- "update_label": "🔄 Refresh prices every",
- "update_suffix": "months"
- },
- "recipe": {
- "title": "🍳 Recipe Preferences",
- "hint": "Configure the default options for recipe generation.",
- "persons_label": "👥 Default servings",
- "options_label": "🎯 Default recipe options",
- "fast": "⚡ Quick Meal",
- "light": "🥗 Light Meal",
- "expiry": "⏰ Expiry Priority",
- "healthy": "💚 Extra Healthy",
- "opened": "📦 Open Items Priority",
- "zerowaste": "♻️ Zero Waste",
- "dietary_label": "🚫 Intolerances / Restrictions",
- "dietary_placeholder": "E.g.: gluten free, lactose free, vegetarian..."
- },
- "mealplan": {
- "title": "📅 Weekly Meal Plan",
- "hint": "Set the meal type for each day. It will be used as a guide in recipe generation.",
- "enabled": "✅ Enable weekly meal plan",
- "legend": "🌤️ = Lunch · 🌙 = Dinner · Tap a badge to change it.",
- "types_title": "📋 Available types",
- "reset_btn": "↺ Restore defaults"
- },
- "appliances": {
- "title": "🔌 Available Appliances",
- "hint": "Indicate the appliances you have. They will be considered in recipe generation.",
- "new_placeholder": "E.g.: Bread machine, Thermomix, Air fryer...",
- "quick_title": "Quick add:",
- "oven": "🔥 Oven",
- "microwave": "📡 Microwave",
- "air_fryer": "🍟 Air fryer",
- "bread_maker": "🍞 Bread maker",
- "bimby": "🤖 Thermomix/Cookeo",
- "mixer": "🌀 Stand mixer",
- "steamer": "♨️ Steamer",
- "pressure_cooker": "🫕 Pressure cooker",
- "toaster": "🍞 Toaster",
- "blender": "🍹 Blender",
- "empty": "No appliances added"
- },
- "spesa": {
- "title": "🛍️ Online Shopping",
- "hint": "Configure the online shopping provider.",
- "provider_label": "🏪 Provider",
- "email_label": "📧 Email",
- "password_label": "🔒 Password",
- "login_btn": "🔐 Login",
- "ai_prompt_label": "🤖 AI product selection prompt",
- "ai_prompt_placeholder": "Instructions for AI when choosing between multiple products...",
- "ai_prompt_hint": "AI uses this prompt to choose the most appropriate product from results. Leave empty for default behavior.",
- "configure_first": "Configure Online Shopping in settings first",
- "missing_credentials": "Enter email and password",
- "login_in_progress": "Signing in...",
- "login_error_prefix": "Error:",
- "login_network_error_prefix": "Network error:",
- "login_success_default": "Login successful!",
- "result_name_label": "Name",
- "result_card_label": "Card",
- "result_pickup_label": "Pickup point",
- "result_points_label": "Loyalty points",
- "connected_relogin": "✅ Connected — Sign in again",
- "connected_as": "Connected as {name}"
- },
- "camera": {
- "title": "📷 Camera",
- "hint": "Choose which camera to use for barcode scanning and AI identification.",
- "device_label": "📸 Default camera",
- "back": "📱 Rear (default)",
- "front": "🤳 Front",
- "devices_hint": "If you have multiple cameras, you can select a specific one from the list above after granting permissions.",
- "detect_btn": "🔄 Detect cameras"
- },
- "security": {
- "title": "🔒 HTTPS Certificate",
- "hint": "If the browser shows the error \"Your connection is not private\" (ERR_CERT_AUTHORITY_INVALID), you need to install the CA certificate on the device.",
- "download_btn": "📥 Download CA Certificate",
- "token_title": "🔑 Settings Token",
- "token_label": "Access token",
- "token_hint": "If `SETTINGS_TOKEN` is configured in the server's `.env`, enter the token here before saving settings. Leave empty if not configured.",
- "token_placeholder": "(empty = no protection)",
- "token_required_hint": "🔒 This server requires a token to save settings.",
- "cert_instructions": "Instructions for Chrome (Android): 1. Download the certificate above 2. Go to Settings → Security & Privacy → More security settings → Install from device storage 3. Select the downloaded EverShelf_CA.crt file 4. Choose \"CA\" and confirm 5. Restart Chrome
Instructions for Chrome (PC): 1. Download the certificate above 2. Go to chrome://settings/certificates (or Settings → Privacy and security → Security → Manage certificates) 3. Tab \"Authorities\" → Import → select the file 4. Check \"Trust this certificate for identifying websites\" 5. Restart Chrome"
- },
- "tts": {
- "title": "🔊 Voice & TTS",
- "hint": "Configure text-to-speech via any external REST API. Recipe steps and expired timers will be sent to the configured endpoint.",
- "enabled": "✅ Enable TTS",
- "engine_label": "⚙️ TTS Engine",
- "engine_browser": "🔇 Browser (offline, no configuration required)",
- "engine_server": "🌐 External server (Home Assistant, REST API...)",
- "voice_label": "🗣️ Voice",
- "rate_label": "⚡ Speed",
- "pitch_label": "🎵 Pitch",
- "url_label": "🌐 Endpoint URL",
- "method_label": "📡 HTTP Method",
- "auth_label": "🔐 Authentication",
- "auth_bearer": "Bearer Token",
- "auth_custom": "Custom Header",
- "auth_none": "None",
- "token_label": "🔑 Bearer Token",
- "custom_header_name": "📋 Header name",
- "custom_header_value": "📋 Header value",
- "content_type_label": "📄 Content-Type",
- "payload_key_label": "🗝️ Text field in payload",
- "payload_key_hint": "Name of the JSON field that will contain the text to read (e.g.: message, text).",
- "extra_fields_label": "➕ Extra fields (JSON)",
- "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}",
- "extra_fields_hint": "Additional fields to include in the payload, in JSON format. Leave empty if not needed.",
- "test_sound_btn": "🔔 Run Sound Test",
- "test_btn": "🔊 Send Test Voice",
- "voices_loading": "Loading voices…",
- "voice_not_supported": "Voice not supported by this browser",
- "voices_none": "No voices available on this device",
- "voices_hint": "Available voices depend on the OS and browser. On macOS/iOS the Paola (Italian) voice is available. Press ↺ if the list does not load.",
- "url_missing": "⚠️ Endpoint URL missing.",
- "test_sending": "⏳ Sending…",
- "test_ok": "✅ Response {code} — check that the speaker has spoken.",
- "heard_question": "Did you hear the voice?",
- "heard_yes": "Yes, I heard it",
- "heard_no": "No, I didn't hear it",
- "test_ok_kiosk": "TTS is working.",
- "test_fail_steps": "Check: 1) media volume is not 0; 2) Google Text-to-Speech is installed and updated; 3) Italian voice package is downloaded in Android TTS settings."
- },
- "language": {
- "title": "🌐 Language",
- "hint": "Select the interface language.",
- "label": "🌐 Language",
- "restart_notice": "The page will reload to apply the new language."
- },
- "screensaver": {
- "label": "Enable screensaver",
- "card_title": "🌙 Screensaver",
- "card_hint": "Shows a clock with useful facts after 5 minutes of inactivity. Disabled by default.",
- "timeout_1": "1 minute",
- "timeout_2": "2 minutes",
- "timeout_5": "5 minutes",
- "timeout_10": "10 minutes",
- "timeout_15": "15 minutes",
- "timeout_30": "30 minutes",
- "timeout_60": "1 hour",
- "start_after": "⏱️ Start after"
- },
- "scale": {
- "title": "⚖️ Smart Scale",
- "hint": "Connect a Bluetooth scale via the Android gateway to automatically read weight.",
- "tab": "Smart Scale",
- "enabled": "✅ Enable smart scale",
- "url_label": "🌐 WebSocket Gateway URL",
- "url_placeholder": "ws://192.168.1.x:8765",
- "url_hint": "URL shown by the Android app (same Wi-Fi network). E.g.:",
- "test_btn": "🔗 Test connection",
- "download_btn": "📥 Download Android Gateway (APK)",
- "download_hint": "Android app that bridges your BLE scale and EverShelf.",
- "download_sub": "Source: evershelf-scale-gateway/ in the project root",
- "live_weight": "real-time weight",
- "auto_reconnect": "🔁 Reconnect: automatic",
- "kiosk_title": "📡 BLE Scale integrated in Kiosk",
- "kiosk_hint": "The scale is directly managed by the internal BLE Gateway of the kiosk. To pair a new device, use the configuration wizard.",
- "kiosk_reconfigure": "🔄 Reconfigure BLE Scale",
- "ble_protocols": "
🔌 Supported BLE protocols:
Bluetooth SIG Weight Scale (0x181D)
Bluetooth SIG Body Composition (0x181B) — weight, fat, BMI
Xiaomi Mi Body Composition Scale 2
Generic — automatic heuristic for 100+ models
"
- },
- "kiosk": {
- "hint": "Turn an Android tablet into an always-on EverShelf panel with built-in BLE scale gateway.",
- "download_btn": "📥 Download EverShelf Kiosk (APK)",
- "download_sub": "Full-screen kiosk mode + integrated scale gateway. Source: evershelf-kiosk/",
- "native_title": "Kiosk Configuration",
- "native_hint": "Server URL, BLE scale, screensaver and setup wizard.",
- "native_btn": "Open kiosk configuration",
- "native_tap_hint": "Tap the gear button at the top right",
- "native_update_hint": "Update the kiosk app to use this feature",
- "update_title": "Kiosk Update",
- "check_updates_btn": "🔍 Check for updates",
- "needs_update": "⚠️ The installed kiosk does not support this feature. Update the kiosk app to enable it."
- },
- "saved": "✅ Configuration saved!",
- "saved_local": "✅ Configuration saved locally",
- "saved_local_error": "⚠️ Saved locally, server error: {error}",
- "theme": {
- "title": "🌙 Appearance",
- "hint": "Choose the interface theme.",
- "label": "🌙 Theme",
- "off": "☀️ Light",
- "on": "🌙 Dark",
- "auto": "🔄 Automatic (time of day)"
- },
- "zerowaste": {
- "card_title": "♻️ Zero-waste tips",
- "card_hint": "During cooking, show tips on how to reuse scraps generated in each step (peels, cooking water, etc.). Disabled by default.",
- "label": "Show tips during cooking"
- },
- "backup": {
- "tab": "Backup",
- "local_title": "Local Backup",
- "local_hint": "Daily database snapshot. Configure how many days of backups to keep.",
- "enabled": "Enable daily automatic backup",
- "retention_days": "Retention (days)",
- "retention_info": "Backups are kept for",
- "backup_now": "Backup Now",
- "backing_up": "Backing up…",
- "backed_up": "Backup complete",
- "backup_error": "Backup error",
- "last_backup": "Last backup",
- "no_backup_yet": "No backup has been created yet",
- "list_empty": "No backups available",
- "restore_btn": "Restore",
- "restore_confirm": "Restore backup",
- "delete_btn": "Delete",
- "delete_confirm": "Delete backup",
- "gdrive_title": "Google Drive",
- "gdrive_hint": "Automatically back up to Google Drive via OAuth 2.0. No external libraries required.",
- "gdrive_enabled": "Enable Google Drive backup",
- "gdrive_folder_id": "Drive Folder ID",
- "gdrive_folder_id_hint": "Copy the ID from the Drive folder URL: …/folders/ID",
- "gdrive_retention_days": "Drive retention (days, 0=keep all)",
- "gdrive_test": "Test Connection",
- "gdrive_ok": "Connection successful!",
- "gdrive_error": "Connection failed",
- "gdrive_push_now": "Upload to Drive Now",
- "gdrive_pushing": "Uploading…",
- "gdrive_pushed": "Uploaded to Drive",
- "gdrive_wizard_hint": "Optional: automatically back up to Google Drive daily via OAuth 2.0.",
- "gdrive_skip": "Skip — configure later in Settings",
- "gdrive_client_id": "Client ID",
- "gdrive_client_secret": "Client Secret",
- "gdrive_redirect_uri_hint": "Add http://localhost as an authorized redirect URI in Google Cloud Console. This works on any server, even without a public domain.",
- "gdrive_code_title": "Paste the authorization URL or code",
- "gdrive_code_hint": "After authorizing, the browser will open http://localhost and may show a connection error — that is expected. Copy the URL from the address bar (e.g. http://localhost/?code=4%2F0A...) and paste it here.",
- "gdrive_code_submit": "Submit",
- "gdrive_code_empty": "Paste the URL or authorization code first",
- "gdrive_redirect_uri_label": "Redirect URI (add this in Google Cloud Console):",
- "gdrive_oauth_authorize": "Authorize with Google",
- "gdrive_oauth_authorized": "Authorized",
- "gdrive_oauth_not_authorized": "Not authorized yet",
- "gdrive_oauth_window_opened": "Browser window opened — authorize and come back",
- "gdrive_oauth_how_to": "How to set up OAuth 2.0 (step by step)",
- "gdrive_oauth_steps": "
Enable the Google Drive API: APIs & Services → Enable APIs → Google Drive API
Go to APIs & Services → Credentials → Create Credentials → OAuth client ID
Application type: Web application; add http://localhost as an Authorized redirect URI
Copy the Client ID and Client Secret into the fields above and save
Click Authorize with Google, sign in and grant access
The browser will open http://localhost (a connection error is expected): copy the URL from the address bar and paste it in the field that appears below
"
- },
- "info": {
- "tab": "Info",
- "ai_title": "Gemini AI — Token Usage",
- "ai_hint": "Monthly consumption and estimated cost for the current API key.",
- "loading": "Loading…",
- "total_tokens": "Total tokens",
- "est_cost": "Est. cost",
- "input_tok": "Input tokens",
- "output_tok": "Output tokens",
- "ai_calls": "Calls",
- "by_action": "Breakdown by function",
- "by_model": "Breakdown by model",
- "pricing_note": "Gemini reference pricing: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.",
- "system_title": "System",
- "db_size": "Database",
- "log_size": "Logs",
- "log_level": "Log level",
- "ai_overview": "AI usage overview, inventory and system status",
- "calls_unit": "calls",
- "inv_title": "Inventory",
- "inv_active": "Active",
- "inv_products": "Total products",
- "inv_expiring": "Expiring (7d)",
- "inv_expired": "Expired",
- "inv_finished": "Finished",
- "act_title": "Monthly activity",
- "act_tx_month": "Movements",
- "act_restock": "Restocks",
- "act_use": "Usages",
- "act_new_products": "New products",
- "act_tx_year": "Yearly movements",
- "price_cache": "Price cache",
- "cache_entries": "products",
- "last_backup": "Last backup",
- "bring_days": "token expires in {n} days",
- "bring_expired": "token expired",
- "year_label": "Year {year}",
- "currency_title": "Currency",
- "currency_hint": "The currency used for all costs and prices in the app."
- },
- "tab_general": "General",
- "shopping": {
- "tab": "Shopping list",
- "title": "Shopping list",
- "hint": "Configure the built-in shopping list or connect Bring!.",
- "enable_label": "Enable shopping list",
- "mode_label": "Provider",
- "mode_internal": "Built-in (no Bring!)",
- "mode_bring": "Bring! (external app)",
- "bring_section_title": "Bring! configuration",
- "ai_section_title": "AI assistance",
- "smart_suggestions_label": "AI suggestions",
- "forecast_label": "Forecast low-stock products",
- "auto_add_label": "Auto-add to list when",
- "auto_add_suffix": "remaining in stock (0 = only when empty)"
- },
- "ha": {
- "tab": "Home Assistant",
- "title": "Home Assistant",
- "hint": "Connect EverShelf to Home Assistant for automations, push notifications and REST sensors.",
- "enabled": "Enable Home Assistant integration",
- "connection_title": "Connection",
- "url_label": "Home Assistant URL",
- "url_placeholder": "http://192.168.1.50:8123",
- "url_hint": "Base URL of your Home Assistant instance (e.g. http://homeassistant.local:8123).",
- "token_label": "Long-Lived Access Token",
- "token_hint": "Generate from HA Profile → Security → Long-Lived Access Tokens.",
- "token_placeholder": "eyJhbGci...",
- "token_saved": "Token saved (hidden for security)",
- "test_btn": "Test connection",
- "test_ok": "Connected to {version}",
- "test_fail": "Connection failed: {error}",
- "test_bad_token": "HA reachable but token is invalid",
- "testing": "Testing…",
- "error_no_url": "Please enter the Home Assistant URL first.",
- "tts_title": "TTS on Smart Speaker",
- "tts_hint": "Read recipe steps aloud on a Home Assistant media player.",
- "tts_entity_label": "Media player entity ID",
- "tts_entity_placeholder": "media_player.living_room",
- "tts_entity_hint": "Entity ID of the HA media player. Find it in HA: Developer Tools → States.",
- "tts_platform_label": "TTS platform",
- "tts_platform_speak": "tts.speak (recommended)",
- "tts_platform_notify": "notify.* (notification service)",
- "tts_apply_btn": "Apply HA preset to TTS tab",
- "tts_apply_hint": "Pre-fills the TTS tab with the Home Assistant URL and token.",
- "tts_preset_applied": "HA preset applied to TTS tab.",
- "webhook_title": "Webhook Automations",
- "webhook_hint": "Send data to Home Assistant when pantry events occur. Create an HA automation with a Webhook trigger and paste the generated ID here.",
- "webhook_id_label": "Webhook ID",
- "webhook_id_placeholder": "evershelf_webhook_abc123",
- "webhook_id_hint": "ID of the webhook created in HA. Copy from: HA → Settings → Automations → Create → Webhook Trigger.",
- "webhook_events_label": "Notify on these events",
- "event_expiry": "Expiring products (daily)",
- "event_shopping": "Item added to shopping list",
- "event_stock": "Stock level updated",
- "expiry_days_label": "Expiry lead time (days)",
- "expiry_days_hint": "Send the expiry alert N days before the expiry date.",
- "webhook_help": "In HA: Settings → Automations → Create automation → Trigger: Webhook → copy the generated ID above.",
- "notify_title": "Push Notifications",
- "notify_hint": "Send push notifications to your phone via a Home Assistant notify service.",
- "notify_service_label": "Notify service",
- "notify_service_placeholder": "notify.mobile_app_my_phone",
- "notify_service_hint": "HA notify service name (e.g. notify.mobile_app_phone). Leave empty to disable.",
- "sensor_title": "REST Sensors",
- "sensor_hint": "Add to configuration.yaml to create EverShelf sensors in Home Assistant.",
- "sensor_copy_btn": "Copy YAML",
- "sensor_copied": "YAML copied to clipboard!",
- "save_btn": "Save HA settings",
- "ha_hint": "If you use Home Assistant, use the Home Assistant tab to configure TTS, webhooks and sensors."
- }
- },
- "expiry": {
- "today": "TODAY",
- "tomorrow": "Tomorrow",
- "days": "{days} days",
- "expired_days": "{days}d ago",
- "expired_yesterday": "Yesterday",
- "expired_today": "Today",
- "badge_today": "⚠️ Expires today!",
- "badge_tomorrow": "⏰ Tomorrow",
- "badge_tomorrow_long": "⏰ Expires tomorrow",
- "badge_days": "⏰ {n} days",
- "badge_expired_ago": "⚠️ Expired {n}d ago",
- "badge_expired": "⛔ Expired!",
- "badge_stable": "✅ Stable",
- "badge_expiring_short": "⏰ Exp. in {n}d",
- "badge_ok_still": "✅ Still {n}d",
- "badge_expires_red": "🔴 Exp. in {n}d",
- "badge_expires_yellow": "🟡 Exp. in {n}d",
- "badge_expired_bare": "⚠️ Expired",
- "badge_expires_warn": "⚠️ Exp. in {n}d",
- "badge_days_left": "⏳ ~{n}d left",
- "days_approx": "~{n} days",
- "weeks_approx": "~{n} weeks",
- "months_approx": "~{n} months",
- "years_approx": "~{n} years",
- "expired_today_long": "Expired today",
- "expired_ago_long": "Expired {n} days ago",
- "expired_suffix": "— Expired!",
- "expired_suffix_ok": "— Expired (still ok)",
- "expired_suffix_warning": "— Expired (check first)",
- "opened_ago_long": "Opened {n} days ago",
- "opened_today_long": "Opened today",
- "opened_suffix": "— Opened too long!",
- "opened_suffix_ok": "— Opened (still ok)",
- "opened_suffix_warning": "— Opened (check first)",
- "days_compact": "{n}d",
- "badge_check_soon": "Check soon"
- },
- "status": {
- "ok": "OK",
- "check": "Check",
- "discard": "Discard",
- "tip_freezer_ok": "In freezer: still safe (~{n}d margin)",
- "tip_freezer_check": "In freezer for a long time, may have lost quality. Consume soon",
- "tip_freezer_danger": "In freezer too long, risk of freezer burn and degradation",
- "tip_highRisk_check": "Expired recently, check smell and appearance before consuming",
- "tip_highRisk_danger": "Perishable product expired: discard for safety",
- "tip_medRisk_check1": "Check appearance and smell before consuming",
- "tip_medRisk_check2": "Expired a while ago, check carefully before use",
- "tip_medRisk_danger": "Too long since expiry, better to discard",
- "tip_lowRisk_ok": "Long-lasting product, still safe to consume",
- "tip_lowRisk_check": "Expired over a month ago, check package integrity",
- "tip_lowRisk_danger": "Expired too long ago, better not to risk it"
- },
- "toast": {
- "product_saved": "Product saved!",
- "product_created": "Product created!",
- "product_updated": "✅ Product updated!",
- "product_removed": "Product removed",
- "updated": "Updated!",
- "quantity_confirmed": "✓ Quantity confirmed",
- "added_to_inventory": "✅ {name} added!",
- "removed_from_list": "✅ {name} removed from the list!",
- "removed_from_list_short": "Removed from the list",
- "added_to_shopping": "🛒 Added to the shopping list!",
- "removed_from_shopping": "🛒 Removed from the shopping list",
- "finished_to_bring": "🛒 Product finished → added to Bring!",
- "thrown_away": "🗑️ {name} thrown away!",
- "thrown_away_partial": "🗑️ Thrown away {qty} {unit} of {name}",
- "finished_all": "📤 {name} finished!",
- "vacuum_sealed": "{name} saved as vacuum sealed",
- "product_finished_confirmed": "✅ Removed — add it again when you restock",
- "appliance_added": "Appliance added",
- "item_added": "{name} added"
- },
- "antiwaste": {
- "title": "🌱 Anti-Waste Report",
- "grade_label": "Grade",
- "you": "You",
- "avg_label": "Avg",
- "better": "🎉 You lose {diff}% less than the {country}!",
- "worse": "⚠️ You lose more than the {country}. Room for improvement!",
- "on_par": "→ You're at the {country}. You can do better!",
- "saved_money": "~{amount}/month saved",
- "saved_meals": "~{n} meals saved",
- "saved_co2": "{n} kg CO₂ avoided",
- "trend_title": "Trend (last 3 months)",
- "months_ago_2": "-60 days",
- "months_ago_1": "-30 days",
- "this_month": "Now",
- "country_it": "Italian avg",
- "country_de": "German avg",
- "country_en": "US average",
- "source": "Sources: REDUCE, Eurostat, USDA 2021",
- "live_on": "Live data",
- "live_off": "Offline",
- "meals": "meals",
- "annual_info": "📅 You ~{you} kg/yr · avg ~{avg} kg/yr",
- "badge_rate": "loss rate",
- "badge_saved_money": "saved vs avg",
- "badge_wasted": "items lost",
- "badge_better": "less than avg"
- },
- "error": {
- "generic": "Error",
- "network": "Network error",
- "no_api_key": "Configure the API key in settings",
- "loading": "Error loading product",
- "not_found": "Product not found",
- "not_found_manual": "Product not found. Enter it manually.",
- "search": "Search error. Try again.",
- "search_short": "Search error",
- "save": "Error saving",
- "connection": "Connection error",
- "camera": "Cannot access camera",
- "bring_add": "Error adding to Bring!",
- "bring_connection": "Bring! connection error",
- "identification": "Identification error",
- "ai_quota": "AI quota exhausted. Please try again in a few minutes.",
- "barcode_empty": "Enter a barcode",
- "barcode_format": "Barcode must contain only numbers (4-14 digits)",
- "min_chars": "Type at least 2 characters",
- "not_in_inventory": "Product not in inventory",
- "appliance_exists": "Appliance already exists",
- "already_exists": "Already exists",
- "network_retry": "Connection error. Try again.",
- "select_items": "Select at least one product",
- "server_offline": "Server connection lost",
- "server_restored": "Server connection restored",
- "server_retry": "Retry",
- "unknown": "Unknown error",
- "prefix": "Error",
- "no_inventory_entry": "No inventory entry found",
- "offline_title": "No connection",
- "offline_subtitle": "The app cannot reach the server. Check your Wi-Fi connection.",
- "offline_checking": "Checking connection…",
- "offline_restored": "Connection restored!",
- "offline_continue": "Continue in offline mode",
- "offline_reading_cache": "Reading from local cache",
- "offline_ops_pending": "{n} operations pending",
- "offline_synced": "{n} operations synced",
- "offline_ai_disabled": "Not available offline",
- "offline_cache_ready": "Offline — {n} items cached"
- },
- "confirm_placeholder_search": null,
- "confirm": {
- "remove_item": "Do you really want to remove this product from inventory?",
- "kiosk_exit": "Exit kiosk mode?",
- "cancel": "Cancel",
- "proceed": "Confirm",
- "discard_one": "Discard 1 piece"
- },
- "location": {
- "dispensa": "Pantry",
- "frigo": "Fridge",
- "freezer": "Freezer"
- },
- "edit": {
- "title": "Edit {name}",
- "unknown_hint": "Enter the product name and information",
- "label_name": "🏷️ Product name",
- "choose_location_title": "Which location?",
- "choose_location_hint": "Choose the location to edit:",
- "confirm_large_qty": "You are setting the quantity to {qty} {unit}. This seems unusually high. Confirm?"
- },
- "screensaver": {
- "recipe_btn": "Recipes",
- "scan_btn": "Scan product"
- },
- "days": {
- "mon": "Monday",
- "tue": "Tuesday",
- "wed": "Wednesday",
- "thu": "Thursday",
- "fri": "Friday",
- "sat": "Saturday",
- "sun": "Sunday",
- "mon_short": "Mon",
- "tue_short": "Tue",
- "wed_short": "Wed",
- "thu_short": "Thu",
- "fri_short": "Fri",
- "sat_short": "Sat",
- "sun_short": "Sun"
- },
- "meal_types": {
- "lunch": "Lunch",
- "dinner": "Dinner",
- "colazione": "Breakfast",
- "merenda": "Snack",
- "dolce": "Dessert",
- "succo": "Fruit Juice",
- "pranzo": "Lunch",
- "cena": "Dinner"
- },
- "scale": {
- "status_connected": "Scale connected",
- "status_searching": "Gateway connected, waiting for scale…",
- "status_disconnected": "Scale gateway unreachable",
- "status_error": "Gateway connection error",
- "not_connected": "Scale gateway not connected",
- "read_btn": "⚖️ Read from scale",
- "reading_title": "Scale reading",
- "place_on_scale": "Place the product on the scale…",
- "waiting_stable": "Weight will be captured automatically once the reading is stable.",
- "no_url": "Enter the gateway URL",
- "testing": "⏳ Testing connection…",
- "connected_ok": "Gateway connection successful!",
- "timeout": "Timeout: no response from gateway",
- "error_connect": "Cannot connect to gateway",
- "tab": "Smart Scale",
- "low_weight": "Weight < 10 g · enter manually\n(auto-reading requires at least 10 g)",
- "density_hint": "(density {density} g/ml)",
- "ml_hint": "(will be converted to ml)",
- "weight_detected": "Weight detected — wait 10s for stability…",
- "weight_too_low": "Weight too low — waiting…",
- "stable": "✓ Stable",
- "auto_confirm": "✅ {val} {unit} — auto-confirm in 5s (tap to cancel)",
- "cancelled_replace": "Cancelled — replace the ingredient on the scale to resume"
- },
- "prediction": {
- "expected_qty": "Expected: {expected} {unit}",
- "actual_qty": "Current: {actual} {unit}",
- "check_suggestion": "Check or weigh the remaining quantity"
- },
- "date": {
- "today": "📅 Today",
- "yesterday": "📅 Yesterday"
- },
- "scanner": {
- "title_barcode": "🔖 Scan Barcode",
- "barcode_hint": "Frame the product barcode",
- "barcode_manual_placeholder": "Or enter manually...",
- "barcode_use_btn": "✅ Use this code",
- "ai_identifying": "🤖 Identifying product...",
- "ai_analyzing": "🤖 AI analysis in progress...",
- "product_label_hint": "Frame the product label",
- "expiry_label_hint": "Frame the expiry date printed on the product",
- "capture_btn": "📸 Capture",
- "capture_photo_btn": "📸 Take Photo",
- "retake_btn": "🔄 Retake",
- "camera_error_hint": "Ensure you use HTTPS and have granted camera permissions. You can enter the barcode manually or use AI identification.",
- "no_barcode": "No barcode",
- "save_new_btn": "🆕 None of these — save as new",
- "expiry_found": "Date found",
- "expiry_read_fail": "Cannot read the date.",
- "expiry_raw_label": "Read"
- },
- "lowstock": {
- "title": "⚠️ Running low!",
- "message": "{name} is running low — only {qty} remaining.",
- "question": "Do you want to add it to the shopping list?",
- "yes": "🛒 Yes, add to Bring!",
- "no": "No, I'm fine for now"
- },
- "move": {
- "title": "📦 Move the rest?",
- "question": "Do you want to move the {thing} of {name} to another location?",
- "question_short": "Do you want to move the {thing} to another location?",
- "thing_opened": "opened package",
- "thing_rest": "rest",
- "stay_btn": "No, stay in {location}",
- "moved_toast": "📦 Opened package moved to {location}",
- "vacuum_restore": "Restore vacuum sealed",
- "vacuum_seal_rest": "Vacuum seal the rest"
- },
- "nova": {
- "1": "Unprocessed",
- "2": "Culinary ingredient",
- "3": "Processed",
- "4": "Ultra-processed"
- },
- "meal_plan_types": {
- "pasta": "Pasta",
- "riso": "Rice",
- "carne": "Meat",
- "pesce": "Fish",
- "legumi": "Legumes",
- "uova": "Eggs",
- "formaggio": "Cheese",
- "pizza": "Pizza",
- "affettati": "Cold Cuts",
- "verdure": "Veggies",
- "zuppa": "Soup",
- "insalata": "Salad",
- "pane": "Bread/Sandwich",
- "dolce": "Dessert",
- "libero": "Free"
- },
- "meal_sub": {
- "dolce_torta": "Cake",
- "dolce_crema": "Cream / Pudding",
- "dolce_crumble": "Crumble / Tart",
- "dolce_biscotti": "Cookies / Pastries",
- "dolce_frutta": "Fruit Dessert",
- "succo_dolce": "Sweet / Fruity",
- "succo_energizzante": "Energizing",
- "succo_detox": "Detox / Green",
- "succo_rinfrescante": "Refreshing",
- "succo_vitaminico": "Vitamin / Citrus"
- },
- "meal_plan": {
- "reset_success": "Weekly plan reset",
- "not_available": "not available in pantry",
- "suggested_by": "suggested by weekly plan"
- },
- "nutrition": {
- "title": "🥗 Food Analysis",
- "score_excellent": "😄 Excellent",
- "score_good": "🙂 Good",
- "score_improve": "😬 Improvable",
- "label_health": "🌿 Health",
- "label_variety": "🎨 Variety",
- "label_fresh": "❄️ Fresh",
- "source": "Based on {n} products in your pantry · EverShelf",
- "products_count": "products",
- "today_title": "🥗 Your pantry today",
- "products_n": "{n} products",
- "macros_title": "Estimated Macronutrients",
- "macros_proteins": "Proteins",
- "macros_carbs": "Carbohydrates",
- "macros_fat": "Fat",
- "macros_fiber": "Fibre",
- "macros_source": "Estimate based on {n} pantry products"
- },
- "facts": {
- "greeting_morning": "Good morning",
- "greeting_afternoon": "Good afternoon",
- "greeting_evening": "Good evening",
- "pantry_waiting": "{greeting}! Your Pantry awaits.",
- "expired_one": "You have 1 expired product in your pantry. Check it!",
- "expired_many": "You have {n} expired products in your pantry. Check them!",
- "expired_list": "Expired products: {names}",
- "expired_list_more": "and {n} more",
- "freezer_expired_ok": "{name} is expired, but being in the freezer it may still be fine! Check it.",
- "freezer_expired_old": "{name} in the freezer has been expired too long. Better to discard it.",
- "fridge_expired_one": "You have 1 expired product in the fridge!",
- "fridge_expired_many": "You have {n} expired products in the fridge!",
- "expiring_today": "{name} expires today! Use it right away.",
- "expiring_tomorrow": "{name} expires tomorrow. Plan ahead!",
- "expiring_days": "{name} expires in {days} days.",
- "expiring_many": "You have {n} products expiring soon.",
- "expiring_this_week": "{n} products expire this week. Plan your meals accordingly!",
- "expiring_item_loc": "{name} ({loc}) expires in {days} {dayslabel}.",
- "expiring_this_month": "{n} products will expire this month.",
- "shopping_add": "Add to list: {names} 🛒",
- "shopping_more": "and {n} more",
- "shopping_empty": "Shopping list empty. All stocked up! ✅",
- "in_fridge": "In the fridge: {name}.",
- "in_freezer": "In the freezer: {name}. Don't forget it!",
- "top_category": "Top category is {icon} {cat} with {n} products.",
- "cat_meat": "You have {n} meat products. 🥩",
- "cat_dairy": "You have {n} dairy products at home. 🥛",
- "cat_veggies": "You have {n} types of vegetables. Great for your health! 🥬",
- "cat_fruit": "You have {n} types of fruit. 🍎",
- "cat_drinks": "You have {n} drinks available. 🥤",
- "cat_frozen": "You have {n} frozen items. ❄️",
- "cat_pasta": "You have {n} types of pasta. 🍝 How about a carbonara?",
- "cat_canned": "You have {n} canned goods in your pantry. 🥫",
- "cat_snacks": "You have {n} snacks. Resist the temptation! 🍪",
- "cat_condiments": "You have {n} condiments available. 🧂",
- "item_random": "Did you know? You have {name} in {loc}.",
- "item_qty": "{name}: you have {qty}.",
- "no_expiry_count": "{n} products have no expiry date set.",
- "furthest_expiry": "The product with the furthest expiry is {name}: {months} months.",
- "high_qty": "You have a great stock of {name}: {qty}!",
- "low_qty_item": "{name} is running low. Add it to your shopping list?",
- "low_qty_count": "{n} products are almost out.",
- "morning_bread": "Good morning! You have bread for breakfast. 🍞",
- "morning_milk": "Is there milk in the fridge for a cappuccino? ☕🥛",
- "morning_fruit": "Good morning! Some fresh fruit is a great way to start. 🍎",
- "noon_pasta": "Lunchtime… How about a nice bowl of pasta? 🍝",
- "noon_salad": "A fresh salad for lunch? You have {n} vegetables! 🥗",
- "evening_meat": "For dinner you could use the meat you have. 🥩",
- "evening_fish": "How about fish for dinner? 🐟",
- "evening_expiring": "You have {n} products expiring this week — use them tonight!",
- "night_reminder": "Good night! Remember to use tomorrow: {names}.",
- "weekly_balance": "Weekly balance: +{in} added, −{out} consumed.",
- "weekly_added": "You added {n} products this week.",
- "weekly_consumed": "You consumed {n} products this week. Well done!",
- "tip_freezer": "💡 Frozen products last much longer than the expiry date.",
- "tip_bread": "💡 Frozen bread keeps its freshness for weeks.",
- "tip_fifo": "💡 To avoid waste, use products closest to expiry first (FIFO).",
- "tip_meat": "💡 Meat in the freezer can last up to 6 months safely.",
- "tip_no_refreeze": "💡 Never refreeze a thawed product. Cook it right away!",
- "tip_fridge": "💡 A tidy fridge saves you time and money.",
- "tip_canned": "💡 Opened canned goods should go in the fridge and be consumed within a few days.",
- "top_brand": "The most common brand in your pantry is {brand} with {n} products.",
- "combo_pasta": "You have pasta and condiments: ready for a first course! 🍝",
- "combo_sandwich": "Bread and meat: a quick sandwich is always a good idea! 🥪",
- "combo_balanced": "Vegetables and meat: you have everything for a balanced meal! 🥗🥩",
- "pantry_empty": "The pantry is empty! Time to go shopping. 🛒",
- "pantry_empty_scan": "No products registered. Scan something to start!",
- "location_distribution": "Distribution: {parts}",
- "day": "day",
- "days": "days"
- },
- "kiosk_session": {
- "first_item": "First item: {name}!",
- "items_two_four": "{n} items — warming up 🚀",
- "items_five_nine": "{n} items — great pace! 💪",
- "items_ten_twenty": "{n} items — almost a record 🏆",
- "items_twenty_plus": "{n} items — epic shopping! 🛒🔥",
- "duplicates_one": "1 duplicate (same thing twice)",
- "duplicates_many": "{n} duplicates (picked multiple times)",
- "top_category": "Top category: {cat} ({count}×)",
- "items_fallback": "{n} item{plural} added"
- },
- "kiosk": {
- "check_btn": "🔍 Check for updates",
- "checking": "⏳ Checking…",
- "error_check": "Error during update check",
- "error_start_install": "Error starting installation",
- "version_installed": "Installed: {v}",
- "update_available": "⬆️ New version available: {latest} (installed: {current})",
- "up_to_date": "✅ You are up to date — version {v}",
- "too_old": "⚠️ The installed kiosk is too old for automatic update checking. Press the button below to download and install the new version directly.",
- "manual_install": "⚠️ This kiosk does not support automatic installation. Manual procedure: 1. Exit the kiosk (✕ button top left) 2. Uninstall the EverShelf Kiosk app 3. Download and install the new APK from GitHub:",
- "starting_download": "⏳ Starting download…",
- "install_btn": "⬇️ Install update",
- "exit_title": "Exit kiosk",
- "refresh_title": "Refresh page"
- },
- "update": {
- "new_version": "New version",
- "btn": "Update"
- },
+ "app": {
+ "name": "EverShelf",
+ "loading": "Loading..."
+ },
+ "nav": {
+ "title": "EverShelf",
+ "home": "Home",
+ "inventory": "Pantry",
+ "recipes": "Recipes",
+ "shopping": "Shopping",
+ "log": "Log",
+ "settings": "Settings"
+ },
+ "btn": {
+ "back": "← Back",
+ "save": "💾 Save",
+ "cancel": "✕ Cancel",
+ "close": "Close",
+ "add": "✅ Add",
+ "delete": "Delete",
+ "edit": "✏️ Edit",
+ "use": "Use",
+ "edit_item": "Edit",
+ "search": "🔍 Search",
+ "go": "✅ Go",
+ "toggle_password": "👁️ Show/Hide",
+ "load_more": "Load more...",
+ "save_config": "💾 Save Configuration",
+ "save_product": "💾 Save Product",
+ "restart": "↺ Restart",
+ "reset_default": "↺ Reset to default",
+ "save_info": "💾 Save information",
+ "retry": "🔄 Retry",
+ "yes_short": "Yes",
+ "no_short": "No"
+ },
+ "form": {
+ "select_placeholder": "-- Select --"
+ },
+ "locations": {
+ "dispensa": "Pantry",
+ "frigo": "Fridge",
+ "freezer": "Freezer",
+ "altro": "Other"
+ },
+ "categories": {
+ "latticini": "Dairy",
+ "carne": "Meat",
+ "pesce": "Fish",
+ "frutta": "Fruit",
+ "verdura": "Vegetables",
+ "pasta": "Pasta & Rice",
+ "pane": "Bread & Bakery",
+ "surgelati": "Frozen",
+ "bevande": "Beverages",
+ "condimenti": "Condiments",
+ "snack": "Snacks & Sweets",
+ "conserve": "Canned Goods",
+ "cereali": "Cereals & Legumes",
+ "igiene": "Hygiene",
+ "pulizia": "Household",
+ "altro": "Other",
+ "select": "-- Select --"
+ },
+ "units": {
+ "pz": "pcs",
+ "conf": "pkg",
+ "g": "g",
+ "ml": "ml",
+ "pieces": "Pieces",
+ "grams": "Grams",
+ "box": "Package",
+ "boxes": "Packages",
+ "millilitres": "Millilitres",
+ "from": "of"
+ },
+ "shopping_sections": {
+ "frutta_verdura": "Fruits & Vegetables",
+ "carne_pesce": "Meat & Fish",
+ "latticini": "Dairy & Fresh",
+ "pane_dolci": "Bread & Sweets",
+ "pasta": "Pasta & Cereals",
+ "conserve": "Canned & Sauces",
+ "surgelati": "Frozen",
+ "bevande": "Beverages",
+ "pulizia_igiene": "Cleaning & Hygiene",
+ "altro": "Other"
+ },
+ "dashboard": {
+ "expired_title": "🚫 Expired",
+ "expiring_title": "⏰ Expiring Soon",
+ "stats_period": "📊 Last 30 days",
+ "opened_title": "📦 Opened Products",
+ "review_title": "🔍 To Review",
+ "review_hint": "Quantities that seem unusual. Confirm if correct or modify.",
+ "quick_recipe": "Quick recipe with expiring products",
+ "banner_review_title": "Anomalous quantity",
+ "banner_review_action_ok": "It's correct",
+ "banner_review_action_finish": "🗑️ All gone",
+ "banner_review_action_edit": "Correct",
+ "banner_review_action_weigh": "Weigh",
+ "banner_review_dismiss": "Dismiss",
+ "banner_prediction_title": "Consumption to review",
+ "banner_prediction_hint": "The consumption estimate adapts to recent data: confirm only if the current quantity is correct.",
+ "banner_prediction_action_confirm": "Confirm {qty} {unit}",
+ "banner_prediction_action_weigh": "Weigh now",
+ "banner_prediction_action_edit": "Update quantity",
+ "banner_expired_title": "Expired product",
+ "banner_expired_today": "Expired today",
+ "banner_expired_days": "Expired {days} days ago",
+ "banner_expired_action_use": "Use anyway",
+ "banner_expired_action_finished": "I finished it!",
+ "banner_expired_action_throw": "I threw it away",
+ "banner_expired_action_edit": "Fix date",
+ "banner_expired_action_modify": "Edit",
+ "banner_expired_action_vacuum": "Put in vacuum seal",
+ "banner_anomaly_action_edit": "Fix inventory",
+ "banner_anomaly_action_dismiss": "Quantity is correct",
+ "banner_no_expiry_title": "Missing expiry: {name}",
+ "banner_no_expiry_detail": "This product has no expiry date. Would you like to add one, or confirm it doesn't expire?",
+ "banner_no_expiry_action_set": "Set expiry date",
+ "banner_no_expiry_action_dismiss": "Doesn't expire ✓",
+ "banner_no_expiry_toast_dismissed": "Marked as 'no expiry'",
+ "banner_expiring_title": "Expiring soon",
+ "banner_expiring_today": "Expires today!",
+ "banner_expiring_tomorrow": "Expires tomorrow",
+ "banner_expiring_days": "Expires in {days} days",
+ "banner_expiring_action_use": "Use now",
+ "banner_finished_title": "finished?",
+ "banner_finished_detail": "I recorded that {name} reached zero stock. Is it really gone, or do you still have some?",
+ "banner_finished_action_yes": "Yes, it's done",
+ "banner_finished_action_no": "No, I still have some",
+ "banner_review_unusual_pkg_title": "Unusual package size",
+ "banner_review_unusual_pkg_detail": "You set a package of {qty} {unit} — the size seems very large. Check if correct or edit.",
+ "banner_review_low_qty_title": "Very low quantity",
+ "banner_review_low_qty_detail": "You only have {qty} in stock — seems very little, could be a typo. Confirm if correct.",
+ "banner_review_high_qty_title": "Unusually high quantity",
+ "banner_review_high_qty_detail": "You have {qty} in stock — the figure seems very high. Confirm if correct or edit.",
+ "banner_prediction_rate_day": "Average ~{n} {unit}/day",
+ "banner_prediction_rate_week": "Average ~{n} {unit}/week",
+ "banner_prediction_days_ago": "{n} days ago you restocked",
+ "banner_prediction_more": "previous estimate: {expected} {unit}{time}; current quantity: {actual} {unit}.",
+ "banner_prediction_less": "estimate: {expected} {unit}{time}; current quantity: {actual} {unit}. If your usage pace changed, the forecast updates automatically.",
+ "banner_finished_zero": "Inventory shows zero, but recorded movements suggest it shouldn't be empty.",
+ "banner_finished_expected": "According to records you should still have {qty} {unit}.",
+ "banner_finished_check": "Can you check?",
+ "banner_anomaly_phantom_title": "you have more stock than expected",
+ "banner_anomaly_phantom_detail": "Inventory shows {inv_qty} {unit}, but based on records you should only have {expected_qty} {unit}. Did you add stock without recording it?",
+ "banner_anomaly_untracked_title": "stock not recorded as an entry",
+ "banner_anomaly_untracked_detail": "You have {inv_qty} {unit} in inventory, but recorded outflows exceed inflows — the initial stock was likely never added as an \"in\" transaction. You can correct the quantity or log the missing entries.",
+ "banner_anomaly_ghost_title": "you have less stock than expected",
+ "banner_anomaly_ghost_detail": "Based on recorded operations you should have {expected_qty} {unit} of {name}, but inventory shows only {inv_qty} {unit}. Did you take stock without recording it?",
+ "consumed": "Consumed: {n} ({pct}%)",
+ "wasted": "Wasted: {n} ({pct}%)",
+ "more_opened": "and {n} more opened...",
+ "banner_expired_detail": "{when} · you still have {qty}.",
+ "banner_opened_detail": "{when} in {location} · you still have {qty}.",
+ "banner_explain_title": "Ask Gemini for an explanation",
+ "banner_explain_btn": "Explain",
+ "banner_analyzing": "🤖 Analyzing…"
+ },
+ "inventory": {
+ "title": "Pantry",
+ "filter_all": "All",
+ "search_placeholder": "🔍 Search product...",
+ "recent_title": "🕐 Recently used",
+ "popular_title": "⭐ Most used",
+ "empty": "No products here.\nScan a product to add it!",
+ "no_items_found": "No inventory items found",
+ "qty_remainder_suffix": "left",
+ "vacuum_badge": "🫙 Vacuum sealed",
+ "opened_badge": "📭 Opened",
+ "label_expiry": "📅 Expiry",
+ "label_storage": "🫙 Storage",
+ "label_status": "📭 Status",
+ "opened_since": "Opened since {date}",
+ "label_position": "📍 Location",
+ "label_quantity": "📦 Quantity",
+ "label_added": "📅 Added",
+ "empty_text": "No products here. Scan a product to add it!",
+ "empty_db": "No products in the database. Scan a product to get started!",
+ "qty_trace": "< 1"
+ },
+ "scan": {
+ "title": "Scan",
+ "mode_shopping": "🛒 Shopping Mode",
+ "mode_shopping_end": "✅ End shopping",
+ "spesa_btn": "🛒 Shopping",
+ "zoom": "Zoom",
+ "tab_barcode": "Barcode",
+ "tab_name": "Name",
+ "tab_ai": "AI",
+ "recents_label": "Recent",
+ "torch_hint": "Torch",
+ "torch_on": "Torch on",
+ "torch_off": "Torch off",
+ "torch_unavailable": "Torch not available on this device",
+ "flip_hint": "Flip camera",
+ "flip_front": "Front camera",
+ "flip_back": "Rear camera",
+ "num_ocr_btn": "🔢 Read numbers with AI",
+ "num_ocr_searching": "Looking for barcode with AI...",
+ "num_ocr_found": "Code found: {code}",
+ "num_ocr_not_found": "No barcode found in image",
+ "barcode_placeholder": "Enter barcode...",
+ "quick_name_divider": "or type the name",
+ "quick_name_placeholder": "E.g.: Apples, Zucchini, Bread...",
+ "manual_entry": "✏️ Manual Entry",
+ "ai_identify": "🤖 Identify with AI",
+ "hint": "Scan the barcode, type the product name, or use AI to identify it",
+ "debug_toggle": "🐛 Debug Log",
+ "barcode_acquired": "🔖 Barcode scanned: {code}",
+ "scan_barcode": "🔖 Scan Barcode",
+ "create_named": "Create {name}",
+ "new_without_barcode": "New product without barcode",
+ "stock_in_pantry": "Already in pantry:",
+ "status_ready": "Point camera at barcode",
+ "status_scanning": "Scanning...",
+ "status_partial": "Detected: {code} — verifying...",
+ "status_invalid": "Invalid: {code} — retrying",
+ "status_confirmed": "Confirmed!",
+ "status_parallel": "Using combined scan methods...",
+ "ai_fallback_searching": "AI identifying product...",
+ "ai_fallback_found": "Product identified by AI",
+ "ai_fallback_not_found": "AI: product not recognized"
+ },
+ "action": {
+ "title": "What do you want to do?",
+ "add_btn": "📥 ADD",
+ "add_sub": "to pantry/fridge",
+ "use_btn": "USE",
+ "use_sub": "from pantry/fridge",
+ "have_title": "📦 Already in stock!",
+ "add_more_sub": "add more",
+ "use_qty_sub": "how much you used",
+ "throw_btn": "🗑️ DISCARD",
+ "throw_sub": "throw away",
+ "edit_sub": "expiry, location…",
+ "create_recipe_btn": "Recipe",
+ "related_stock_title": "Also at home"
+ },
+ "add": {
+ "title": "Add to Pantry",
+ "location_label": "📍 Where do you put it?",
+ "quantity_label": "📦 Quantity",
+ "conf_size_label": "📦 Each package contains:",
+ "conf_size_placeholder": "e.g. 300",
+ "vacuum_label": "🫙 Vacuum sealed",
+ "vacuum_hint": "Expiry date will be extended automatically",
+ "submit": "✅ Add",
+ "purchase_type_label": "🛒 This product is...",
+ "new_btn": "🆕 Just bought",
+ "existing_btn": "📦 I already had it",
+ "remaining_label": "📦 Remaining quantity",
+ "remaining_hint": "Approximately how much is left?",
+ "remaining_full": "🟢 Full",
+ "remaining_half": "🟠 Half",
+ "estimated_expiry": "Estimated expiry:",
+ "suffix_freezer": "(freezer)",
+ "suffix_vacuum": "(vacuum sealed)",
+ "hint_modify": "📝 You can change the date or scan it with the camera",
+ "scan_expiry_title": "📷 Scan Expiry Date",
+ "product_added": "✅ {name} added!{qty}",
+ "suffix_freezer_vacuum": "(freezer + vacuum sealed)",
+ "history_badge_tip": "Average from {n} previous entries",
+ "vacuum_question": "Vacuum sealed?",
+ "vacuum_saved": "🔒 Vacuum sealed!"
+ },
+ "use": {
+ "title": "Use / Consume",
+ "location_label": "📍 From where?",
+ "quantity_label": "How much did you use?",
+ "change": "change",
+ "partial_hint": "Or specify the quantity used:",
+ "partial_piece_hint": "Did you use only a part?",
+ "piece": "piece",
+ "one_whole": "1 whole",
+ "use_all": "🗑️ Used ALL / Finished",
+ "submit": "📤 Use this quantity",
+ "available": "📦 Available:",
+ "opened_badge": "OPENED",
+ "not_in_inventory": "⚠️ Product not in inventory.",
+ "expiry_warning": "⚠️ Use first the one{loc} that expires on {date} — {when}!",
+ "expiry_warning_opened": "⚠️ The one{loc} has been open for {when} — use it first!",
+ "throw_title": "🗑️ Discard Product",
+ "throw_all": "🗑️ Discard ALL ({qty})",
+ "throw_qty_label": "How much to discard?",
+ "throw_qty_hint": "or enter a quantity:",
+ "throw_partial_btn": "🗑️ Discard this quantity",
+ "when_expired": "expired {n} days ago",
+ "when_today": "expires today",
+ "when_tomorrow": "expires tomorrow",
+ "when_days": "expires in {n} days",
+ "toast_used": "📤 Used {qty} of {name}",
+ "toast_bring": "🛒 Product finished → added to Bring!",
+ "toast_opened_finished": "🔓 Opened package of {name} finished!",
+ "disambiguation_hint": "What do you mean by \"all done\"?",
+ "disambiguation_all": "🗑️ Finish EVERYTHING ({qty})",
+ "error_exceeds_stock": "⚠️ You cannot use more than you have available!",
+ "use_all_confirm_title": "✅ Finish everything",
+ "use_all_confirm_msg": "Confirm that you have finished the product:",
+ "use_all_confirm_btn": "✅ Yes, finished",
+ "throw_all_confirm_title": "🗑️ Discard everything",
+ "throw_all_confirm_msg": "Do you really want to throw away the whole product?",
+ "throw_all_confirm_btn": "🗑️ Yes, discard"
+ },
+ "product": {
+ "title_new": "New Product",
+ "title_edit": "Edit Product",
+ "ai_fill": "📷 Take photo and identify with AI",
+ "ai_fill_hint": "AI will automatically fill in the product fields",
+ "name_label": "🏷️ Product Name *",
+ "name_placeholder": "E.g.: Whole milk, Penne pasta...",
+ "brand_label": "🏢 Brand",
+ "brand_placeholder": "E.g.: Barilla, Granarolo, Mutti...",
+ "category_label": "📂 Category",
+ "unit_label": "📏 Unit of measure",
+ "default_qty_label": "🔢 Default quantity",
+ "conf_size_label": "📦 Each package contains:",
+ "conf_size_placeholder": "e.g. 300",
+ "notes_label": "📝 Notes",
+ "notes_placeholder": "E.g.: lactose free, organic, store in fridge after opening...",
+ "barcode_label": "🔖 Barcode",
+ "barcode_placeholder": "Barcode (if available)",
+ "barcode_hint": "⚠️ Add the barcode so next time you just need to scan it!",
+ "submit": "💾 Save Product",
+ "name_required": "Enter the product name",
+ "conf_size_required": "Specify the package content",
+ "expiry_estimated": "Estimated expiry:",
+ "scan_expiry": "Scan expiry date",
+ "expiry_hint": "📝 You can edit the date or scan it with the camera",
+ "add_batch": "📦 + Batch with different expiry",
+ "package_info": "📦 Package: {info}",
+ "edit_catalog": "⚙️ Edit product info (name, brand, category…)",
+ "not_recognized": "⚠️ Product not recognized",
+ "edit_info": "✏️ Edit information",
+ "modify_details": "EDIT\nexpiry, location…",
+ "already_in_pantry": "📋 Already in pantry",
+ "no_barcode": "No barcode",
+ "unknown_product": "Unrecognized product",
+ "edit_name_brand": "Edit name/brand",
+ "weight_label": "Weight",
+ "origin_label": "Origin",
+ "labels_label": "Labels",
+ "select_variant": "Select the exact variant or use AI data:"
+ },
+ "products": {
+ "title": "📦 All Products",
+ "search_placeholder": "🔍 Search product...",
+ "empty": "No products in database.\nScan a product to get started!",
+ "no_category": "No products in this category"
+ },
+ "recipes": {
+ "title": "🍳 Recipes",
+ "generate": "✨ Generate new recipe",
+ "archive_empty": "No recipes saved. Generate your first recipe!",
+ "dialog_title": "🍳 Recipe",
+ "dialog_desc": "I will generate a healthy recipe using pantry ingredients, prioritizing expiring items.",
+ "meal_label": "🕐 Which meal?",
+ "persons_label": "👥 How many people?",
+ "meal_type_label": "🎯 Meal type",
+ "opt_fast": "⚡ Quick meal",
+ "opt_light": "🥗 Light appetite",
+ "opt_expiry": "⏰ Prioritize expiring items",
+ "opt_healthy": "💚 Extra healthy",
+ "opt_opened": "📦 Prioritize opened items",
+ "opt_zero_waste": "♻️ Zero waste",
+ "generate_btn": "✨ Generate Recipe",
+ "loading_msg": "Preparing your recipe...",
+ "start_cooking": "👨🍳 Cooking Mode",
+ "regenerate": "🔄 Generate another one",
+ "regen_choice_title": "What do you want to do with this recipe?",
+ "regen_replace": "🔄 Generate another (discard this one)",
+ "regen_save_new": "💾 Save to archive & generate a new one",
+ "close_btn": "✅ Close",
+ "ingredients_title": "🧾 Ingredients",
+ "tools_title": "Equipment needed",
+ "steps_title": "👨🍳 Steps",
+ "no_steps": "No steps available",
+ "generate_error": "Generation error",
+ "stream_interrupted": "Generation interrupted (incomplete server response). Check logs or try again.",
+ "persons_short": "serv.",
+ "use_ingredient_title": "Use ingredient",
+ "recipe_qty_label": "Recipe",
+ "from_where_label": "From where?",
+ "amount_label": "How much",
+ "use_amount_btn": "Use this amount",
+ "use_all_btn": "Use ALL / Finished",
+ "packs_label": "Packs",
+ "quantity_in_total": "Quantity in {unit} (total: {total})",
+ "packs_of_have": "Packs of {size} (you have {count} packs)",
+ "scale_wait_stable": "Wait 10s of stable weight for auto-fill…",
+ "ingredient_scaled_toast": "📦 Ingredient deducted from pantry!",
+ "finished_added_bring_toast": "🛒 Finished product → added to Bring!",
+ "load_error": "Loading error",
+ "favorite": "Add to favourites",
+ "unfavorite": "Remove from favourites",
+ "adjust_persons": "Persons"
+ },
+ "shopping": {
+ "title": "🛒 Shopping List",
+ "bring_loading": "Connecting to Bring!...",
+ "bring_not_configured": "Bring! is not configured. Add your email and password in settings.",
+ "tab_to_buy": "🛍️ To buy",
+ "tab_forecast": "🧠 Forecast",
+ "total_label": "💰 Estimated total",
+ "section_to_buy": "🛍️ To buy",
+ "suggestions_title": "💡 AI Suggestions",
+ "suggestions_add": "✅ Add selected to Bring!",
+ "search_prices": "🔍 Search all prices",
+ "suggest_btn": "Suggest what to buy",
+ "smart_title": "🧠 Smart Predictions",
+ "smart_empty": "No predictions available. Add products to your pantry to receive smart predictions.",
+ "smart_filter_all": "All",
+ "smart_filter_critical": "🔴 Urgent",
+ "smart_filter_high": "🟠 Soon",
+ "smart_filter_medium": "🟡 Plan",
+ "smart_filter_low": "🟢 Forecast",
+ "smart_add": "🛒 Add selected to Bring!",
+ "empty": "Shopping list empty!\nUse the button below to generate suggestions.",
+ "already_in_list": "🛒 \"{name}\" is already in the shopping list",
+ "already_in_list_short": "ℹ️ Already in the shopping list",
+ "add_prompt": "Do you want to add it to the shopping list?",
+ "smart_already": "📊 Smart shopping already predicts {name}",
+ "all_searched": "All products have already been searched. Use 🔄 to search individual ones.",
+ "search_complete": "Search complete: {count} products",
+ "removed_sufficient": "🧹 {removed} product(s) with sufficient stock removed from the list",
+ "suggest_buy": "🛒 Buy: {qty} {unit}",
+ "suggest_buy_approx": "🛒 At least: {qty} {unit}",
+ "suggest_buy_tip": "Suggested quantity based on your last 14 days of consumption",
+ "suggest_buy_approx_tip": "Minimum estimate based on consumption (buy the nearest package size)",
+ "bring_badge": "🛒 Already on Bring!",
+ "add_urgent_toast": "🔴 {n} urgent product(s) automatically added to Bring!",
+ "migration_done": "✅ {migrated} updated, {skipped} already ok",
+ "added_to_bring": "🛒 {n} products added to Bring!",
+ "added_to_bring_skip": "{n} already present",
+ "all_on_bring": "All products were already on Bring!",
+ "freq_high": "📈 Frequent",
+ "freq_regular": "📊 Regular",
+ "freq_occasional": "📉 Occasional",
+ "out_of_stock": "Out of stock",
+ "scan_toast": "📷 Scan: {name}",
+ "empty_category": "No products in this category",
+ "session_empty": "🛒 No products yet",
+ "urgency_critical": "Urgent",
+ "urgency_high": "Soon",
+ "urgency_medium": "Plan",
+ "urgency_low": "Forecast",
+ "urgency_medium_short": "Medium",
+ "urgency_low_short": "Ok",
+ "tag_urgent": "🔴 Urgent",
+ "tag_priority": "⭐ Priority",
+ "tag_check": "✅ Check",
+ "smart_already_predicted": "📊 Smart shopping already predicts {name}{urgency}.",
+ "item_removed": "✅ {name} removed from list!",
+ "urgency_spec_critical": "⚡ Urgent",
+ "urgency_spec_high": "🟠 Soon",
+ "bring_add_n": "Add {n} to Bring!",
+ "bring_add_selected": "Add selected to Bring!",
+ "bring_adding": "Adding...",
+ "bring_added_one": "1 product added to Bring!",
+ "bring_added_many": "{n} products added to Bring!",
+ "bring_skipped": "({n} already in list)",
+ "force_sync": "Force Bring! sync",
+ "scan_target_label": "You are looking for",
+ "scan_target_found": "Found! Remove from list",
+ "bring_add_one": "Add 1 product to Bring!",
+ "bring_add_many": "Add {n} products to Bring!",
+ "syncing": "Syncing…",
+ "sync_done": "Sync completed",
+ "price_searching": "Searching...",
+ "search_action": "Search",
+ "open_action": "Open",
+ "not_found": "Not found",
+ "search_price": "Search price",
+ "tap_to_scan": "Tap to scan",
+ "tag_title": "Tag",
+ "remove_title": "Remove",
+ "found_count": "{found}/{total} products found",
+ "savings_offers": "· 🏷️ You save €{amount} with offers",
+ "searching_progress": "Searching {current}/{total}...",
+ "remove_error": "Removal error",
+ "btn_fetch_prices": "Find prices",
+ "price_total_label": "💰 Estimated total:",
+ "price_loading": "Looking up prices…",
+ "price_not_found": "price n/a",
+ "suggest_loading": "Analyzing...",
+ "suggest_error": "Suggestion generation error",
+ "priority_high": "High",
+ "priority_medium": "Medium",
+ "priority_low": "Low",
+ "smart_last_update": "Updated {time}",
+ "names_already_updated": "All names are already up to date",
+ "pantry_hint": "Already at home: {qty}"
+ },
+ "ai": {
+ "title": "🤖 AI Identification",
+ "capture": "📸 Take Photo",
+ "retake": "🔄 Retake",
+ "hint": "Take a photo of the product and AI will try to identify it",
+ "identifying": "🤖 Identifying product...",
+ "no_api_key": "⚠️ Gemini API key not configured.\nAdd GEMINI_API_KEY to the .env file on the server.",
+ "fields_filled": "✅ Fields filled by AI",
+ "use_data": "✅ Use AI data",
+ "use_data_no_barcode": "✅ Use AI data (no barcode)"
+ },
+ "log": {
+ "title": "📒 Operations Log",
+ "type_added": "Added",
+ "type_waste": "Discarded",
+ "type_used": "Used",
+ "type_bring": "Added to Bring!",
+ "undone_badge": "Undone",
+ "undo_title": "Undo this operation",
+ "load_error": "Error loading log",
+ "empty": "No operations recorded.",
+ "undo_action_remove": "removal of",
+ "undo_action_restore": "restock of",
+ "undo_confirm": "Undo this operation?\n→ {action} {name}",
+ "undo_success": "↩ Operation undone for {name}",
+ "already_undone": "Operation already undone",
+ "too_old": "Cannot undo operations older than 24 hours",
+ "undo_error": "Error during undo",
+ "recipe_prefix": "Recipe"
+ },
+ "chat": {
+ "title": "Gemini Chef",
+ "welcome": "Hi! I'm your kitchen assistant",
+ "welcome_desc": "Ask me to make you a juice, a snack, a quick dish... I know your pantry, your appliances and your preferences!",
+ "suggestion_snack": "🍿 Quick snack",
+ "suggestion_juice": "🥤 Juice/Smoothie",
+ "suggestion_light": "🥗 Something light",
+ "suggestion_expiry": "⏰ Use expiring items",
+ "clear": "New conversation",
+ "placeholder": "Ask something...",
+ "cleared": "Chat cleared",
+ "suggestion_snack_text": "What can I make for a quick snack?",
+ "suggestion_juice_text": "Make me a juice or smoothie with what I have",
+ "suggestion_light_text": "I'm hungry but want something light",
+ "suggestion_expiry_text": "What's about to expire and how can I use it?",
+ "transfer_to_recipes": "Transfer to Recipes",
+ "transferring": "Transferring...",
+ "transferred": "Added to Recipes!",
+ "open_recipe": "Open recipe",
+ "quick_recipe_prompt": "Suggest a quick recipe FOR ONE PERSON using the products that expire first! Ignore freezer items, focus on fridge and pantry."
+ },
+ "cooking": {
+ "close": "Close",
+ "tts_btn": "Read aloud",
+ "restart": "↺ Restart",
+ "replay": "🔊 Replay",
+ "timer": "⏱️ {time} · Timer",
+ "prev": "◀ Previous",
+ "next": "Next ▶",
+ "ingredient_used": "✔️ Deducted",
+ "ingredient_use_btn": "Use",
+ "ingredient_deduct_title": "Deduct from pantry",
+ "timer_expired_tts": "Timer {label} expired!",
+ "timer_warning_tts": "Heads up! {label}: 10 seconds left!",
+ "recipe_done_tts": "Recipe complete! Enjoy your meal!",
+ "expires_chip": "exp. {date}",
+ "finish": "✅ Finish",
+ "step_fallback": "Step {n}",
+ "zerowaste_label": "♻️ Scrap",
+ "zerowaste_tip_title": "Zero-waste tip"
+ },
+ "settings": {
+ "title": "⚙️ Settings",
+ "tab_api": "API Keys",
+ "tab_bring": "Bring!",
+ "tab_recipe": "Recipes",
+ "tab_mealplan": "Weekly Plan",
+ "tab_appliances": "Appliances",
+ "tab_spesa": "Online Shopping",
+ "tab_camera": "Camera",
+ "tab_security": "Security",
+ "tab_tts": "Voice (TTS)",
+ "tab_language": "Language",
+ "tab_scale": "Smart Scale",
"gemini": {
- "chat_title": "Chat with Gemini",
- "not_configured": "🤖 Gemini not configured — set GEMINI_API_KEY in settings"
+ "title": "🤖 Google Gemini AI",
+ "hint": "API key for product identification, expiry dates and recipes.",
+ "key_label": "Gemini API Key"
+ },
+ "bring": {
+ "title": "🛒 Bring! Shopping List",
+ "hint": "Credentials for the Bring! shopping list integration.",
+ "email_label": "📧 Bring! Email",
+ "password_label": "🔒 Bring! Password"
+ },
+ "price": {
+ "title": "💰 Price Estimation (AI)",
+ "hint": "Show estimated cost per product in the shopping list using AI.",
+ "enabled_label": "Enable price estimation",
+ "country_label": "🌍 Reference country",
+ "currency_label": "💱 Currency",
+ "update_label": "🔄 Refresh prices every",
+ "update_suffix": "months"
+ },
+ "recipe": {
+ "title": "🍳 Recipe Preferences",
+ "hint": "Configure the default options for recipe generation.",
+ "persons_label": "👥 Default servings",
+ "options_label": "🎯 Default recipe options",
+ "fast": "⚡ Quick Meal",
+ "light": "🥗 Light Meal",
+ "expiry": "⏰ Expiry Priority",
+ "healthy": "💚 Extra Healthy",
+ "opened": "📦 Open Items Priority",
+ "zerowaste": "♻️ Zero Waste",
+ "dietary_label": "🚫 Intolerances / Restrictions",
+ "dietary_placeholder": "E.g.: gluten free, lactose free, vegetarian..."
+ },
+ "mealplan": {
+ "title": "📅 Weekly Meal Plan",
+ "hint": "Set the meal type for each day. It will be used as a guide in recipe generation.",
+ "enabled": "✅ Enable weekly meal plan",
+ "legend": "🌤️ = Lunch · 🌙 = Dinner · Tap a badge to change it.",
+ "types_title": "📋 Available types",
+ "reset_btn": "↺ Restore defaults"
},
"appliances": {
- "empty": "No appliances added"
+ "title": "🔌 Available Appliances",
+ "hint": "Indicate the appliances you have. They will be considered in recipe generation.",
+ "new_placeholder": "E.g.: Bread machine, Thermomix, Air fryer...",
+ "quick_title": "Quick add:",
+ "oven": "🔥 Oven",
+ "microwave": "📡 Microwave",
+ "air_fryer": "🍟 Air fryer",
+ "bread_maker": "🍞 Bread maker",
+ "bimby": "🤖 Thermomix/Cookeo",
+ "mixer": "🌀 Stand mixer",
+ "steamer": "♨️ Steamer",
+ "pressure_cooker": "🫕 Pressure cooker",
+ "toaster": "🍞 Toaster",
+ "blender": "🍹 Blender",
+ "empty": "No appliances added"
},
- "about": {
- "title": "About",
- "version": "Version",
- "report_bug": "Report a Bug",
- "report_bug_hint": "Something not working? Send us a report directly from the app.",
- "report_bug_modal_title": "Report a Bug",
- "report_type_bug": "Bug",
- "report_type_feature": "Feature",
- "report_type_question": "Question",
- "report_field_title": "Title",
- "report_field_title_ph": "Brief description of the issue",
- "report_field_desc": "Description",
- "report_field_desc_ph": "Describe the issue in detail…",
- "report_field_steps": "Steps to reproduce (optional)",
- "report_field_steps_ph": "1. Go to…\n2. Tap…\n3. See the error…",
- "report_auto_info": "Automatically attached: version {version}, language {lang}.",
- "report_send_btn": "Send report",
- "report_bug_sending": "Sending…",
- "report_bug_sent": "Report sent — thank you!",
- "report_bug_error": "Could not send the report. Check your connection.",
- "changelog": "Changelog",
- "github": "GitHub Repository"
+ "spesa": {
+ "title": "🛍️ Online Shopping",
+ "hint": "Configure the online shopping provider.",
+ "provider_label": "🏪 Provider",
+ "email_label": "📧 Email",
+ "password_label": "🔒 Password",
+ "login_btn": "🔐 Login",
+ "ai_prompt_label": "🤖 AI product selection prompt",
+ "ai_prompt_placeholder": "Instructions for AI when choosing between multiple products...",
+ "ai_prompt_hint": "AI uses this prompt to choose the most appropriate product from results. Leave empty for default behavior.",
+ "configure_first": "Configure Online Shopping in settings first",
+ "missing_credentials": "Enter email and password",
+ "login_in_progress": "Signing in...",
+ "login_error_prefix": "Error:",
+ "login_network_error_prefix": "Network error:",
+ "login_success_default": "Login successful!",
+ "result_name_label": "Name",
+ "result_card_label": "Card",
+ "result_pickup_label": "Pickup point",
+ "result_points_label": "Loyalty points",
+ "connected_relogin": "✅ Connected — Sign in again",
+ "connected_as": "Connected as {name}"
},
- "export": {
- "title": "Export inventory",
- "hint": "Download the current inventory as CSV or open a print-ready version (PDF).",
- "btn_csv": "Download CSV",
- "btn_pdf": "PDF / Print",
- "btn_title": "Export"
+ "camera": {
+ "title": "📷 Camera",
+ "hint": "Choose which camera to use for barcode scanning and AI identification.",
+ "device_label": "📸 Default camera",
+ "back": "📱 Rear (default)",
+ "front": "🤳 Front",
+ "devices_hint": "If you have multiple cameras, you can select a specific one from the list above after granting permissions.",
+ "detect_btn": "🔄 Detect cameras",
+ "ai_fallback_label": "AI visual identification (5s fallback)",
+ "ai_fallback_hint": "If no barcode is read within 5 seconds, a frame is automatically sent to AI to visually identify the product. Requires Gemini configured."
},
- "startup": {
- "connecting": "Connecting to server...",
- "check_php_memory": "PHP memory",
- "check_php_timeout": "PHP timeout",
- "check_php_upload": "PHP upload",
- "check_data_dir": "Data directory",
- "check_rate_limits": "Rate limits dir",
- "check_backups": "Backup dir",
- "check_write_test": "Disk write test",
- "check_disk_space": "Disk space",
- "check_db_legacy": "Legacy DB (dispensa.db)",
- "check_db_connect": "Database connection",
- "check_db_tables": "Database tables",
- "check_db_integrity": "Database integrity",
- "check_db_wal": "WAL mode",
- "check_db_size": "Database size",
- "check_db_rows": "Inventory data",
- "check_env": ".env file",
- "check_gemini": "Gemini AI key",
- "check_bring_creds": "Bring! credentials",
- "check_bring_token": "Bring! token",
- "check_tts": "Text-to-Speech URL",
- "check_scale": "Scale gateway",
- "check_curl_ssl": "cURL SSL",
- "check_internet": "Internet connection",
- "fresh_install": "fresh install",
- "warnings_found": "warnings found",
- "all_ok": "System OK",
- "critical_error_short": "Critical error",
- "critical_error": "Critical error: the app cannot start. Check your server logs.",
- "critical_error_intro": "The app cannot start due to the following issues:",
- "error_network": "Cannot reach the server.",
- "error_network_detail": "The browser cannot reach the PHP server.\n\nPossible causes:\n• Apache/PHP server is not running\n• Network or firewall issue\n• Incorrect app URL\n\nMake sure the server is started and try again.",
- "retry": "Retry",
- "syncing_local": "Syncing local data...",
- "sync_done": "Local data synced"
+ "security": {
+ "title": "🔒 HTTPS Certificate",
+ "hint": "If the browser shows the error \"Your connection is not private\" (ERR_CERT_AUTHORITY_INVALID), you need to install the CA certificate on the device.",
+ "download_btn": "📥 Download CA Certificate",
+ "token_title": "🔑 Settings Token",
+ "token_label": "Access token",
+ "token_hint": "If `SETTINGS_TOKEN` is configured in the server's `.env`, enter the token here before saving settings. Leave empty if not configured.",
+ "token_placeholder": "(empty = no protection)",
+ "token_required_hint": "🔒 This server requires a token to save settings.",
+ "cert_instructions": "Instructions for Chrome (Android): 1. Download the certificate above 2. Go to Settings → Security & Privacy → More security settings → Install from device storage 3. Select the downloaded EverShelf_CA.crt file 4. Choose \"CA\" and confirm 5. Restart Chrome
Instructions for Chrome (PC): 1. Download the certificate above 2. Go to chrome://settings/certificates (or Settings → Privacy and security → Security → Manage certificates) 3. Tab \"Authorities\" → Import → select the file 4. Check \"Trust this certificate for identifying websites\" 5. Restart Chrome"
},
- "stats_monthly": {
- "title": "Monthly Stats",
- "consumed": "products used",
- "trend_up": "+{pct}% vs {prev}",
- "trend_down": "-{pct}% vs {prev}",
- "trend_same": "same pace as last month",
- "added": "added",
- "wasted": "wasted",
- "top_used": "top used",
- "top_cats": "Top categories",
- "source": "Transaction history · current month"
+ "tts": {
+ "title": "🔊 Voice & TTS",
+ "hint": "Configure text-to-speech via any external REST API. Recipe steps and expired timers will be sent to the configured endpoint.",
+ "enabled": "✅ Enable TTS",
+ "engine_label": "⚙️ TTS Engine",
+ "engine_browser": "🔇 Browser (offline, no configuration required)",
+ "engine_server": "🌐 External server (Home Assistant, REST API...)",
+ "voice_label": "🗣️ Voice",
+ "rate_label": "⚡ Speed",
+ "pitch_label": "🎵 Pitch",
+ "url_label": "🌐 Endpoint URL",
+ "method_label": "📡 HTTP Method",
+ "auth_label": "🔐 Authentication",
+ "auth_bearer": "Bearer Token",
+ "auth_custom": "Custom Header",
+ "auth_none": "None",
+ "token_label": "🔑 Bearer Token",
+ "custom_header_name": "📋 Header name",
+ "custom_header_value": "📋 Header value",
+ "content_type_label": "📄 Content-Type",
+ "payload_key_label": "🗝️ Text field in payload",
+ "payload_key_hint": "Name of the JSON field that will contain the text to read (e.g.: message, text).",
+ "extra_fields_label": "➕ Extra fields (JSON)",
+ "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}",
+ "extra_fields_hint": "Additional fields to include in the payload, in JSON format. Leave empty if not needed.",
+ "test_sound_btn": "🔔 Run Sound Test",
+ "test_btn": "🔊 Send Test Voice",
+ "voices_loading": "Loading voices…",
+ "voice_not_supported": "Voice not supported by this browser",
+ "voices_none": "No voices available on this device",
+ "voices_hint": "Available voices depend on the OS and browser. On macOS/iOS the Paola (Italian) voice is available. Press ↺ if the list does not load.",
+ "url_missing": "⚠️ Endpoint URL missing.",
+ "test_sending": "⏳ Sending…",
+ "test_ok": "✅ Response {code} — check that the speaker has spoken.",
+ "heard_question": "Did you hear the voice?",
+ "heard_yes": "Yes, I heard it",
+ "heard_no": "No, I didn't hear it",
+ "test_ok_kiosk": "TTS is working.",
+ "test_fail_steps": "Check: 1) media volume is not 0; 2) Google Text-to-Speech is installed and updated; 3) Italian voice package is downloaded in Android TTS settings."
+ },
+ "language": {
+ "title": "🌐 Language",
+ "hint": "Select the interface language.",
+ "label": "🌐 Language",
+ "restart_notice": "The page will reload to apply the new language."
+ },
+ "screensaver": {
+ "label": "Enable screensaver",
+ "card_title": "🌙 Screensaver",
+ "card_hint": "Shows a clock with useful facts after 5 minutes of inactivity. Disabled by default.",
+ "timeout_1": "1 minute",
+ "timeout_2": "2 minutes",
+ "timeout_5": "5 minutes",
+ "timeout_10": "10 minutes",
+ "timeout_15": "15 minutes",
+ "timeout_30": "30 minutes",
+ "timeout_60": "1 hour",
+ "start_after": "⏱️ Start after"
+ },
+ "scale": {
+ "title": "⚖️ Smart Scale",
+ "hint": "Connect a Bluetooth scale via the Android gateway to automatically read weight.",
+ "tab": "Smart Scale",
+ "enabled": "✅ Enable smart scale",
+ "url_label": "🌐 WebSocket Gateway URL",
+ "url_placeholder": "ws://192.168.1.x:8765",
+ "url_hint": "URL shown by the Android app (same Wi-Fi network). E.g.:",
+ "test_btn": "🔗 Test connection",
+ "download_btn": "📥 Download Android Gateway (APK)",
+ "download_hint": "Android app that bridges your BLE scale and EverShelf.",
+ "download_sub": "Source: evershelf-scale-gateway/ in the project root",
+ "live_weight": "real-time weight",
+ "auto_reconnect": "🔁 Reconnect: automatic",
+ "kiosk_title": "📡 BLE Scale integrated in Kiosk",
+ "kiosk_hint": "The scale is directly managed by the internal BLE Gateway of the kiosk. To pair a new device, use the configuration wizard.",
+ "kiosk_reconfigure": "🔄 Reconfigure BLE Scale",
+ "ble_protocols": "
🔌 Supported BLE protocols:
Bluetooth SIG Weight Scale (0x181D)
Bluetooth SIG Body Composition (0x181B) — weight, fat, BMI
Xiaomi Mi Body Composition Scale 2
Generic — automatic heuristic for 100+ models
"
+ },
+ "kiosk": {
+ "hint": "Turn an Android tablet into an always-on EverShelf panel with built-in BLE scale gateway.",
+ "download_btn": "📥 Download EverShelf Kiosk (APK)",
+ "download_sub": "Full-screen kiosk mode + integrated scale gateway. Source: evershelf-kiosk/",
+ "native_title": "Kiosk Configuration",
+ "native_hint": "Server URL, BLE scale, screensaver and setup wizard.",
+ "native_btn": "Open kiosk configuration",
+ "native_tap_hint": "Tap the gear button at the top right",
+ "native_update_hint": "Update the kiosk app to use this feature",
+ "update_title": "Kiosk Update",
+ "check_updates_btn": "🔍 Check for updates",
+ "needs_update": "⚠️ The installed kiosk does not support this feature. Update the kiosk app to enable it."
+ },
+ "saved": "✅ Configuration saved!",
+ "saved_local": "✅ Configuration saved locally",
+ "saved_local_error": "⚠️ Saved locally, server error: {error}",
+ "theme": {
+ "title": "🌙 Appearance",
+ "hint": "Choose the interface theme.",
+ "label": "🌙 Theme",
+ "off": "☀️ Light",
+ "on": "🌙 Dark",
+ "auto": "🔄 Automatic (time of day)"
+ },
+ "zerowaste": {
+ "card_title": "♻️ Zero-waste tips",
+ "card_hint": "During cooking, show tips on how to reuse scraps generated in each step (peels, cooking water, etc.). Disabled by default.",
+ "label": "Show tips during cooking"
+ },
+ "backup": {
+ "tab": "Backup",
+ "local_title": "Local Backup",
+ "local_hint": "Daily database snapshot. Configure how many days of backups to keep.",
+ "enabled": "Enable daily automatic backup",
+ "retention_days": "Retention (days)",
+ "retention_info": "Backups are kept for",
+ "backup_now": "Backup Now",
+ "backing_up": "Backing up…",
+ "backed_up": "Backup complete",
+ "backup_error": "Backup error",
+ "last_backup": "Last backup",
+ "no_backup_yet": "No backup has been created yet",
+ "list_empty": "No backups available",
+ "restore_btn": "Restore",
+ "restore_confirm": "Restore backup",
+ "delete_btn": "Delete",
+ "delete_confirm": "Delete backup",
+ "gdrive_title": "Google Drive",
+ "gdrive_hint": "Automatically back up to Google Drive via OAuth 2.0. No external libraries required.",
+ "gdrive_enabled": "Enable Google Drive backup",
+ "gdrive_folder_id": "Drive Folder ID",
+ "gdrive_folder_id_hint": "Copy the ID from the Drive folder URL: …/folders/ID",
+ "gdrive_retention_days": "Drive retention (days, 0=keep all)",
+ "gdrive_test": "Test Connection",
+ "gdrive_ok": "Connection successful!",
+ "gdrive_error": "Connection failed",
+ "gdrive_push_now": "Upload to Drive Now",
+ "gdrive_pushing": "Uploading…",
+ "gdrive_pushed": "Uploaded to Drive",
+ "gdrive_wizard_hint": "Optional: automatically back up to Google Drive daily via OAuth 2.0.",
+ "gdrive_skip": "Skip — configure later in Settings",
+ "gdrive_client_id": "Client ID",
+ "gdrive_client_secret": "Client Secret",
+ "gdrive_redirect_uri_hint": "Add http://localhost as an authorized redirect URI in Google Cloud Console. This works on any server, even without a public domain.",
+ "gdrive_code_title": "Paste the authorization URL or code",
+ "gdrive_code_hint": "After authorizing, the browser will open http://localhost and may show a connection error — that is expected. Copy the URL from the address bar (e.g. http://localhost/?code=4%2F0A...) and paste it here.",
+ "gdrive_code_submit": "Submit",
+ "gdrive_code_empty": "Paste the URL or authorization code first",
+ "gdrive_redirect_uri_label": "Redirect URI (add this in Google Cloud Console):",
+ "gdrive_oauth_authorize": "Authorize with Google",
+ "gdrive_oauth_authorized": "Authorized",
+ "gdrive_oauth_not_authorized": "Not authorized yet",
+ "gdrive_oauth_window_opened": "Browser window opened — authorize and come back",
+ "gdrive_oauth_how_to": "How to set up OAuth 2.0 (step by step)",
+ "gdrive_oauth_steps": "
Enable the Google Drive API: APIs & Services → Enable APIs → Google Drive API
Go to APIs & Services → Credentials → Create Credentials → OAuth client ID
Application type: Web application; add http://localhost as an Authorized redirect URI
Copy the Client ID and Client Secret into the fields above and save
Click Authorize with Google, sign in and grant access
The browser will open http://localhost (a connection error is expected): copy the URL from the address bar and paste it in the field that appears below
"
+ },
+ "info": {
+ "tab": "Info",
+ "ai_title": "Gemini AI — Token Usage",
+ "ai_hint": "Monthly consumption and estimated cost for the current API key.",
+ "loading": "Loading…",
+ "total_tokens": "Total tokens",
+ "est_cost": "Est. cost",
+ "input_tok": "Input tokens",
+ "output_tok": "Output tokens",
+ "ai_calls": "Calls",
+ "by_action": "Breakdown by function",
+ "by_model": "Breakdown by model",
+ "pricing_note": "Gemini reference pricing: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.",
+ "system_title": "System",
+ "db_size": "Database",
+ "log_size": "Logs",
+ "log_level": "Log level",
+ "ai_overview": "AI usage overview, inventory and system status",
+ "calls_unit": "calls",
+ "inv_title": "Inventory",
+ "inv_active": "Active",
+ "inv_products": "Total products",
+ "inv_expiring": "Expiring (7d)",
+ "inv_expired": "Expired",
+ "inv_finished": "Finished",
+ "act_title": "Monthly activity",
+ "act_tx_month": "Movements",
+ "act_restock": "Restocks",
+ "act_use": "Usages",
+ "act_new_products": "New products",
+ "act_tx_year": "Yearly movements",
+ "price_cache": "Price cache",
+ "cache_entries": "products",
+ "last_backup": "Last backup",
+ "bring_days": "token expires in {n} days",
+ "bring_expired": "token expired",
+ "year_label": "Year {year}",
+ "currency_title": "Currency",
+ "currency_hint": "The currency used for all costs and prices in the app."
+ },
+ "tab_general": "General",
+ "shopping": {
+ "tab": "Shopping list",
+ "title": "Shopping list",
+ "hint": "Configure the built-in shopping list or connect Bring!.",
+ "enable_label": "Enable shopping list",
+ "mode_label": "Provider",
+ "mode_internal": "Built-in (no Bring!)",
+ "mode_bring": "Bring! (external app)",
+ "bring_section_title": "Bring! configuration",
+ "ai_section_title": "AI assistance",
+ "smart_suggestions_label": "AI suggestions",
+ "forecast_label": "Forecast low-stock products",
+ "auto_add_label": "Auto-add to list when",
+ "auto_add_suffix": "remaining in stock (0 = only when empty)"
+ },
+ "ha": {
+ "tab": "Home Assistant",
+ "title": "Home Assistant",
+ "hint": "Connect EverShelf to Home Assistant for automations, push notifications and REST sensors.",
+ "enabled": "Enable Home Assistant integration",
+ "connection_title": "Connection",
+ "url_label": "Home Assistant URL",
+ "url_placeholder": "http://192.168.1.50:8123",
+ "url_hint": "Base URL of your Home Assistant instance (e.g. http://homeassistant.local:8123).",
+ "token_label": "Long-Lived Access Token",
+ "token_hint": "Generate from HA Profile → Security → Long-Lived Access Tokens.",
+ "token_placeholder": "eyJhbGci...",
+ "token_saved": "Token saved (hidden for security)",
+ "test_btn": "Test connection",
+ "test_ok": "Connected to {version}",
+ "test_fail": "Connection failed: {error}",
+ "test_bad_token": "HA reachable but token is invalid",
+ "testing": "Testing…",
+ "error_no_url": "Please enter the Home Assistant URL first.",
+ "tts_title": "TTS on Smart Speaker",
+ "tts_hint": "Read recipe steps aloud on a Home Assistant media player.",
+ "tts_entity_label": "Media player entity ID",
+ "tts_entity_placeholder": "media_player.living_room",
+ "tts_entity_hint": "Entity ID of the HA media player. Find it in HA: Developer Tools → States.",
+ "tts_platform_label": "TTS platform",
+ "tts_platform_speak": "tts.speak (recommended)",
+ "tts_platform_notify": "notify.* (notification service)",
+ "tts_apply_btn": "Apply HA preset to TTS tab",
+ "tts_apply_hint": "Pre-fills the TTS tab with the Home Assistant URL and token.",
+ "tts_preset_applied": "HA preset applied to TTS tab.",
+ "webhook_title": "Webhook Automations",
+ "webhook_hint": "Send data to Home Assistant when pantry events occur. Create an HA automation with a Webhook trigger and paste the generated ID here.",
+ "webhook_id_label": "Webhook ID",
+ "webhook_id_placeholder": "evershelf_webhook_abc123",
+ "webhook_id_hint": "ID of the webhook created in HA. Copy from: HA → Settings → Automations → Create → Webhook Trigger.",
+ "webhook_events_label": "Notify on these events",
+ "event_expiry": "Expiring products (daily)",
+ "event_shopping": "Item added to shopping list",
+ "event_stock": "Stock level updated",
+ "expiry_days_label": "Expiry lead time (days)",
+ "expiry_days_hint": "Send the expiry alert N days before the expiry date.",
+ "webhook_help": "In HA: Settings → Automations → Create automation → Trigger: Webhook → copy the generated ID above.",
+ "notify_title": "Push Notifications",
+ "notify_hint": "Send push notifications to your phone via a Home Assistant notify service.",
+ "notify_service_label": "Notify service",
+ "notify_service_placeholder": "notify.mobile_app_my_phone",
+ "notify_service_hint": "HA notify service name (e.g. notify.mobile_app_phone). Leave empty to disable.",
+ "sensor_title": "REST Sensors",
+ "sensor_hint": "Add to configuration.yaml to create EverShelf sensors in Home Assistant.",
+ "sensor_copy_btn": "Copy YAML",
+ "sensor_copied": "YAML copied to clipboard!",
+ "save_btn": "Save HA settings",
+ "ha_hint": "If you use Home Assistant, use the Home Assistant tab to configure TTS, webhooks and sensors."
}
+ },
+ "expiry": {
+ "today": "TODAY",
+ "tomorrow": "Tomorrow",
+ "days": "{days} days",
+ "expired_days": "{days}d ago",
+ "expired_yesterday": "Yesterday",
+ "expired_today": "Today",
+ "badge_today": "⚠️ Expires today!",
+ "badge_tomorrow": "⏰ Tomorrow",
+ "badge_tomorrow_long": "⏰ Expires tomorrow",
+ "badge_days": "⏰ {n} days",
+ "badge_expired_ago": "⚠️ Expired {n}d ago",
+ "badge_expired": "⛔ Expired!",
+ "badge_stable": "✅ Stable",
+ "badge_expiring_short": "⏰ Exp. in {n}d",
+ "badge_ok_still": "✅ Still {n}d",
+ "badge_expires_red": "🔴 Exp. in {n}d",
+ "badge_expires_yellow": "🟡 Exp. in {n}d",
+ "badge_expired_bare": "⚠️ Expired",
+ "badge_expires_warn": "⚠️ Exp. in {n}d",
+ "badge_days_left": "⏳ ~{n}d left",
+ "days_approx": "~{n} days",
+ "weeks_approx": "~{n} weeks",
+ "months_approx": "~{n} months",
+ "years_approx": "~{n} years",
+ "expired_today_long": "Expired today",
+ "expired_ago_long": "Expired {n} days ago",
+ "expired_suffix": "— Expired!",
+ "expired_suffix_ok": "— Expired (still ok)",
+ "expired_suffix_warning": "— Expired (check first)",
+ "opened_ago_long": "Opened {n} days ago",
+ "opened_today_long": "Opened today",
+ "opened_suffix": "— Opened too long!",
+ "opened_suffix_ok": "— Opened (still ok)",
+ "opened_suffix_warning": "— Opened (check first)",
+ "days_compact": "{n}d",
+ "badge_check_soon": "Check soon"
+ },
+ "status": {
+ "ok": "OK",
+ "check": "Check",
+ "discard": "Discard",
+ "tip_freezer_ok": "In freezer: still safe (~{n}d margin)",
+ "tip_freezer_check": "In freezer for a long time, may have lost quality. Consume soon",
+ "tip_freezer_danger": "In freezer too long, risk of freezer burn and degradation",
+ "tip_highRisk_check": "Expired recently, check smell and appearance before consuming",
+ "tip_highRisk_danger": "Perishable product expired: discard for safety",
+ "tip_medRisk_check1": "Check appearance and smell before consuming",
+ "tip_medRisk_check2": "Expired a while ago, check carefully before use",
+ "tip_medRisk_danger": "Too long since expiry, better to discard",
+ "tip_lowRisk_ok": "Long-lasting product, still safe to consume",
+ "tip_lowRisk_check": "Expired over a month ago, check package integrity",
+ "tip_lowRisk_danger": "Expired too long ago, better not to risk it"
+ },
+ "toast": {
+ "product_saved": "Product saved!",
+ "product_created": "Product created!",
+ "product_updated": "✅ Product updated!",
+ "product_removed": "Product removed",
+ "updated": "Updated!",
+ "quantity_confirmed": "✓ Quantity confirmed",
+ "added_to_inventory": "✅ {name} added!",
+ "removed_from_list": "✅ {name} removed from the list!",
+ "removed_from_list_short": "Removed from the list",
+ "added_to_shopping": "🛒 Added to the shopping list!",
+ "removed_from_shopping": "🛒 Removed from the shopping list",
+ "finished_to_bring": "🛒 Product finished → added to Bring!",
+ "thrown_away": "🗑️ {name} thrown away!",
+ "thrown_away_partial": "🗑️ Thrown away {qty} {unit} of {name}",
+ "finished_all": "📤 {name} finished!",
+ "vacuum_sealed": "{name} saved as vacuum sealed",
+ "product_finished_confirmed": "✅ Removed — add it again when you restock",
+ "appliance_added": "Appliance added",
+ "item_added": "{name} added"
+ },
+ "antiwaste": {
+ "title": "🌱 Anti-Waste Report",
+ "grade_label": "Grade",
+ "you": "You",
+ "avg_label": "Avg",
+ "better": "🎉 You lose {diff}% less than the {country}!",
+ "worse": "⚠️ You lose more than the {country}. Room for improvement!",
+ "on_par": "→ You're at the {country}. You can do better!",
+ "saved_money": "~{amount}/month saved",
+ "saved_meals": "~{n} meals saved",
+ "saved_co2": "{n} kg CO₂ avoided",
+ "trend_title": "Trend (last 3 months)",
+ "months_ago_2": "-60 days",
+ "months_ago_1": "-30 days",
+ "this_month": "Now",
+ "country_it": "Italian avg",
+ "country_de": "German avg",
+ "country_en": "US average",
+ "source": "Sources: REDUCE, Eurostat, USDA 2021",
+ "live_on": "Live data",
+ "live_off": "Offline",
+ "meals": "meals",
+ "annual_info": "📅 You ~{you} kg/yr · avg ~{avg} kg/yr",
+ "badge_rate": "loss rate",
+ "badge_saved_money": "saved vs avg",
+ "badge_wasted": "items lost",
+ "badge_better": "less than avg"
+ },
+ "error": {
+ "generic": "Error",
+ "network": "Network error",
+ "no_api_key": "Configure the API key in settings",
+ "loading": "Error loading product",
+ "not_found": "Product not found",
+ "not_found_manual": "Product not found. Enter it manually.",
+ "search": "Search error. Try again.",
+ "search_short": "Search error",
+ "save": "Error saving",
+ "connection": "Connection error",
+ "camera": "Cannot access camera",
+ "bring_add": "Error adding to Bring!",
+ "bring_connection": "Bring! connection error",
+ "identification": "Identification error",
+ "ai_quota": "AI quota exhausted. Please try again in a few minutes.",
+ "barcode_empty": "Enter a barcode",
+ "barcode_format": "Barcode must contain only numbers (4-14 digits)",
+ "min_chars": "Type at least 2 characters",
+ "not_in_inventory": "Product not in inventory",
+ "appliance_exists": "Appliance already exists",
+ "already_exists": "Already exists",
+ "network_retry": "Connection error. Try again.",
+ "select_items": "Select at least one product",
+ "server_offline": "Server connection lost",
+ "server_restored": "Server connection restored",
+ "server_retry": "Retry",
+ "unknown": "Unknown error",
+ "prefix": "Error",
+ "no_inventory_entry": "No inventory entry found",
+ "offline_title": "No connection",
+ "offline_subtitle": "The app cannot reach the server. Check your Wi-Fi connection.",
+ "offline_checking": "Checking connection…",
+ "offline_restored": "Connection restored!",
+ "offline_continue": "Continue in offline mode",
+ "offline_reading_cache": "Reading from local cache",
+ "offline_ops_pending": "{n} operations pending",
+ "offline_synced": "{n} operations synced",
+ "offline_ai_disabled": "Not available offline",
+ "offline_cache_ready": "Offline — {n} items cached"
+ },
+ "confirm_placeholder_search": null,
+ "confirm": {
+ "remove_item": "Do you really want to remove this product from inventory?",
+ "kiosk_exit": "Exit kiosk mode?",
+ "cancel": "Cancel",
+ "proceed": "Confirm",
+ "discard_one": "Discard 1 piece"
+ },
+ "location": {
+ "dispensa": "Pantry",
+ "frigo": "Fridge",
+ "freezer": "Freezer"
+ },
+ "edit": {
+ "title": "Edit {name}",
+ "unknown_hint": "Enter the product name and information",
+ "label_name": "🏷️ Product name",
+ "choose_location_title": "Which location?",
+ "choose_location_hint": "Choose the location to edit:",
+ "confirm_large_qty": "You are setting the quantity to {qty} {unit}. This seems unusually high. Confirm?"
+ },
+ "screensaver": {
+ "recipe_btn": "Recipes",
+ "scan_btn": "Scan product"
+ },
+ "days": {
+ "mon": "Monday",
+ "tue": "Tuesday",
+ "wed": "Wednesday",
+ "thu": "Thursday",
+ "fri": "Friday",
+ "sat": "Saturday",
+ "sun": "Sunday",
+ "mon_short": "Mon",
+ "tue_short": "Tue",
+ "wed_short": "Wed",
+ "thu_short": "Thu",
+ "fri_short": "Fri",
+ "sat_short": "Sat",
+ "sun_short": "Sun"
+ },
+ "meal_types": {
+ "lunch": "Lunch",
+ "dinner": "Dinner",
+ "colazione": "Breakfast",
+ "merenda": "Snack",
+ "dolce": "Dessert",
+ "succo": "Fruit Juice",
+ "pranzo": "Lunch",
+ "cena": "Dinner"
+ },
+ "scale": {
+ "status_connected": "Scale connected",
+ "status_searching": "Gateway connected, waiting for scale…",
+ "status_disconnected": "Scale gateway unreachable",
+ "status_error": "Gateway connection error",
+ "not_connected": "Scale gateway not connected",
+ "read_btn": "⚖️ Read from scale",
+ "reading_title": "Scale reading",
+ "place_on_scale": "Place the product on the scale…",
+ "waiting_stable": "Weight will be captured automatically once the reading is stable.",
+ "no_url": "Enter the gateway URL",
+ "testing": "⏳ Testing connection…",
+ "connected_ok": "Gateway connection successful!",
+ "timeout": "Timeout: no response from gateway",
+ "error_connect": "Cannot connect to gateway",
+ "tab": "Smart Scale",
+ "low_weight": "Weight < 10 g · enter manually\n(auto-reading requires at least 10 g)",
+ "density_hint": "(density {density} g/ml)",
+ "ml_hint": "(will be converted to ml)",
+ "weight_detected": "Weight detected — wait 10s for stability…",
+ "weight_too_low": "Weight too low — waiting…",
+ "stable": "✓ Stable",
+ "auto_confirm": "✅ {val} {unit} — auto-confirm in 5s (tap to cancel)",
+ "cancelled_replace": "Cancelled — replace the ingredient on the scale to resume"
+ },
+ "prediction": {
+ "expected_qty": "Expected: {expected} {unit}",
+ "actual_qty": "Current: {actual} {unit}",
+ "check_suggestion": "Check or weigh the remaining quantity"
+ },
+ "date": {
+ "today": "📅 Today",
+ "yesterday": "📅 Yesterday"
+ },
+ "scanner": {
+ "title_barcode": "🔖 Scan Barcode",
+ "barcode_hint": "Frame the product barcode",
+ "barcode_manual_placeholder": "Or enter manually...",
+ "barcode_use_btn": "✅ Use this code",
+ "ai_identifying": "🤖 Identifying product...",
+ "ai_analyzing": "🤖 AI analysis in progress...",
+ "product_label_hint": "Frame the product label",
+ "expiry_label_hint": "Frame the expiry date printed on the product",
+ "capture_btn": "📸 Capture",
+ "capture_photo_btn": "📸 Take Photo",
+ "retake_btn": "🔄 Retake",
+ "camera_error_hint": "Ensure you use HTTPS and have granted camera permissions. You can enter the barcode manually or use AI identification.",
+ "no_barcode": "No barcode",
+ "save_new_btn": "🆕 None of these — save as new",
+ "expiry_found": "Date found",
+ "expiry_read_fail": "Cannot read the date.",
+ "expiry_raw_label": "Read"
+ },
+ "lowstock": {
+ "title": "⚠️ Running low!",
+ "message": "{name} is running low — only {qty} remaining.",
+ "question": "Do you want to add it to the shopping list?",
+ "yes": "🛒 Yes, add to Bring!",
+ "no": "No, I'm fine for now"
+ },
+ "move": {
+ "title": "📦 Move the rest?",
+ "question": "Do you want to move the {thing} of {name} to another location?",
+ "question_short": "Do you want to move the {thing} to another location?",
+ "thing_opened": "opened package",
+ "thing_rest": "rest",
+ "stay_btn": "No, stay in {location}",
+ "moved_toast": "📦 Opened package moved to {location}",
+ "vacuum_restore": "Restore vacuum sealed",
+ "vacuum_seal_rest": "Vacuum seal the rest"
+ },
+ "nova": {
+ "1": "Unprocessed",
+ "2": "Culinary ingredient",
+ "3": "Processed",
+ "4": "Ultra-processed"
+ },
+ "meal_plan_types": {
+ "pasta": "Pasta",
+ "riso": "Rice",
+ "carne": "Meat",
+ "pesce": "Fish",
+ "legumi": "Legumes",
+ "uova": "Eggs",
+ "formaggio": "Cheese",
+ "pizza": "Pizza",
+ "affettati": "Cold Cuts",
+ "verdure": "Veggies",
+ "zuppa": "Soup",
+ "insalata": "Salad",
+ "pane": "Bread/Sandwich",
+ "dolce": "Dessert",
+ "libero": "Free"
+ },
+ "meal_sub": {
+ "dolce_torta": "Cake",
+ "dolce_crema": "Cream / Pudding",
+ "dolce_crumble": "Crumble / Tart",
+ "dolce_biscotti": "Cookies / Pastries",
+ "dolce_frutta": "Fruit Dessert",
+ "succo_dolce": "Sweet / Fruity",
+ "succo_energizzante": "Energizing",
+ "succo_detox": "Detox / Green",
+ "succo_rinfrescante": "Refreshing",
+ "succo_vitaminico": "Vitamin / Citrus"
+ },
+ "meal_plan": {
+ "reset_success": "Weekly plan reset",
+ "not_available": "not available in pantry",
+ "suggested_by": "suggested by weekly plan"
+ },
+ "nutrition": {
+ "title": "🥗 Food Analysis",
+ "score_excellent": "😄 Excellent",
+ "score_good": "🙂 Good",
+ "score_improve": "😬 Improvable",
+ "label_health": "🌿 Health",
+ "label_variety": "🎨 Variety",
+ "label_fresh": "❄️ Fresh",
+ "source": "Based on {n} products in your pantry · EverShelf",
+ "products_count": "products",
+ "today_title": "🥗 Your pantry today",
+ "products_n": "{n} products",
+ "macros_title": "Estimated Macronutrients",
+ "macros_proteins": "Proteins",
+ "macros_carbs": "Carbohydrates",
+ "macros_fat": "Fat",
+ "macros_fiber": "Fibre",
+ "macros_source": "Estimate based on {n} pantry products"
+ },
+ "facts": {
+ "greeting_morning": "Good morning",
+ "greeting_afternoon": "Good afternoon",
+ "greeting_evening": "Good evening",
+ "pantry_waiting": "{greeting}! Your Pantry awaits.",
+ "expired_one": "You have 1 expired product in your pantry. Check it!",
+ "expired_many": "You have {n} expired products in your pantry. Check them!",
+ "expired_list": "Expired products: {names}",
+ "expired_list_more": "and {n} more",
+ "freezer_expired_ok": "{name} is expired, but being in the freezer it may still be fine! Check it.",
+ "freezer_expired_old": "{name} in the freezer has been expired too long. Better to discard it.",
+ "fridge_expired_one": "You have 1 expired product in the fridge!",
+ "fridge_expired_many": "You have {n} expired products in the fridge!",
+ "expiring_today": "{name} expires today! Use it right away.",
+ "expiring_tomorrow": "{name} expires tomorrow. Plan ahead!",
+ "expiring_days": "{name} expires in {days} days.",
+ "expiring_many": "You have {n} products expiring soon.",
+ "expiring_this_week": "{n} products expire this week. Plan your meals accordingly!",
+ "expiring_item_loc": "{name} ({loc}) expires in {days} {dayslabel}.",
+ "expiring_this_month": "{n} products will expire this month.",
+ "shopping_add": "Add to list: {names} 🛒",
+ "shopping_more": "and {n} more",
+ "shopping_empty": "Shopping list empty. All stocked up! ✅",
+ "in_fridge": "In the fridge: {name}.",
+ "in_freezer": "In the freezer: {name}. Don't forget it!",
+ "top_category": "Top category is {icon} {cat} with {n} products.",
+ "cat_meat": "You have {n} meat products. 🥩",
+ "cat_dairy": "You have {n} dairy products at home. 🥛",
+ "cat_veggies": "You have {n} types of vegetables. Great for your health! 🥬",
+ "cat_fruit": "You have {n} types of fruit. 🍎",
+ "cat_drinks": "You have {n} drinks available. 🥤",
+ "cat_frozen": "You have {n} frozen items. ❄️",
+ "cat_pasta": "You have {n} types of pasta. 🍝 How about a carbonara?",
+ "cat_canned": "You have {n} canned goods in your pantry. 🥫",
+ "cat_snacks": "You have {n} snacks. Resist the temptation! 🍪",
+ "cat_condiments": "You have {n} condiments available. 🧂",
+ "item_random": "Did you know? You have {name} in {loc}.",
+ "item_qty": "{name}: you have {qty}.",
+ "no_expiry_count": "{n} products have no expiry date set.",
+ "furthest_expiry": "The product with the furthest expiry is {name}: {months} months.",
+ "high_qty": "You have a great stock of {name}: {qty}!",
+ "low_qty_item": "{name} is running low. Add it to your shopping list?",
+ "low_qty_count": "{n} products are almost out.",
+ "morning_bread": "Good morning! You have bread for breakfast. 🍞",
+ "morning_milk": "Is there milk in the fridge for a cappuccino? ☕🥛",
+ "morning_fruit": "Good morning! Some fresh fruit is a great way to start. 🍎",
+ "noon_pasta": "Lunchtime… How about a nice bowl of pasta? 🍝",
+ "noon_salad": "A fresh salad for lunch? You have {n} vegetables! 🥗",
+ "evening_meat": "For dinner you could use the meat you have. 🥩",
+ "evening_fish": "How about fish for dinner? 🐟",
+ "evening_expiring": "You have {n} products expiring this week — use them tonight!",
+ "night_reminder": "Good night! Remember to use tomorrow: {names}.",
+ "weekly_balance": "Weekly balance: +{in} added, −{out} consumed.",
+ "weekly_added": "You added {n} products this week.",
+ "weekly_consumed": "You consumed {n} products this week. Well done!",
+ "tip_freezer": "💡 Frozen products last much longer than the expiry date.",
+ "tip_bread": "💡 Frozen bread keeps its freshness for weeks.",
+ "tip_fifo": "💡 To avoid waste, use products closest to expiry first (FIFO).",
+ "tip_meat": "💡 Meat in the freezer can last up to 6 months safely.",
+ "tip_no_refreeze": "💡 Never refreeze a thawed product. Cook it right away!",
+ "tip_fridge": "💡 A tidy fridge saves you time and money.",
+ "tip_canned": "💡 Opened canned goods should go in the fridge and be consumed within a few days.",
+ "top_brand": "The most common brand in your pantry is {brand} with {n} products.",
+ "combo_pasta": "You have pasta and condiments: ready for a first course! 🍝",
+ "combo_sandwich": "Bread and meat: a quick sandwich is always a good idea! 🥪",
+ "combo_balanced": "Vegetables and meat: you have everything for a balanced meal! 🥗🥩",
+ "pantry_empty": "The pantry is empty! Time to go shopping. 🛒",
+ "pantry_empty_scan": "No products registered. Scan something to start!",
+ "location_distribution": "Distribution: {parts}",
+ "day": "day",
+ "days": "days"
+ },
+ "kiosk_session": {
+ "first_item": "First item: {name}!",
+ "items_two_four": "{n} items — warming up 🚀",
+ "items_five_nine": "{n} items — great pace! 💪",
+ "items_ten_twenty": "{n} items — almost a record 🏆",
+ "items_twenty_plus": "{n} items — epic shopping! 🛒🔥",
+ "duplicates_one": "1 duplicate (same thing twice)",
+ "duplicates_many": "{n} duplicates (picked multiple times)",
+ "top_category": "Top category: {cat} ({count}×)",
+ "items_fallback": "{n} item{plural} added"
+ },
+ "kiosk": {
+ "check_btn": "🔍 Check for updates",
+ "checking": "⏳ Checking…",
+ "error_check": "Error during update check",
+ "error_start_install": "Error starting installation",
+ "version_installed": "Installed: {v}",
+ "update_available": "⬆️ New version available: {latest} (installed: {current})",
+ "up_to_date": "✅ You are up to date — version {v}",
+ "too_old": "⚠️ The installed kiosk is too old for automatic update checking. Press the button below to download and install the new version directly.",
+ "manual_install": "⚠️ This kiosk does not support automatic installation. Manual procedure: 1. Exit the kiosk (✕ button top left) 2. Uninstall the EverShelf Kiosk app 3. Download and install the new APK from GitHub:",
+ "starting_download": "⏳ Starting download…",
+ "install_btn": "⬇️ Install update",
+ "exit_title": "Exit kiosk",
+ "refresh_title": "Refresh page"
+ },
+ "update": {
+ "new_version": "New version",
+ "btn": "Update"
+ },
+ "gemini": {
+ "chat_title": "Chat with Gemini",
+ "not_configured": "🤖 Gemini not configured — set GEMINI_API_KEY in settings"
+ },
+ "appliances": {
+ "empty": "No appliances added"
+ },
+ "about": {
+ "title": "About",
+ "version": "Version",
+ "report_bug": "Report a Bug",
+ "report_bug_hint": "Something not working? Send us a report directly from the app.",
+ "report_bug_modal_title": "Report a Bug",
+ "report_type_bug": "Bug",
+ "report_type_feature": "Feature",
+ "report_type_question": "Question",
+ "report_field_title": "Title",
+ "report_field_title_ph": "Brief description of the issue",
+ "report_field_desc": "Description",
+ "report_field_desc_ph": "Describe the issue in detail…",
+ "report_field_steps": "Steps to reproduce (optional)",
+ "report_field_steps_ph": "1. Go to…\n2. Tap…\n3. See the error…",
+ "report_auto_info": "Automatically attached: version {version}, language {lang}.",
+ "report_send_btn": "Send report",
+ "report_bug_sending": "Sending…",
+ "report_bug_sent": "Report sent — thank you!",
+ "report_bug_error": "Could not send the report. Check your connection.",
+ "changelog": "Changelog",
+ "github": "GitHub Repository"
+ },
+ "export": {
+ "title": "Export inventory",
+ "hint": "Download the current inventory as CSV or open a print-ready version (PDF).",
+ "btn_csv": "Download CSV",
+ "btn_pdf": "PDF / Print",
+ "btn_title": "Export"
+ },
+ "startup": {
+ "connecting": "Connecting to server...",
+ "check_php_memory": "PHP memory",
+ "check_php_timeout": "PHP timeout",
+ "check_php_upload": "PHP upload",
+ "check_data_dir": "Data directory",
+ "check_rate_limits": "Rate limits dir",
+ "check_backups": "Backup dir",
+ "check_write_test": "Disk write test",
+ "check_disk_space": "Disk space",
+ "check_db_legacy": "Legacy DB (dispensa.db)",
+ "check_db_connect": "Database connection",
+ "check_db_tables": "Database tables",
+ "check_db_integrity": "Database integrity",
+ "check_db_wal": "WAL mode",
+ "check_db_size": "Database size",
+ "check_db_rows": "Inventory data",
+ "check_env": ".env file",
+ "check_gemini": "Gemini AI key",
+ "check_bring_creds": "Bring! credentials",
+ "check_bring_token": "Bring! token",
+ "check_tts": "Text-to-Speech URL",
+ "check_scale": "Scale gateway",
+ "check_curl_ssl": "cURL SSL",
+ "check_internet": "Internet connection",
+ "fresh_install": "fresh install",
+ "warnings_found": "warnings found",
+ "all_ok": "System OK",
+ "critical_error_short": "Critical error",
+ "critical_error": "Critical error: the app cannot start. Check your server logs.",
+ "critical_error_intro": "The app cannot start due to the following issues:",
+ "error_network": "Cannot reach the server.",
+ "error_network_detail": "The browser cannot reach the PHP server.\n\nPossible causes:\n• Apache/PHP server is not running\n• Network or firewall issue\n• Incorrect app URL\n\nMake sure the server is started and try again.",
+ "retry": "Retry",
+ "syncing_local": "Syncing local data...",
+ "sync_done": "Local data synced"
+ },
+ "stats_monthly": {
+ "title": "Monthly Stats",
+ "consumed": "products used",
+ "trend_up": "+{pct}% vs {prev}",
+ "trend_down": "-{pct}% vs {prev}",
+ "trend_same": "same pace as last month",
+ "added": "added",
+ "wasted": "wasted",
+ "top_used": "top used",
+ "top_cats": "Top categories",
+ "source": "Transaction history · current month"
+ }
}
\ No newline at end of file
diff --git a/translations/es.json b/translations/es.json
index dd7cc40..f7714bb 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -1,1401 +1,1406 @@
{
- "app": {
- "name": "EverShelf",
- "loading": "Cargando..."
- },
- "nav": {
- "title": "EverShelf",
- "home": "Inicio",
- "inventory": "Despensa",
- "recipes": "Recetas",
- "shopping": "Compras",
- "log": "Registro",
- "settings": "Ajustes"
- },
- "btn": {
- "back": "← Volver",
- "save": "💾 Guardar",
- "cancel": "✕ Cancelar",
- "close": "Cerrar",
- "add": "✅ Añadir",
- "delete": "Eliminar",
- "edit": "✏️ Editar",
- "use": "Usar",
- "edit_item": "Editar",
- "search": "🔍 Buscar",
- "go": "✅ Ir",
- "toggle_password": "👁️ Mostrar/Ocultar",
- "load_more": "Cargar más...",
- "save_config": "💾 Guardar configuración",
- "save_product": "💾 Guardar producto",
- "restart": "↺ Reiniciar",
- "reset_default": "↺ Restablecer valores por defecto",
- "save_info": "💾 Guardar información",
- "retry": "🔄 Reintentar",
- "yes_short": "Sí",
- "no_short": "No"
- },
- "form": {
- "select_placeholder": "-- Seleccionar --"
- },
- "locations": {
- "dispensa": "Despensa",
- "frigo": "Nevera",
- "freezer": "Congelador",
- "altro": "Otro"
- },
- "categories": {
- "latticini": "Lácteos",
- "carne": "Carne",
- "pesce": "Pescado",
- "frutta": "Fruta",
- "verdura": "Verduras",
- "pasta": "Pasta y Arroz",
- "pane": "Pan y Panadería",
- "surgelati": "Congelados",
- "bevande": "Bebidas",
- "condimenti": "Condimentos",
- "snack": "Snacks y Dulces",
- "conserve": "Conservas",
- "cereali": "Cereales y Legumbres",
- "igiene": "Higiene",
- "pulizia": "Limpieza",
- "altro": "Otro",
- "select": "-- Seleccionar --"
- },
- "units": {
- "pz": "uds",
- "conf": "paq",
- "g": "g",
- "ml": "ml",
- "pieces": "Unidades",
- "grams": "Gramos",
- "box": "Paquete",
- "boxes": "Paquetes",
- "millilitres": "Mililitros",
- "from": "de"
- },
- "shopping_sections": {
- "frutta_verdura": "Frutas y Verduras",
- "carne_pesce": "Carne y Pescado",
- "latticini": "Lácteos y Frescos",
- "pane_dolci": "Pan y Dulces",
- "pasta": "Pasta y Cereales",
- "conserve": "Conservas y Salsas",
- "surgelati": "Congelados",
- "bevande": "Bebidas",
- "pulizia_igiene": "Limpieza e Higiene",
- "altro": "Otro"
- },
- "dashboard": {
- "expired_title": "🚫 Caducado",
- "expiring_title": "⏰ Caduca pronto",
- "stats_period": "📊 Últimos 30 días",
- "opened_title": "📦 Productos abiertos",
- "review_title": "🔍 Por revisar",
- "review_hint": "Cantidades inusuales. Confirma si son correctas o modifícalas.",
- "quick_recipe": "Receta rápida con productos que caducan",
- "banner_review_title": "Cantidad anormal",
- "banner_review_action_ok": "Es correcto",
- "banner_review_action_finish": "🗑️ Todo terminado",
- "banner_review_action_edit": "Corregir",
- "banner_review_action_weigh": "Pesar",
- "banner_review_dismiss": "Ignorar",
- "banner_prediction_title": "Consumo por revisar",
- "banner_prediction_hint": "La estimación de consumo se adapta a los datos recientes: confirma solo si la cantidad actual es correcta.",
- "banner_prediction_action_confirm": "Confirmar {qty} {unit}",
- "banner_prediction_action_weigh": "Pesar ahora",
- "banner_prediction_action_edit": "Actualizar cantidad",
- "banner_expired_title": "Producto caducado",
- "banner_expired_today": "Caducado hoy",
- "banner_expired_days": "Caducado hace {days} días",
- "banner_expired_action_use": "Usar de todas formas",
- "banner_expired_action_finished": "¡Ya lo terminé!",
- "banner_expired_action_throw": "Lo tiré",
- "banner_expired_action_edit": "Corregir fecha",
- "banner_anomaly_action_edit": "Corregir inventario",
- "banner_anomaly_action_dismiss": "La cantidad es correcta",
- "banner_no_expiry_title": "Caducidad faltante: {name}",
- "banner_no_expiry_detail": "Este producto no tiene fecha de caducidad. ¿Quieres añadir una o confirmar que no caduca?",
- "banner_no_expiry_action_set": "Establecer fecha de caducidad",
- "banner_no_expiry_action_dismiss": "No caduca ✓",
- "banner_no_expiry_toast_dismissed": "Marcado como «sin fecha de caducidad»",
- "banner_expiring_title": "Caduca pronto",
- "banner_expiring_today": "¡Caduca hoy!",
- "banner_expiring_tomorrow": "Caduca mañana",
- "banner_expiring_days": "Caduca en {days} días",
- "banner_expiring_action_use": "Usar ahora",
- "banner_finished_title": "¿terminado?",
- "banner_finished_detail": "Registré que {name} llegó a cero de stock. ¿Realmente se acabó o todavía tienes algo?",
- "banner_finished_action_yes": "Sí, se acabó",
- "banner_finished_action_no": "No, todavía tengo",
- "banner_review_unusual_pkg_title": "Tamaño de paquete inusual",
- "banner_review_unusual_pkg_detail": "Configuraste un paquete de {qty} {unit} — el tamaño parece muy grande. Comprueba si es correcto o edita.",
- "banner_review_low_qty_title": "Cantidad muy baja",
- "banner_review_low_qty_detail": "Solo tienes {qty} en stock — parece muy poco, puede ser un error de escritura. Confirma si es correcto.",
- "banner_review_high_qty_title": "Cantidad inusualmente alta",
- "banner_review_high_qty_detail": "Tienes {qty} en stock — la cifra parece muy alta. Confirma si es correcto o edita.",
- "banner_prediction_rate_day": "Media ~{n} {unit}/día",
- "banner_prediction_rate_week": "Media ~{n} {unit}/semana",
- "banner_prediction_days_ago": "Hace {n} días reabasteciste",
- "banner_prediction_more": "estimación anterior: {expected} {unit}{time}; cantidad actual: {actual} {unit}.",
- "banner_prediction_less": "estimación: {expected} {unit}{time}; cantidad actual: {actual} {unit}. Si tu ritmo de uso cambió, la previsión se actualiza automáticamente.",
- "banner_finished_zero": "El inventario muestra cero, pero los movimientos registrados sugieren que no debería estar vacío.",
- "banner_finished_expected": "Según los registros deberías tener todavía {qty} {unit}.",
- "banner_finished_check": "¿Puedes comprobarlo?",
- "banner_anomaly_phantom_title": "tienes más stock del esperado",
- "banner_anomaly_phantom_detail": "El inventario indica {inv_qty} {unit}, pero según los registros solo deberías tener {expected_qty} {unit}. ¿Añadiste stock sin registrarlo?",
- "banner_anomaly_untracked_title": "stock no registrado como entrada",
- "banner_anomaly_untracked_detail": "Tienes {inv_qty} {unit} en inventario, pero las salidas registradas superan las entradas — el stock inicial probablemente nunca se añadió como transacción «entrada». Puedes corregir la cantidad o registrar las entradas faltantes.",
- "banner_anomaly_ghost_title": "tienes menos stock del esperado",
- "banner_anomaly_ghost_detail": "Según las operaciones registradas deberías tener {expected_qty} {unit} de {name}, pero el inventario solo muestra {inv_qty} {unit}. ¿Tomaste stock sin registrarlo?",
- "consumed": "Consumido: {n} ({pct}%)",
- "wasted": "Desperdiciado: {n} ({pct}%)",
- "more_opened": "y {n} más abiertos...",
- "banner_expired_detail": "{when} · aún tienes {qty}.",
- "banner_opened_detail": "{when} en {location} · aún tienes {qty}.",
- "banner_explain_title": "Pedir explicación a Gemini",
- "banner_explain_btn": "Explicar",
- "banner_analyzing": "🤖 Analizando…"
- },
- "inventory": {
- "title": "Despensa",
- "filter_all": "Todo",
- "search_placeholder": "🔍 Buscar producto...",
- "recent_title": "🕐 Usados recientemente",
- "popular_title": "⭐ Más usados",
- "empty": "No hay productos aquí.\n¡Escanea un producto para añadirlo!",
- "no_items_found": "No se encontraron artículos",
- "qty_remainder_suffix": "restante",
- "vacuum_badge": "🫙 Al vacío",
- "opened_badge": "📭 Abierto",
- "label_expiry": "📅 Caducidad",
- "label_storage": "🫙 Almacenamiento",
- "label_status": "📭 Estado",
- "opened_since": "Abierto desde el {date}",
- "label_position": "📍 Ubicación",
- "label_quantity": "📦 Cantidad",
- "label_added": "📅 Añadido",
- "empty_text": "No hay productos aquí. ¡Escanea un producto para añadirlo!",
- "empty_db": "No hay productos en la base de datos. ¡Escanea un producto para empezar!",
- "qty_trace": "< 1"
- },
- "scan": {
- "title": "Escáner",
- "mode_shopping": "🛒 Modo compras",
- "mode_shopping_end": "✅ Finalizar compras",
- "spesa_btn": "🛒 Compras",
- "zoom": "Zoom",
- "tab_barcode": "Código de barras",
- "tab_name": "Nombre",
- "tab_ai": "IA",
- "recents_label": "Recientes",
- "torch_hint": "Linterna",
- "torch_on": "Linterna encendida",
- "torch_off": "Linterna apagada",
- "torch_unavailable": "Linterna no disponible en este dispositivo",
- "flip_hint": "Cambiar cámara",
- "flip_front": "Cámara frontal",
- "flip_back": "Cámara trasera",
- "num_ocr_btn": "🔢 Leer números con IA",
- "num_ocr_searching": "Buscando código de barras con IA...",
- "num_ocr_found": "Código encontrado: {code}",
- "num_ocr_not_found": "No se encontró código de barras en la imagen",
- "barcode_placeholder": "Introduce el código de barras...",
- "quick_name_divider": "o escribe el nombre",
- "quick_name_placeholder": "Ej.: Manzanas, Calabacín, Pan...",
- "manual_entry": "✏️ Entrada manual",
- "ai_identify": "🤖 Identificar con IA",
- "hint": "Escanea el código de barras, escribe el nombre del producto o usa la IA para identificarlo",
- "debug_toggle": "🐛 Registro de depuración",
- "barcode_acquired": "🔖 Código de barras escaneado: {code}",
- "scan_barcode": "🔖 Escanear código de barras",
- "create_named": "Crear {name}",
- "new_without_barcode": "Nuevo producto sin código de barras",
- "status_ready": "Apunta la cámara al código de barras",
- "status_scanning": "Escaneando...",
- "status_partial": "Detectado: {code} — verificando...",
- "status_invalid": "Inválido: {code} — reintentando",
- "status_confirmed": "Confirmado!",
- "status_parallel": "Escaneo combinado activo..."
- },
- "action": {
- "title": "¿Qué quieres hacer?",
- "add_btn": "📥 AÑADIR",
- "add_sub": "a la despensa/nevera",
- "use_btn": "📤 USAR / CONSUMIR",
- "use_sub": "de la despensa/nevera",
- "have_title": "📦 ¡Ya en stock!",
- "add_more_sub": "añadir más",
- "use_qty_sub": "cuánto usaste",
- "throw_btn": "🗑️ DESECHAR",
- "throw_sub": "tirar",
- "edit_sub": "caducidad, ubicación…",
- "create_recipe_btn": "Receta"
- },
- "add": {
- "title": "Añadir a la despensa",
- "location_label": "📍 ¿Dónde lo guardas?",
- "quantity_label": "📦 Cantidad",
- "conf_size_label": "📦 Cada paquete contiene:",
- "conf_size_placeholder": "ej. 300",
- "vacuum_label": "🫙 Al vacío",
- "vacuum_hint": "La fecha de caducidad se ampliará automáticamente",
- "submit": "✅ Añadir",
- "purchase_type_label": "🛒 Este producto es...",
- "new_btn": "🆕 Recién comprado",
- "existing_btn": "📦 Ya lo tenía",
- "remaining_label": "📦 Cantidad restante",
- "remaining_hint": "¿Aproximadamente cuánto queda?",
- "remaining_full": "🟢 Lleno",
- "remaining_half": "🟠 Por la mitad",
- "estimated_expiry": "Caducidad estimada:",
- "suffix_freezer": "(congelador)",
- "suffix_vacuum": "(al vacío)",
- "hint_modify": "📝 Puedes cambiar la fecha o escanearla con la cámara",
- "scan_expiry_title": "📷 Escanear fecha de caducidad",
- "product_added": "✅ ¡{name} añadido!{qty}",
- "suffix_freezer_vacuum": "(congelador + al vacío)",
- "history_badge_tip": "Media de {n} entradas anteriores",
- "vacuum_question": "¿Al vacío?",
- "vacuum_saved": "🔒 ¡Al vacío!"
- },
- "use": {
- "title": "Usar / Consumir",
- "location_label": "📍 ¿De dónde?",
- "quantity_label": "¿Cuánto usaste?",
- "change": "cambiar",
- "partial_hint": "O especifica la cantidad usada:",
- "partial_piece_hint": "¿Usaste solo una parte?",
- "piece": "unidad",
- "one_whole": "1 entero",
- "use_all": "🗑️ Todo usado / Terminado",
- "submit": "📤 Usar esta cantidad",
- "available": "📦 Disponible:",
- "opened_badge": "ABIERTO",
- "not_in_inventory": "⚠️ Producto no en inventario.",
- "expiry_warning": "⚠️ ¡Usa primero el{loc} que caduca el {date} — {when}!",
- "expiry_warning_opened": "⚠️ El{loc} lleva abierto {when} — ¡úsalo primero!",
- "throw_title": "🗑️ Desechar producto",
- "throw_all": "🗑️ Desechar TODO ({qty})",
- "throw_qty_label": "¿Cuánto desechar?",
- "throw_qty_hint": "o introduce una cantidad:",
- "throw_partial_btn": "🗑️ Desechar esta cantidad",
- "when_expired": "caducado hace {n} días",
- "when_today": "caduca hoy",
- "when_tomorrow": "caduca mañana",
- "when_days": "caduca en {n} días",
- "toast_used": "📤 {qty} de {name} usado",
- "toast_bring": "🛒 Producto terminado → añadido a Bring!",
- "toast_opened_finished": "🔓 ¡Paquete abierto de {name} terminado!",
- "disambiguation_hint": "¿Qué quieres decir con «todo terminado»?",
- "disambiguation_all": "🗑️ Terminar TODO ({qty})",
- "error_exceeds_stock": "⚠️ ¡No puedes usar más de lo que tienes disponible!",
- "use_all_confirm_title": "✅ Terminar todo",
- "use_all_confirm_msg": "Confirma que has terminado el producto:",
- "use_all_confirm_btn": "✅ Sí, terminado",
- "throw_all_confirm_title": "🗑️ Desechar todo",
- "throw_all_confirm_msg": "¿Realmente quieres tirar todo el producto?",
- "throw_all_confirm_btn": "🗑️ Sí, desechar"
- },
- "product": {
- "title_new": "Nuevo producto",
- "title_edit": "Editar producto",
- "ai_fill": "📷 Sacar foto e identificar con IA",
- "ai_fill_hint": "La IA rellenará automáticamente los campos del producto",
- "name_label": "🏷️ Nombre del producto *",
- "name_placeholder": "Ej.: Leche entera, Pasta penne...",
- "brand_label": "🏢 Marca",
- "brand_placeholder": "Ej.: Barilla, Danone, Heinz...",
- "category_label": "📂 Categoría",
- "unit_label": "📏 Unidad de medida",
- "default_qty_label": "🔢 Cantidad por defecto",
- "conf_size_label": "📦 Cada paquete contiene:",
- "conf_size_placeholder": "ej. 300",
- "notes_label": "📝 Notas",
- "notes_placeholder": "Ej.: sin lactosa, ecológico, guardar en nevera tras abrir...",
- "barcode_label": "🔖 Código de barras",
- "barcode_placeholder": "Código de barras (si disponible)",
- "barcode_hint": "⚠️ ¡Añade el código de barras para la próxima vez solo tendrás que escanear!",
- "submit": "💾 Guardar producto",
- "name_required": "Introduce el nombre del producto",
- "conf_size_required": "Especifica el contenido del paquete",
- "expiry_estimated": "Caducidad estimada:",
- "scan_expiry": "Escanear fecha de caducidad",
- "expiry_hint": "📝 Puedes editar la fecha o escanearla con la cámara",
- "add_batch": "📦 + Lote con fecha diferente",
- "package_info": "📦 Paquete: {info}",
- "edit_catalog": "⚙️ Editar información del producto (nombre, marca, categoría…)",
- "not_recognized": "⚠️ Producto no reconocido",
- "edit_info": "✏️ Editar información",
- "modify_details": "EDITAR\ncaducidad, ubicación…",
- "already_in_pantry": "📋 Ya en la despensa",
- "no_barcode": "Sin código de barras",
- "unknown_product": "Producto no reconocido",
- "edit_name_brand": "Editar nombre/marca",
- "weight_label": "Peso",
- "origin_label": "Origen",
- "labels_label": "Etiquetas",
- "select_variant": "Selecciona la variante exacta o usa los datos de IA:"
- },
- "products": {
- "title": "📦 Todos los productos",
- "search_placeholder": "🔍 Buscar producto...",
- "empty": "No hay productos en la base de datos.\n¡Escanea un producto para empezar!",
- "no_category": "No hay productos en esta categoría"
- },
- "recipes": {
- "title": "🍳 Recetas",
- "generate": "✨ Generar nueva receta",
- "archive_empty": "No hay recetas guardadas. ¡Genera tu primera receta!",
- "dialog_title": "🍳 Receta",
- "dialog_desc": "Generaré una receta saludable usando ingredientes de la despensa, priorizando los productos que caducan.",
- "meal_label": "🕐 ¿Qué comida?",
- "persons_label": "👥 ¿Para cuántas personas?",
- "meal_type_label": "🎯 Tipo de comida",
- "opt_fast": "⚡ Comida rápida",
- "opt_light": "🥗 Apetito ligero",
- "opt_expiry": "⏰ Priorizar productos que caducan",
- "opt_healthy": "💚 Extra saludable",
- "opt_opened": "📦 Priorizar productos abiertos",
- "opt_zero_waste": "♻️ Cero desperdicio",
- "generate_btn": "✨ Generar receta",
- "loading_msg": "Preparando tu receta...",
- "start_cooking": "👨🍳 Modo cocina",
- "regenerate": "🔄 Generar otra",
- "regen_choice_title": "¿Qué quieres hacer con esta receta?",
- "regen_replace": "🔄 Generar otra (descartar esta)",
- "regen_save_new": "💾 Guardar en el archivo y generar una nueva",
- "close_btn": "✅ Cerrar",
- "ingredients_title": "🧾 Ingredientes",
- "tools_title": "Equipo necesario",
- "steps_title": "👨🍳 Pasos",
- "no_steps": "No hay pasos disponibles",
- "generate_error": "Error de generación",
- "persons_short": "com.",
- "use_ingredient_title": "Usar ingrediente",
- "recipe_qty_label": "Receta",
- "from_where_label": "¿De dónde?",
- "amount_label": "Cuánto",
- "use_amount_btn": "Usar esta cantidad",
- "use_all_btn": "Usar TODO / Terminado",
- "packs_label": "Paquetes",
- "quantity_in_total": "Cantidad en {unit} (total: {total})",
- "packs_of_have": "Paquetes de {size} (tienes {count} paquetes)",
- "scale_wait_stable": "Espera 10s de peso estable para el relleno automático…",
- "ingredient_scaled_toast": "📦 ¡Ingrediente deducido de la despensa!",
- "finished_added_bring_toast": "🛒 Producto terminado → ¡añadido a Bring!",
- "load_error": "Error de carga",
- "favorite": "Añadir a favoritos",
- "unfavorite": "Quitar de favoritos",
- "adjust_persons": "Personas"
- },
- "shopping": {
- "title": "🛒 Lista de la compra",
- "bring_loading": "Conectando a Bring!...",
- "bring_not_configured": "Bring! no está configurado. Añade tu email y contraseña en los ajustes.",
- "tab_to_buy": "🛍️ Por comprar",
- "tab_forecast": "🧠 Previsión",
- "total_label": "💰 Total estimado",
- "section_to_buy": "🛍️ Por comprar",
- "suggestions_title": "💡 Sugerencias IA",
- "suggestions_add": "✅ Añadir selección a Bring!",
- "search_prices": "🔍 Buscar todos los precios",
- "suggest_btn": "Sugerir qué comprar",
- "smart_title": "🧠 Predicciones inteligentes",
- "smart_empty": "No hay predicciones disponibles. Añade productos a tu despensa para recibir predicciones inteligentes.",
- "smart_filter_all": "Todo",
- "smart_filter_critical": "🔴 Urgente",
- "smart_filter_high": "🟠 Pronto",
- "smart_filter_medium": "🟡 Planificar",
- "smart_filter_low": "🟢 Previsión",
- "smart_add": "🛒 Añadir selección a Bring!",
- "empty": "¡Lista de la compra vacía!\nUsa el botón de abajo para generar sugerencias.",
- "already_in_list": "🛒 \"{name}\" ya está en la lista de la compra",
- "already_in_list_short": "ℹ️ Ya en la lista de la compra",
- "add_prompt": "¿Quieres añadirlo a la lista de la compra?",
- "smart_already": "📊 Las predicciones de compra ya predicen {name}",
- "all_searched": "Todos los productos ya han sido buscados. Usa 🔄 para buscar individualmente.",
- "search_complete": "Búsqueda completada: {count} productos",
- "removed_sufficient": "🧹 {removed} producto(s) con stock suficiente eliminado(s) de la lista",
- "suggest_buy": "🛒 Comprar: {qty} {unit}",
- "suggest_buy_approx": "🛒 Al menos: {qty} {unit}",
- "suggest_buy_tip": "Cantidad sugerida basada en tus últimos 14 días de consumo",
- "suggest_buy_approx_tip": "Estimación mínima basada en el consumo (compra el tamaño de paquete más cercano)",
- "bring_badge": "🛒 Ya en Bring!",
- "add_urgent_toast": "🔴 {n} producto(s) urgente(s) añadido(s) automáticamente a Bring!",
- "migration_done": "✅ {migrated} actualizado(s), {skipped} ya estaban ok",
- "added_to_bring": "🛒 {n} productos añadidos a Bring!",
- "added_to_bring_skip": "{n} ya presentes",
- "all_on_bring": "¡Todos los productos ya estaban en Bring!",
- "freq_high": "📈 Frecuente",
- "freq_regular": "📊 Regular",
- "freq_occasional": "📉 Ocasional",
- "out_of_stock": "Sin stock",
- "scan_toast": "📷 Escanear: {name}",
- "empty_category": "No hay productos en esta categoría",
- "session_empty": "🛒 Sin productos aún",
- "urgency_critical": "Urgente",
- "urgency_high": "Pronto",
- "urgency_medium": "Planificar",
- "urgency_low": "Previsión",
- "urgency_medium_short": "Medio",
- "urgency_low_short": "Ok",
- "tag_urgent": "🔴 Urgente",
- "tag_priority": "⭐ Prioridad",
- "tag_check": "✅ Comprobar",
- "smart_already_predicted": "📊 Las predicciones inteligentes ya predicen {name}{urgency}.",
- "item_removed": "✅ ¡{name} eliminado de la lista!",
- "urgency_spec_critical": "⚡ Urgente",
- "urgency_spec_high": "🟠 Pronto",
- "bring_add_n": "Añadir {n} a Bring!",
- "bring_add_selected": "Añadir selección a Bring!",
- "bring_adding": "Añadiendo...",
- "bring_added_one": "1 producto añadido a Bring!",
- "bring_added_many": "{n} productos añadidos a Bring!",
- "bring_skipped": "({n} ya en la lista)",
- "force_sync": "Forzar sincronización de Bring!",
- "scan_target_label": "Estás buscando",
- "scan_target_found": "¡Encontrado! Eliminar de la lista",
- "bring_add_one": "Añadir 1 producto a Bring!",
- "bring_add_many": "Añadir {n} productos a Bring!",
- "syncing": "Sincronizando…",
- "sync_done": "Sincronización completada",
- "price_searching": "Buscando...",
- "search_action": "Buscar",
- "open_action": "Abrir",
- "not_found": "No encontrado",
- "search_price": "Buscar precio",
- "tap_to_scan": "Toca para escanear",
- "tag_title": "Etiqueta",
- "remove_title": "Eliminar",
- "found_count": "{found}/{total} productos encontrados",
- "savings_offers": "· 🏷️ Ahorras {amount}€ con ofertas",
- "searching_progress": "Buscando {current}/{total}...",
- "remove_error": "Error al eliminar",
- "btn_fetch_prices": "Buscar precios",
- "price_total_label": "💰 Total estimado:",
- "price_loading": "Buscando precios…",
- "price_not_found": "precio n/d",
- "suggest_loading": "Analizando...",
- "suggest_error": "Error al generar sugerencias",
- "priority_high": "Alta",
- "priority_medium": "Media",
- "priority_low": "Baja",
- "smart_last_update": "Actualizado {time}",
- "names_already_updated": "Todos los nombres ya están actualizados"
- },
- "ai": {
- "title": "🤖 Identificación IA",
- "capture": "📸 Sacar foto",
- "retake": "🔄 Repetir",
- "hint": "Saca una foto del producto y la IA intentará identificarlo",
- "identifying": "🤖 Identificando producto...",
- "no_api_key": "⚠️ Clave API de Gemini no configurada.\nAñade GEMINI_API_KEY al archivo .env en el servidor.",
- "fields_filled": "✅ Campos rellenados por IA",
- "use_data": "✅ Usar datos de IA",
- "use_data_no_barcode": "✅ Usar datos de IA (sin código de barras)"
- },
- "log": {
- "title": "📒 Registro de operaciones",
- "type_added": "Añadido",
- "type_waste": "Desechado",
- "type_used": "Usado",
- "type_bring": "Añadido a Bring!",
- "undone_badge": "Deshecho",
- "undo_title": "Deshacer esta operación",
- "load_error": "Error al cargar el registro",
- "empty": "No hay operaciones registradas.",
- "undo_action_remove": "eliminación de",
- "undo_action_restore": "reabastecimiento de",
- "undo_confirm": "¿Deshacer esta operación?\n→ {action} {name}",
- "undo_success": "↩ Operación deshecha para {name}",
- "already_undone": "Operación ya deshecha",
- "too_old": "No se pueden deshacer operaciones de más de 24 horas",
- "undo_error": "Error al deshacer",
- "recipe_prefix": "Receta"
- },
- "chat": {
- "title": "Chef Gemini",
- "welcome": "¡Hola! Soy tu asistente de cocina",
- "welcome_desc": "¡Pídeme que te prepare un zumo, un snack, un plato rápido… Conozco tu despensa, tus electrodomésticos y tus preferencias!",
- "suggestion_snack": "🍿 Snack rápido",
- "suggestion_juice": "🥤 Zumo/Batido",
- "suggestion_light": "🥗 Algo ligero",
- "suggestion_expiry": "⏰ Usar productos que caducan",
- "clear": "Nueva conversación",
- "placeholder": "Pregunta algo...",
- "cleared": "Chat borrado",
- "suggestion_snack_text": "¿Qué puedo preparar como snack rápido?",
- "suggestion_juice_text": "Hazme un zumo o batido con lo que tengo",
- "suggestion_light_text": "Tengo hambre pero quiero algo ligero",
- "suggestion_expiry_text": "¿Qué está a punto de caducar y cómo puedo usarlo?",
- "transfer_to_recipes": "Transferir a recetas",
- "transferring": "Transfiriendo...",
- "transferred": "¡Añadido a recetas!",
- "open_recipe": "Abrir receta",
- "quick_recipe_prompt": "¡Sugiere una receta rápida PARA UNA PERSONA usando los productos que caducan primero! Ignora el congelador, céntrate en la nevera y la despensa."
- },
- "cooking": {
- "close": "Cerrar",
- "tts_btn": "Leer en voz alta",
- "restart": "↺ Reiniciar",
- "replay": "🔊 Repetir",
- "timer": "⏱️ {time} · Temporizador",
- "prev": "◀ Anterior",
- "next": "Siguiente ▶",
- "ingredient_used": "✔️ Deducido",
- "ingredient_use_btn": "📦 Usar",
- "ingredient_deduct_title": "Deducir de la despensa",
- "timer_expired_tts": "¡Temporizador {label} finalizado!",
- "timer_warning_tts": "¡Atención! {label}: ¡10 segundos restantes!",
- "recipe_done_tts": "¡Receta completada! ¡Buen provecho!",
- "expires_chip": "cad. {date}",
- "finish": "✅ Finalizar",
- "step_fallback": "Paso {n}",
- "zerowaste_label": "♻️ Desperdicio",
- "zerowaste_tip_title": "Consejo sin desperdicios"
- },
- "settings": {
- "title": "⚙️ Ajustes",
- "tab_api": "Claves API",
- "tab_bring": "Bring!",
- "tab_recipe": "Recetas",
- "tab_mealplan": "Plan semanal",
- "tab_appliances": "Electrodomésticos",
- "tab_spesa": "Compra online",
- "tab_camera": "Cámara",
- "tab_security": "Seguridad",
- "tab_tts": "Voz (TTS)",
- "tab_language": "Idioma",
- "tab_scale": "Báscula inteligente",
- "gemini": {
- "title": "🤖 Google Gemini IA",
- "hint": "Clave API para identificación de productos, fechas de caducidad y recetas.",
- "key_label": "Clave API Gemini"
- },
- "bring": {
- "title": "🛒 Lista de la compra Bring!",
- "hint": "Credenciales para la integración con la lista de la compra Bring!",
- "email_label": "📧 Email Bring!",
- "password_label": "🔒 Contraseña Bring!"
- },
- "price": {
- "title": "💰 Estimación de precios (IA)",
- "hint": "Mostrar el coste estimado por producto en la lista de la compra usando IA.",
- "enabled_label": "Activar estimación de precios",
- "country_label": "🌍 País de referencia",
- "currency_label": "💱 Moneda",
- "update_label": "🔄 Actualizar precios cada",
- "update_suffix": "meses"
- },
- "recipe": {
- "title": "🍳 Preferencias de recetas",
- "hint": "Configura las opciones predeterminadas para la generación de recetas.",
- "persons_label": "👥 Raciones por defecto",
- "options_label": "🎯 Opciones de receta por defecto",
- "fast": "⚡ Comida rápida",
- "light": "🥗 Comida ligera",
- "expiry": "⏰ Prioridad caducidad",
- "healthy": "💚 Extra saludable",
- "opened": "📦 Prioridad productos abiertos",
- "zerowaste": "♻️ Cero desperdicio",
- "dietary_label": "🚫 Intolerancias / Restricciones",
- "dietary_placeholder": "Ej.: sin gluten, sin lactosa, vegetariano..."
- },
- "mealplan": {
- "title": "📅 Plan de comidas semanal",
- "hint": "Establece el tipo de comida para cada día. Se usará como guía en la generación de recetas.",
- "enabled": "✅ Activar plan semanal",
- "legend": "🌤️ = Almuerzo · 🌙 = Cena · Toca un badge para cambiarlo.",
- "types_title": "📋 Tipos disponibles",
- "reset_btn": "↺ Restaurar valores por defecto"
- },
- "appliances": {
- "title": "🔌 Electrodomésticos disponibles",
- "hint": "Indica los electrodomésticos que tienes. Se tendrán en cuenta en la generación de recetas.",
- "new_placeholder": "Ej.: Panificadora, Thermomix, Freidora de aire...",
- "quick_title": "Añadir rápido:",
- "oven": "🔥 Horno",
- "microwave": "📡 Microondas",
- "air_fryer": "🍟 Freidora de aire",
- "bread_maker": "🍞 Panificadora",
- "bimby": "🤖 Thermomix/Cookeo",
- "mixer": "🌀 Robot de cocina",
- "steamer": "♨️ Vaporera",
- "pressure_cooker": "🫕 Olla a presión",
- "toaster": "🍞 Tostadora",
- "blender": "🍹 Batidora",
- "empty": "No hay electrodomésticos añadidos"
- },
- "spesa": {
- "title": "🛍️ Compra online",
- "hint": "Configura el proveedor de compra online.",
- "provider_label": "🏪 Proveedor",
- "email_label": "📧 Email",
- "password_label": "🔒 Contraseña",
- "login_btn": "🔐 Iniciar sesión",
- "ai_prompt_label": "🤖 Prompt IA de selección de producto",
- "ai_prompt_placeholder": "Instrucciones para la IA al elegir entre varios productos...",
- "ai_prompt_hint": "La IA usa este prompt para elegir el producto más apropiado de los resultados. Deja vacío para el comportamiento por defecto.",
- "configure_first": "Configura primero la compra online en ajustes",
- "missing_credentials": "Introduce email y contraseña",
- "login_in_progress": "Iniciando sesión...",
- "login_error_prefix": "Error:",
- "login_network_error_prefix": "Error de red:",
- "login_success_default": "¡Inicio de sesión exitoso!",
- "result_name_label": "Nombre",
- "result_card_label": "Tarjeta",
- "result_pickup_label": "Punto de recogida",
- "result_points_label": "Puntos de fidelidad",
- "connected_relogin": "✅ Conectado — Iniciar sesión de nuevo",
- "connected_as": "Conectado como {name}"
- },
- "camera": {
- "title": "📷 Cámara",
- "hint": "Elige qué cámara usar para el escaneo de códigos de barras e identificación IA.",
- "device_label": "📸 Cámara por defecto",
- "back": "📱 Trasera (por defecto)",
- "front": "🤳 Frontal",
- "devices_hint": "Si tienes varias cámaras, puedes seleccionar una específica de la lista de arriba tras conceder los permisos.",
- "detect_btn": "🔄 Detectar cámaras"
- },
- "security": {
- "title": "🔒 Certificado HTTPS",
- "hint": "Si el navegador muestra el error «Tu conexión no es privada» (ERR_CERT_AUTHORITY_INVALID), necesitas instalar el certificado CA en el dispositivo.",
- "download_btn": "📥 Descargar certificado CA",
- "token_title": "🔑 Token de ajustes",
- "token_label": "Token de acceso",
- "token_hint": "Si `SETTINGS_TOKEN` está configurado en el `.env` del servidor, introduce el token aquí antes de guardar los ajustes. Deja vacío si no está configurado.",
- "token_placeholder": "(vacío = sin protección)",
- "token_required_hint": "🔒 Este servidor requiere un token para guardar los ajustes.",
- "cert_instructions": "Instrucciones para Chrome (Android): 1. Descarga el certificado de arriba 2. Ve a Ajustes → Seguridad y privacidad → Más ajustes de seguridad → Instalar desde almacenamiento 3. Selecciona el archivo EverShelf_CA.crt descargado 4. Elige «CA» y confirma 5. Reinicia Chrome
Instrucciones para Chrome (PC): 1. Descarga el certificado de arriba 2. Ve a chrome://settings/certificates 3. Pestaña «Autoridades» → Importar → selecciona el archivo 4. Marca «Confiar en este certificado para identificar sitios web» 5. Reinicia Chrome"
- },
- "tts": {
- "title": "🔊 Voz & TTS",
- "hint": "Configura la síntesis de voz mediante cualquier API REST externa. Los pasos de la receta y los temporizadores expirados se enviarán al endpoint configurado.",
- "enabled": "✅ Activar TTS",
- "engine_label": "⚙️ Motor TTS",
- "engine_browser": "🔇 Navegador (sin conexión, sin configuración requerida)",
- "engine_server": "🌐 Servidor externo (Home Assistant, API REST...)",
- "voice_label": "🗣️ Voz",
- "rate_label": "⚡ Velocidad",
- "pitch_label": "🎵 Tono",
- "url_label": "🌐 URL del endpoint",
- "method_label": "📡 Método HTTP",
- "auth_label": "🔐 Autenticación",
- "auth_bearer": "Bearer Token",
- "auth_custom": "Cabecera personalizada",
- "auth_none": "Ninguna",
- "token_label": "🔑 Bearer Token",
- "custom_header_name": "📋 Nombre de cabecera",
- "custom_header_value": "📋 Valor de cabecera",
- "content_type_label": "📄 Content-Type",
- "payload_key_label": "🗝️ Campo de texto en el payload",
- "payload_key_hint": "Nombre del campo JSON que contendrá el texto a leer (ej.: message, text).",
- "extra_fields_label": "➕ Campos adicionales (JSON)",
- "extra_fields_placeholder": "{\"entity_id\": \"media_player.salon\"}",
- "extra_fields_hint": "Campos adicionales a incluir en el payload, en formato JSON. Deja vacío si no es necesario.",
- "test_btn": "🔊 Enviar voz de prueba",
- "voices_loading": "Cargando voces…",
- "voice_not_supported": "Voz no compatible con este navegador",
- "voices_none": "No hay voces disponibles en este dispositivo",
- "voices_hint": "Las voces disponibles dependen del SO y el navegador. Pulsa ↺ si la lista no carga.",
- "url_missing": "⚠️ URL del endpoint faltante.",
- "test_sending": "⏳ Enviando…",
- "test_ok": "✅ Respuesta {code} — comprueba que el altavoz haya hablado.",
- "heard_question": "¿Has escuchado la voz?",
- "heard_yes": "Sí, la escuché",
- "heard_no": "No, no escuché nada",
- "test_ok_kiosk": "TTS funcionando.",
- "test_fail_steps": "Comprueba: 1) el volumen del multimedia no es 0; 2) Google Text-to-Speech está instalado y actualizado; 3) el paquete de voz español está descargado en la configuración TTS de Android."
- },
- "language": {
- "title": "🌐 Idioma",
- "hint": "Selecciona el idioma de la interfaz.",
- "label": "🌐 Idioma",
- "restart_notice": "La página se recargará para aplicar el nuevo idioma."
- },
- "screensaver": {
- "label": "Activar protector de pantalla",
- "card_title": "🌙 Protector de pantalla",
- "card_hint": "Muestra un reloj con información útil después de 5 minutos de inactividad. Desactivado por defecto.",
- "timeout_1": "1 minuto",
- "timeout_2": "2 minutos",
- "timeout_5": "5 minutos",
- "timeout_10": "10 minutos",
- "timeout_15": "15 minutos",
- "timeout_30": "30 minutos",
- "timeout_60": "1 hora",
- "start_after": "⏱️ Iniciar tras"
- },
- "scale": {
- "title": "⚖️ Báscula inteligente",
- "hint": "Conecta una báscula Bluetooth mediante la pasarela Android para leer el peso automáticamente.",
- "tab": "Báscula inteligente",
- "enabled": "✅ Activar báscula inteligente",
- "url_label": "🌐 URL de la pasarela WebSocket",
- "url_placeholder": "ws://192.168.1.x:8765",
- "url_hint": "URL mostrada por la app Android (misma red Wi-Fi). Ej.:",
- "test_btn": "🔗 Probar conexión",
- "download_btn": "📥 Descargar pasarela Android (APK)",
- "download_hint": "App Android que conecta tu báscula BLE con EverShelf.",
- "download_sub": "Fuente: evershelf-scale-gateway/ en la raíz del proyecto",
- "live_weight": "peso en tiempo real",
- "auto_reconnect": "🔁 Reconexión: automática",
- "kiosk_title": "📡 Báscula BLE integrada en el kiosco",
- "kiosk_hint": "La báscula está gestionada directamente por la pasarela BLE interna del kiosco. Para vincular un nuevo dispositivo, usa el asistente de configuración.",
- "kiosk_reconfigure": "🔄 Reconfigurar báscula BLE",
- "ble_protocols": "
🔌 Protocolos BLE soportados:
Bluetooth SIG Weight Scale (0x181D)
Bluetooth SIG Body Composition (0x181B) — peso, grasa, IMC
Xiaomi Mi Body Composition Scale 2
Genérico — heurística automática para 100+ modelos
"
- },
- "kiosk": {
- "hint": "Convierte una tableta Android en un panel EverShelf permanente con pasarela BLE integrada.",
- "download_btn": "📥 Descargar EverShelf Kiosk (APK)",
- "download_sub": "Modo kiosco a pantalla completa + pasarela de báscula integrada. Fuente: evershelf-kiosk/",
- "native_title": "Configuración del kiosco",
- "native_hint": "URL del servidor, báscula BLE, protector de pantalla y asistente de configuración.",
- "native_btn": "Abrir configuración del kiosco",
- "native_tap_hint": "Toca el botón de engranaje en la parte superior derecha",
- "native_update_hint": "Actualiza la app del kiosco para usar esta función",
- "update_title": "Actualización del kiosco",
- "check_updates_btn": "🔍 Buscar actualizaciones",
- "needs_update": "⚠️ El kiosco instalado no admite esta función. Actualiza la app del kiosco para activarla."
- },
- "saved": "✅ ¡Configuración guardada!",
- "saved_local": "✅ Configuración guardada localmente",
- "saved_local_error": "⚠️ Guardado localmente, error del servidor: {error}",
- "theme": {
- "title": "🌙 Apariencia",
- "hint": "Elige el tema de la interfaz.",
- "label": "🌙 Tema",
- "off": "☀️ Claro",
- "on": "🌙 Oscuro",
- "auto": "🔄 Automático (sistema)"
- },
- "zerowaste": {
- "card_title": "♻️ Consejos sin desperdicios",
- "card_hint": "Durante la cocción, muestra consejos sobre cómo reutilizar los restos generados en cada paso (peladuras, agua de cocción, etc.). Desactivado por defecto.",
- "label": "Mostrar consejos durante la cocción"
- },
- "backup": {
- "tab": "Copia de seguridad",
- "local_title": "Copia local",
- "local_hint": "Instantánea diaria de la base de datos. Configura cuántos días de copias de seguridad conservar.",
- "enabled": "Activar copia de seguridad diaria automática",
- "retention_days": "Retención (días)",
- "retention_info": "Las copias se conservan durante",
- "backup_now": "Hacer copia ahora",
- "backing_up": "Haciendo copia…",
- "backed_up": "Copia completada",
- "backup_error": "Error en la copia",
- "last_backup": "Última copia",
- "no_backup_yet": "Aún no se ha creado ninguna copia",
- "list_empty": "No hay copias disponibles",
- "restore_btn": "Restaurar",
- "restore_confirm": "Restaurar la copia",
- "delete_btn": "Eliminar",
- "delete_confirm": "Eliminar la copia",
- "gdrive_title": "Google Drive",
- "gdrive_hint": "Copias de seguridad automáticas en Google Drive via OAuth 2.0. No se requieren bibliotecas externas.",
- "gdrive_enabled": "Activar copia en Google Drive",
- "gdrive_folder_id": "ID de carpeta de Drive",
- "gdrive_folder_id_hint": "Copia el ID desde la URL de la carpeta de Drive: …/folders/ID",
- "gdrive_retention_days": "Retención en Drive (días, 0=mantener todo)",
- "gdrive_test": "Probar conexión",
- "gdrive_ok": "Conexión exitosa!",
- "gdrive_error": "Conexión fallida",
- "gdrive_push_now": "Subir a Drive ahora",
- "gdrive_pushing": "Subiendo…",
- "gdrive_pushed": "Subido a Drive",
- "gdrive_wizard_hint": "Opcional: copia de seguridad diaria automática en Google Drive via OAuth 2.0.",
- "gdrive_skip": "Omitir — configurar después en Ajustes",
- "gdrive_client_id": "Client ID",
- "gdrive_client_secret": "Client Secret",
- "gdrive_redirect_uri_hint": "Agrega http://localhost como URI de redireccionamiento autorizado en Google Cloud Console. Funciona en cualquier servidor, incluso sin dominio público.",
- "gdrive_code_title": "Pegar la URL o el código de autorización",
- "gdrive_code_hint": "Tras autorizar, el navegador abrirá http://localhost y puede mostrar un error de conexión — es normal. Copia la URL de la barra de direcciones (ej. http://localhost/?code=4%2F0A...) y pégala aquí.",
- "gdrive_code_submit": "Confirmar",
- "gdrive_code_empty": "Pega primero la URL o el código de autorización",
- "gdrive_redirect_uri_label": "URI de redirección (agregar en Google Cloud Console):",
- "gdrive_oauth_authorize": "Autorizar con Google",
- "gdrive_oauth_authorized": "Autorizado",
- "gdrive_oauth_not_authorized": "Aún no autorizado",
- "gdrive_oauth_window_opened": "Ventana abierta — autoriza y regresa aquí",
- "gdrive_oauth_how_to": "Cómo configurar OAuth 2.0 (paso a paso)",
- "gdrive_oauth_steps": "
Habilita la API de Google Drive: API y servicios → Habilitar API → Google Drive API
Ve a API y servicios → Credenciales → Crear credenciales → ID de cliente OAuth
Tipo de aplicación: Aplicación web; agrega la URL mostrada abajo como URI de redirección autorizado
Copia el Client ID y el Client Secret en los campos de arriba y guarda
Haz clic en Autorizar con Google: inicia sesión en tu cuenta de Google y concede acceso
La ventana se cierra automáticamente al finalizar y las copias de seguridad están listas
"
- },
- "shopping": {
- "tab": "Lista de la compra",
- "title": "Lista de la compra",
- "hint": "Configura la lista de la compra integrada o conecta Bring!.",
- "enable_label": "Activar lista de la compra",
- "mode_label": "Proveedor",
- "mode_internal": "Integrado (sin Bring!)",
- "mode_bring": "Bring! (app externa)",
- "bring_section_title": "Configuración de Bring!",
- "ai_section_title": "Asistencia IA",
- "smart_suggestions_label": "Sugerencias IA",
- "forecast_label": "Previsión de productos por agotar",
- "auto_add_label": "Añadir automáticamente cuando",
- "auto_add_suffix": "restante en stock (0 = solo cuando se agota)"
- },
- "ha": {
- "tab": "Home Assistant",
- "title": "Home Assistant",
- "hint": "Conecta EverShelf a Home Assistant para automatizaciones, notificaciones push y sensores REST.",
- "enabled": "Activar integración con Home Assistant",
- "connection_title": "Conexión",
- "url_label": "URL de Home Assistant",
- "url_placeholder": "http://192.168.1.50:8123",
- "url_hint": "URL base de tu instancia de Home Assistant.",
- "token_label": "Token de acceso de larga duración",
- "token_hint": "Genera desde Perfil HA → Seguridad → Tokens de acceso de larga duración.",
- "token_placeholder": "eyJhbGci...",
- "token_saved": "Token guardado (oculto por seguridad)",
- "test_btn": "Probar conexión",
- "test_ok": "Conectado a {version}",
- "test_fail": "Conexión fallida: {error}",
- "test_bad_token": "HA accesible pero el token no es válido",
- "testing": "Probando…",
- "error_no_url": "Por favor, introduce primero la URL de Home Assistant.",
- "tts_title": "TTS en altavoz inteligente",
- "tts_hint": "Lee los pasos de la receta en un reproductor de medios de Home Assistant.",
- "tts_entity_label": "Entity ID del reproductor multimedia",
- "tts_entity_placeholder": "media_player.salon",
- "tts_entity_hint": "ID de entidad del reproductor multimedia HA. Encuéntralo en HA: Herramientas para desarrolladores → Estados.",
- "tts_platform_label": "Plataforma TTS",
- "tts_platform_speak": "tts.speak (recomendado)",
- "tts_platform_notify": "notify.* (servicio de notificaciones)",
- "tts_apply_btn": "Aplicar preset HA a la pestaña TTS",
- "tts_apply_hint": "Pre-rellena la pestaña TTS con la URL y el token de Home Assistant.",
- "tts_preset_applied": "Preset HA aplicado a la pestaña TTS.",
- "webhook_title": "Automatizaciones Webhook",
- "webhook_hint": "Envía datos a Home Assistant cuando ocurren eventos en la despensa.",
- "webhook_id_label": "ID de Webhook",
- "webhook_id_placeholder": "evershelf_webhook_abc123",
- "webhook_id_hint": "ID del webhook creado en HA. Copia desde: HA → Ajustes → Automatizaciones → Crear → Disparador Webhook.",
- "webhook_events_label": "Notificar en estos eventos",
- "event_expiry": "Productos próximos a caducar (diario)",
- "event_shopping": "Artículo añadido a la lista de compras",
- "event_stock": "Nivel de stock actualizado",
- "expiry_days_label": "Antelación de caducidad (días)",
- "expiry_days_hint": "Enviar alerta de caducidad N días antes de la fecha.",
- "webhook_help": "En HA: Ajustes → Automatizaciones → Crear automatización → Disparador: Webhook → copia el ID generado.",
- "notify_title": "Notificaciones push",
- "notify_hint": "Envía notificaciones push a tu teléfono mediante un servicio notify de Home Assistant.",
- "notify_service_label": "Servicio notify",
- "notify_service_placeholder": "notify.mobile_app_mi_telefono",
- "notify_service_hint": "Nombre del servicio notify de HA. Déjalo vacío para desactivar.",
- "sensor_title": "Sensores REST",
- "sensor_hint": "Añade a configuration.yaml para crear sensores de EverShelf en Home Assistant.",
- "sensor_copy_btn": "Copiar YAML",
- "sensor_copied": "¡YAML copiado al portapapeles!",
- "save_btn": "Guardar ajustes HA",
- "ha_hint": "Si usas Home Assistant, utiliza la pestaña Home Assistant para configurar TTS, webhooks y sensores."
- }
- },
- "expiry": {
- "today": "HOY",
- "tomorrow": "Mañana",
- "days": "{days} días",
- "expired_days": "hace {days}d",
- "expired_yesterday": "Ayer",
- "expired_today": "Hoy",
- "badge_today": "⚠️ ¡Caduca hoy!",
- "badge_tomorrow": "⏰ Mañana",
- "badge_tomorrow_long": "⏰ Caduca mañana",
- "badge_days": "⏰ {n} días",
- "badge_expired_ago": "⚠️ Caducado hace {n}d",
- "badge_expired": "⛔ ¡Caducado!",
- "badge_stable": "✅ Estable",
- "badge_expiring_short": "⏰ Cad. en {n}d",
- "badge_ok_still": "✅ Aún {n}d",
- "badge_expires_red": "🔴 Cad. en {n}d",
- "badge_expires_yellow": "🟡 Cad. en {n}d",
- "badge_expired_bare": "⚠️ Caducado",
- "badge_expires_warn": "⚠️ Cad. en {n}d",
- "badge_days_left": "⏳ ~{n}d restantes",
- "days_approx": "~{n} días",
- "weeks_approx": "~{n} semanas",
- "months_approx": "~{n} meses",
- "years_approx": "~{n} años",
- "expired_today_long": "Caducado hoy",
- "expired_ago_long": "Caducado hace {n} días",
- "expired_suffix": "— ¡Caducado!",
- "expired_suffix_ok": "— Caducado (aún ok)",
- "expired_suffix_warning": "— Caducado (comprobar primero)",
- "opened_ago_long": "Abierto hace {n} días",
- "opened_today_long": "Abierto hoy",
- "opened_suffix": "— ¡Abierto demasiado tiempo!",
- "opened_suffix_ok": "— Abierto (aún ok)",
- "opened_suffix_warning": "— Abierto (comprobar primero)",
- "days_compact": "{n}d",
- "badge_check_soon": "Comprobar pronto"
- },
- "status": {
- "ok": "OK",
- "check": "Comprobar",
- "discard": "Desechar",
- "tip_freezer_ok": "En congelador: aún seguro (~{n}d de margen)",
- "tip_freezer_check": "En el congelador mucho tiempo, puede haber perdido calidad. Consumir pronto",
- "tip_freezer_danger": "En el congelador demasiado tiempo, riesgo de quemadura por congelación y degradación",
- "tip_highRisk_check": "Caducado recientemente, comprueba el olor y el aspecto antes de consumir",
- "tip_highRisk_danger": "Producto perecedero caducado: desechar por seguridad",
- "tip_medRisk_check1": "Comprueba el aspecto y el olor antes de consumir",
- "tip_medRisk_check2": "Caducado hace tiempo, comprueba cuidadosamente antes de usar",
- "tip_medRisk_danger": "Demasiado tiempo desde la caducidad, mejor desechar",
- "tip_lowRisk_ok": "Producto de larga duración, aún seguro para consumir",
- "tip_lowRisk_check": "Caducado hace más de un mes, comprueba la integridad del envase",
- "tip_lowRisk_danger": "Caducado hace demasiado tiempo, mejor no arriesgarse"
- },
- "toast": {
- "product_saved": "¡Producto guardado!",
- "product_created": "¡Producto creado!",
- "product_updated": "✅ ¡Producto actualizado!",
- "product_removed": "Producto eliminado",
- "updated": "¡Actualizado!",
- "quantity_confirmed": "✓ Cantidad confirmada",
- "added_to_inventory": "✅ ¡{name} añadido!",
- "removed_from_list": "✅ ¡{name} eliminado de la lista!",
- "removed_from_list_short": "Eliminado de la lista",
- "added_to_shopping": "🛒 ¡Añadido a la lista de la compra!",
- "removed_from_shopping": "🛒 Eliminado de la lista de la compra",
- "finished_to_bring": "🛒 Producto terminado → ¡añadido a Bring!",
- "thrown_away": "🗑️ ¡{name} tirado!",
- "thrown_away_partial": "🗑️ {qty} {unit} de {name} tirado(s)",
- "finished_all": "📤 ¡{name} terminado!",
- "product_finished_confirmed": "✅ Eliminado — añádelo de nuevo cuando reabastezcas",
- "appliance_added": "Electrodoméstico añadido",
- "item_added": "{name} añadido"
- },
- "antiwaste": {
- "title": "🌱 Informe anti-desperdicio",
- "grade_label": "Nota",
- "you": "Tú",
- "avg_label": "Media",
- "better": "🎉 ¡Desperdicias un {diff}% menos que la media {country}!",
- "worse": "⚠️ Desperdicias más que la media {country}. ¡Hay margen de mejora!",
- "on_par": "→ Estás en la media {country}. ¡Puedes hacerlo mejor!",
- "saved_money": "~{amount}/mes ahorrado",
- "saved_meals": "~{n} comidas salvadas",
- "saved_co2": "{n} kg CO₂ evitados",
- "trend_title": "Tendencia (últimos 3 meses)",
- "months_ago_2": "-60 días",
- "months_ago_1": "-30 días",
- "this_month": "Ahora",
- "country_it": "Media italiana",
- "country_de": "Media alemana",
- "country_en": "Media estadounidense",
- "source": "Fuentes: REDUCE, Eurostat, USDA 2021",
- "live_on": "Datos en vivo",
- "live_off": "Sin conexión",
- "meals": "comidas",
- "annual_info": "📅 Tú ~{you} kg/año · media ~{avg} kg/año",
- "badge_rate": "tasa de pérdida",
- "badge_saved_money": "ahorrado vs media",
- "badge_wasted": "artículos perdidos",
- "badge_better": "menos que la media"
- },
- "error": {
- "generic": "Error",
- "network": "Error de red",
- "no_api_key": "Configura la clave API en los ajustes",
- "loading": "Error al cargar el producto",
- "not_found": "Producto no encontrado",
- "not_found_manual": "Producto no encontrado. Introdúcelo manualmente.",
- "search": "Error de búsqueda. Inténtalo de nuevo.",
- "search_short": "Error de búsqueda",
- "save": "Error al guardar",
- "connection": "Error de conexión",
- "camera": "No se puede acceder a la cámara",
- "bring_add": "Error al añadir a Bring!",
- "bring_connection": "Error de conexión con Bring!",
- "identification": "Error de identificación",
- "ai_quota": "Cuota de IA agotada. Inténtalo de nuevo en unos minutos.",
- "barcode_empty": "Introduce un código de barras",
- "barcode_format": "El código de barras solo puede contener números (4-14 dígitos)",
- "min_chars": "Escribe al menos 2 caracteres",
- "not_in_inventory": "Producto no en inventario",
- "appliance_exists": "El electrodoméstico ya existe",
- "already_exists": "Ya existe",
- "network_retry": "Error de conexión. Inténtalo de nuevo.",
- "select_items": "Selecciona al menos un producto",
- "server_offline": "Conexión con el servidor perdida",
- "server_restored": "Conexión con el servidor restaurada",
- "server_retry": "Reintentar",
- "unknown": "Error desconocido",
- "prefix": "Error",
- "no_inventory_entry": "No se encontró ninguna entrada de inventario",
- "offline_title": "Sin conexión",
- "offline_subtitle": "La app no puede conectar con el servidor. Verifica tu conexión Wi-Fi.",
- "offline_checking": "Verificando conexión…",
- "offline_restored": "¡Conexión restaurada!",
- "offline_continue": "Continuar en modo sin conexión",
- "offline_reading_cache": "Leyendo desde caché local",
- "offline_ops_pending": "{n} operaciones pendientes",
- "offline_synced": "{n} operaciones sincronizadas",
- "offline_ai_disabled": "No disponible sin conexión",
- "offline_cache_ready": "Offline — {n} productos en caché"
- },
- "confirm_placeholder_search": null,
- "confirm": {
- "remove_item": "¿Realmente quieres eliminar este producto del inventario?",
- "kiosk_exit": "¿Salir del modo kiosco?",
- "cancel": "Cancelar",
- "proceed": "Confirmar",
- "discard_one": "Tirar 1 unidad"
- },
- "location": {
- "dispensa": "Despensa",
- "frigo": "Nevera",
- "freezer": "Congelador"
- },
- "edit": {
- "title": "Editar {name}",
- "unknown_hint": "Introduce el nombre del producto y la información",
- "label_name": "🏷️ Nombre del producto",
- "choose_location_title": "¿Qué ubicación?",
- "choose_location_hint": "Elige la ubicación a editar:",
- "confirm_large_qty": "Estás configurando la cantidad a {qty} {unit}. Esto parece inusualmente alto. ¿Confirmar?"
- },
- "screensaver": {
- "recipe_btn": "Recetas",
- "scan_btn": "Escanear producto"
- },
- "days": {
- "mon": "Lunes",
- "tue": "Martes",
- "wed": "Miércoles",
- "thu": "Jueves",
- "fri": "Viernes",
- "sat": "Sábado",
- "sun": "Domingo",
- "mon_short": "Lun",
- "tue_short": "Mar",
- "wed_short": "Mié",
- "thu_short": "Jue",
- "fri_short": "Vie",
- "sat_short": "Sáb",
- "sun_short": "Dom"
- },
- "meal_types": {
- "lunch": "Almuerzo",
- "dinner": "Cena",
- "colazione": "Desayuno",
- "merenda": "Merienda",
- "dolce": "Postre",
- "succo": "Zumo de fruta",
- "pranzo": "Almuerzo",
- "cena": "Cena"
- },
- "scale": {
- "status_connected": "Báscula conectada",
- "status_searching": "Pasarela conectada, esperando báscula…",
- "status_disconnected": "Pasarela de báscula inaccesible",
- "status_error": "Error de conexión con la pasarela",
- "not_connected": "Pasarela de báscula no conectada",
- "read_btn": "⚖️ Leer desde báscula",
- "reading_title": "Lectura de báscula",
- "place_on_scale": "Coloca el producto en la báscula…",
- "waiting_stable": "El peso se capturará automáticamente cuando la lectura sea estable.",
- "no_url": "Introduce la URL de la pasarela",
- "testing": "⏳ Probando conexión…",
- "connected_ok": "¡Conexión con la pasarela exitosa!",
- "timeout": "Tiempo de espera agotado: sin respuesta de la pasarela",
- "error_connect": "No se puede conectar a la pasarela",
- "tab": "Báscula inteligente",
- "low_weight": "Peso < 10 g · introduce manualmente\n(la lectura automática requiere al menos 10 g)",
- "density_hint": "(densidad {density} g/ml)",
- "ml_hint": "(se convertirá a ml)",
- "weight_detected": "Peso detectado — espera 10s de estabilidad…",
- "weight_too_low": "Peso demasiado bajo — esperando…",
- "stable": "✓ Estable",
- "auto_confirm": "✅ {val} {unit} — confirmación automática en 5s (toca para cancelar)",
- "cancelled_replace": "Cancelado — vuelve a colocar el ingrediente en la báscula para continuar"
- },
- "prediction": {
- "expected_qty": "Esperado: {expected} {unit}",
- "actual_qty": "Actual: {actual} {unit}",
- "check_suggestion": "Comprueba o pesa la cantidad restante"
- },
- "date": {
- "today": "📅 Hoy",
- "yesterday": "📅 Ayer"
- },
- "scanner": {
- "title_barcode": "🔖 Escanear código de barras",
- "barcode_hint": "Encuadra el código de barras del producto",
- "barcode_manual_placeholder": "O introduce manualmente...",
- "barcode_use_btn": "✅ Usar este código",
- "ai_identifying": "🤖 Identificando producto...",
- "ai_analyzing": "🤖 Análisis IA en curso...",
- "product_label_hint": "Encuadra la etiqueta del producto",
- "expiry_label_hint": "Encuadra la fecha de caducidad impresa en el producto",
- "capture_btn": "📸 Capturar",
- "capture_photo_btn": "📸 Sacar foto",
- "retake_btn": "🔄 Repetir",
- "camera_error_hint": "Asegúrate de usar HTTPS y haber concedido los permisos de cámara. Puedes introducir el código de barras manualmente o usar la identificación IA.",
- "no_barcode": "Sin código de barras",
- "save_new_btn": "🆕 Ninguno de estos — guardar como nuevo"
- },
- "lowstock": {
- "title": "⚠️ ¡Stock bajo!",
- "message": "{name} se está agotando — solo quedan {qty}.",
- "question": "¿Quieres añadirlo a la lista de la compra?",
- "yes": "🛒 Sí, añadir a Bring!",
- "no": "No, por ahora estoy bien"
- },
- "move": {
- "title": "📦 ¿Mover el resto?",
- "question": "¿Quieres mover {thing} de {name} a otra ubicación?",
- "question_short": "¿Quieres mover {thing} a otra ubicación?",
- "thing_opened": "el paquete abierto",
- "thing_rest": "el resto",
- "stay_btn": "No, quedarse en {location}",
- "moved_toast": "📦 Paquete abierto movido a {location}",
- "vacuum_restore": "🫙 Restaurar al vacío",
- "vacuum_seal_rest": "🔒 Sellar el resto al vacío"
- },
- "nova": {
- "1": "Sin procesar",
- "2": "Ingrediente culinario",
- "3": "Procesado",
- "4": "Ultraprocesado"
- },
- "meal_plan_types": {
- "pasta": "Pasta",
- "riso": "Arroz",
- "carne": "Carne",
- "pesce": "Pescado",
- "legumi": "Legumbres",
- "uova": "Huevos",
- "formaggio": "Queso",
- "pizza": "Pizza",
- "affettati": "Fiambres",
- "verdure": "Verduras",
- "zuppa": "Sopa",
- "insalata": "Ensalada",
- "pane": "Pan/Bocadillo",
- "dolce": "Postre",
- "libero": "Libre"
- },
- "meal_sub": {
- "dolce_torta": "Tarta",
- "dolce_crema": "Crema / Pudín",
- "dolce_crumble": "Crumble / Tarta",
- "dolce_biscotti": "Galletas / Pastelería",
- "dolce_frutta": "Postre de fruta",
- "succo_dolce": "Dulce / Afrutado",
- "succo_energizzante": "Energizante",
- "succo_detox": "Detox / Verde",
- "succo_rinfrescante": "Refrescante",
- "succo_vitaminico": "Vitamínico / Cítricos"
- },
- "meal_plan": {
- "reset_success": "Plan semanal restablecido",
- "not_available": "no disponible en la despensa",
- "suggested_by": "sugerido por el plan semanal"
- },
- "nutrition": {
- "title": "🥗 Análisis alimentario",
- "score_excellent": "😄 Excelente",
- "score_good": "🙂 Bien",
- "score_improve": "😬 Mejorable",
- "label_health": "🌿 Salud",
- "label_variety": "🎨 Variedad",
- "label_fresh": "❄️ Fresco",
- "source": "Basado en {n} productos en tu despensa · EverShelf",
- "products_count": "productos",
- "today_title": "🥗 Tu despensa hoy",
- "products_n": "{n} productos",
- "macros_title": "Macronutrientes estimados",
- "macros_proteins": "Proteínas",
- "macros_carbs": "Carbohidratos",
- "macros_fat": "Grasas",
- "macros_fiber": "Fibra",
- "macros_source": "Estimación basada en {n} productos en despensa"
- },
- "facts": {
- "greeting_morning": "Buenos días",
- "greeting_afternoon": "Buenas tardes",
- "greeting_evening": "Buenas noches",
- "pantry_waiting": "¡{greeting}! Tu despensa te espera.",
- "expired_one": "Tienes 1 producto caducado en tu despensa. ¡Compruébalo!",
- "expired_many": "Tienes {n} productos caducados en tu despensa. ¡Compruébalos!",
- "expired_list": "Productos caducados: {names}",
- "expired_list_more": "y {n} más",
- "freezer_expired_ok": "{name} está caducado, pero al estar en el congelador puede estar bien. ¡Compruébalo!",
- "freezer_expired_old": "{name} en el congelador lleva demasiado tiempo caducado. Mejor tirarlo.",
- "fridge_expired_one": "¡Tienes 1 producto caducado en la nevera!",
- "fridge_expired_many": "¡Tienes {n} productos caducados en la nevera!",
- "expiring_today": "¡{name} caduca hoy! Úsalo enseguida.",
- "expiring_tomorrow": "{name} caduca mañana. ¡Planifica!",
- "expiring_days": "{name} caduca en {days} días.",
- "expiring_many": "Tienes {n} productos que caducan pronto.",
- "expiring_this_week": "¡{n} productos caducan esta semana. Planifica tus comidas en consecuencia!",
- "expiring_item_loc": "{name} ({loc}) caduca en {days} {dayslabel}.",
- "expiring_this_month": "{n} productos caducarán este mes.",
- "shopping_add": "Añadir a la lista: {names} 🛒",
- "shopping_more": "y {n} más",
- "shopping_empty": "¡Lista de la compra vacía. Todo en stock! ✅",
- "in_fridge": "En la nevera: {name}.",
- "in_freezer": "En el congelador: {name}. ¡No lo olvides!",
- "top_category": "La categoría principal es {icon} {cat} con {n} productos.",
- "cat_meat": "Tienes {n} productos cárnicos. 🥩",
- "cat_dairy": "Tienes {n} productos lácteos en casa. 🥛",
- "cat_veggies": "Tienes {n} tipos de verduras. ¡Genial para la salud! 🥬",
- "cat_fruit": "Tienes {n} tipos de fruta. 🍎",
- "cat_drinks": "Tienes {n} bebidas disponibles. 🥤",
- "cat_frozen": "Tienes {n} artículos congelados. ❄️",
- "cat_pasta": "Tienes {n} tipos de pasta. 🍝 ¿Y si hacemos una carbonara?",
- "cat_canned": "Tienes {n} conservas en la despensa. 🥫",
- "cat_snacks": "Tienes {n} snacks. ¡Resiste la tentación! 🍪",
- "cat_condiments": "Tienes {n} condimentos disponibles. 🧂",
- "item_random": "¿Sabías que tienes {name} en {loc}?",
- "item_qty": "{name}: tienes {qty}.",
- "no_expiry_count": "{n} productos no tienen fecha de caducidad.",
- "furthest_expiry": "El producto con la fecha de caducidad más lejana es {name}: {months} meses.",
- "high_qty": "¡Tienes un buen stock de {name}: {qty}!",
- "low_qty_item": "{name} se está agotando. ¿Añadirlo a tu lista de la compra?",
- "low_qty_count": "{n} productos están casi agotados.",
- "morning_bread": "¡Buenos días! Tienes pan para el desayuno. 🍞",
- "morning_milk": "¿Hay leche en la nevera para un café con leche? ☕🥛",
- "morning_fruit": "¡Buenos días! Algo de fruta fresca es un gran comienzo. 🍎",
- "noon_pasta": "Es hora de comer… ¿Y si preparamos un buen plato de pasta? 🍝",
- "noon_salad": "¿Una ensalada fresca para comer? ¡Tienes {n} verduras! 🥗",
- "evening_meat": "Para cenar podrías usar la carne que tienes. 🥩",
- "evening_fish": "¿Qué tal pescado para cenar? 🐟",
- "evening_expiring": "Tienes {n} productos que caducan esta semana — ¡úsalos esta noche!",
- "night_reminder": "¡Buenas noches! Recuerda usar mañana: {names}.",
- "weekly_balance": "Balance semanal: +{in} añadidos, −{out} consumidos.",
- "weekly_added": "Has añadido {n} productos esta semana.",
- "weekly_consumed": "Has consumido {n} productos esta semana. ¡Bien hecho!",
- "tip_freezer": "💡 Los productos congelados duran mucho más que la fecha de caducidad.",
- "tip_bread": "💡 El pan congelado conserva su frescura durante semanas.",
- "tip_fifo": "💡 Para evitar desperdicios, usa primero los productos más cercanos a la caducidad (FIFO).",
- "tip_meat": "💡 La carne en el congelador puede durar hasta 6 meses con seguridad.",
- "tip_no_refreeze": "💡 Nunca vuelvas a congelar un producto descongelado. ¡Cocínalo enseguida!",
- "tip_fridge": "💡 Una nevera ordenada te ahorra tiempo y dinero.",
- "tip_canned": "💡 Las conservas abiertas deben ir a la nevera y consumirse en pocos días.",
- "top_brand": "La marca más común en tu despensa es {brand} con {n} productos.",
- "combo_pasta": "Tienes pasta y condimentos: ¡listo para un primer plato! 🍝",
- "combo_sandwich": "Pan y carne: ¡un sándwich rápido siempre es buena idea! 🥪",
- "combo_balanced": "Verduras y carne: ¡tienes todo para una comida equilibrada! 🥗🥩",
- "pantry_empty": "¡La despensa está vacía! Es hora de ir al supermercado. 🛒",
- "pantry_empty_scan": "No hay productos registrados. ¡Escanea algo para empezar!",
- "location_distribution": "Distribución: {parts}",
- "day": "día",
- "days": "días"
- },
- "kiosk_session": {
- "first_item": "¡Primer artículo: {name}!",
- "items_two_four": "{n} artículos — arrancando 🚀",
- "items_five_nine": "{n} artículos — ¡buen ritmo! 💪",
- "items_ten_twenty": "{n} artículos — casi un récord 🏆",
- "items_twenty_plus": "{n} artículos — ¡compra épica! 🛒🔥",
- "duplicates_one": "1 duplicado (mismo artículo dos veces)",
- "duplicates_many": "{n} duplicados (cogido varias veces)",
- "top_category": "Categoría principal: {cat} ({count}×)",
- "items_fallback": "{n} artículo{plural} añadido{plural}"
- },
- "kiosk": {
- "check_btn": "🔍 Buscar actualizaciones",
- "checking": "⏳ Comprobando…",
- "error_check": "Error durante la comprobación de actualizaciones",
- "error_start_install": "Error al iniciar la instalación",
- "version_installed": "Instalado: {v}",
- "update_available": "⬆️ Nueva versión disponible: {latest} (instalada: {current})",
- "up_to_date": "✅ Estás actualizado — versión {v}",
- "too_old": "⚠️ El kiosco instalado es demasiado antiguo para la comprobación automática de actualizaciones. Pulsa el botón de abajo para descargar e instalar la nueva versión directamente.",
- "manual_install": "⚠️ Este kiosco no admite instalación automática. Procedimiento manual: 1. Sal del kiosco (botón ✕ arriba a la izquierda) 2. Desinstala la app EverShelf Kiosk 3. Descarga e instala el nuevo APK desde GitHub:",
- "starting_download": "⏳ Iniciando descarga…",
- "install_btn": "⬇️ Instalar actualización",
- "exit_title": "Salir del kiosco",
- "refresh_title": "Actualizar página"
- },
- "update": {
- "new_version": "Nueva versión",
- "btn": "Actualizar"
- },
+ "app": {
+ "name": "EverShelf",
+ "loading": "Cargando..."
+ },
+ "nav": {
+ "title": "EverShelf",
+ "home": "Inicio",
+ "inventory": "Despensa",
+ "recipes": "Recetas",
+ "shopping": "Compras",
+ "log": "Registro",
+ "settings": "Ajustes"
+ },
+ "btn": {
+ "back": "← Volver",
+ "save": "💾 Guardar",
+ "cancel": "✕ Cancelar",
+ "close": "Cerrar",
+ "add": "✅ Añadir",
+ "delete": "Eliminar",
+ "edit": "✏️ Editar",
+ "use": "Usar",
+ "edit_item": "Editar",
+ "search": "🔍 Buscar",
+ "go": "✅ Ir",
+ "toggle_password": "👁️ Mostrar/Ocultar",
+ "load_more": "Cargar más...",
+ "save_config": "💾 Guardar configuración",
+ "save_product": "💾 Guardar producto",
+ "restart": "↺ Reiniciar",
+ "reset_default": "↺ Restablecer valores por defecto",
+ "save_info": "💾 Guardar información",
+ "retry": "🔄 Reintentar",
+ "yes_short": "Sí",
+ "no_short": "No"
+ },
+ "form": {
+ "select_placeholder": "-- Seleccionar --"
+ },
+ "locations": {
+ "dispensa": "Despensa",
+ "frigo": "Nevera",
+ "freezer": "Congelador",
+ "altro": "Otro"
+ },
+ "categories": {
+ "latticini": "Lácteos",
+ "carne": "Carne",
+ "pesce": "Pescado",
+ "frutta": "Fruta",
+ "verdura": "Verduras",
+ "pasta": "Pasta y Arroz",
+ "pane": "Pan y Panadería",
+ "surgelati": "Congelados",
+ "bevande": "Bebidas",
+ "condimenti": "Condimentos",
+ "snack": "Snacks y Dulces",
+ "conserve": "Conservas",
+ "cereali": "Cereales y Legumbres",
+ "igiene": "Higiene",
+ "pulizia": "Limpieza",
+ "altro": "Otro",
+ "select": "-- Seleccionar --"
+ },
+ "units": {
+ "pz": "uds",
+ "conf": "paq",
+ "g": "g",
+ "ml": "ml",
+ "pieces": "Unidades",
+ "grams": "Gramos",
+ "box": "Paquete",
+ "boxes": "Paquetes",
+ "millilitres": "Mililitros",
+ "from": "de"
+ },
+ "shopping_sections": {
+ "frutta_verdura": "Frutas y Verduras",
+ "carne_pesce": "Carne y Pescado",
+ "latticini": "Lácteos y Frescos",
+ "pane_dolci": "Pan y Dulces",
+ "pasta": "Pasta y Cereales",
+ "conserve": "Conservas y Salsas",
+ "surgelati": "Congelados",
+ "bevande": "Bebidas",
+ "pulizia_igiene": "Limpieza e Higiene",
+ "altro": "Otro"
+ },
+ "dashboard": {
+ "expired_title": "🚫 Caducado",
+ "expiring_title": "⏰ Caduca pronto",
+ "stats_period": "📊 Últimos 30 días",
+ "opened_title": "📦 Productos abiertos",
+ "review_title": "🔍 Por revisar",
+ "review_hint": "Cantidades inusuales. Confirma si son correctas o modifícalas.",
+ "quick_recipe": "Receta rápida con productos que caducan",
+ "banner_review_title": "Cantidad anormal",
+ "banner_review_action_ok": "Es correcto",
+ "banner_review_action_finish": "🗑️ Todo terminado",
+ "banner_review_action_edit": "Corregir",
+ "banner_review_action_weigh": "Pesar",
+ "banner_review_dismiss": "Ignorar",
+ "banner_prediction_title": "Consumo por revisar",
+ "banner_prediction_hint": "La estimación de consumo se adapta a los datos recientes: confirma solo si la cantidad actual es correcta.",
+ "banner_prediction_action_confirm": "Confirmar {qty} {unit}",
+ "banner_prediction_action_weigh": "Pesar ahora",
+ "banner_prediction_action_edit": "Actualizar cantidad",
+ "banner_expired_title": "Producto caducado",
+ "banner_expired_today": "Caducado hoy",
+ "banner_expired_days": "Caducado hace {days} días",
+ "banner_expired_action_use": "Usar de todas formas",
+ "banner_expired_action_finished": "¡Ya lo terminé!",
+ "banner_expired_action_throw": "Lo tiré",
+ "banner_expired_action_edit": "Corregir fecha",
+ "banner_anomaly_action_edit": "Corregir inventario",
+ "banner_anomaly_action_dismiss": "La cantidad es correcta",
+ "banner_no_expiry_title": "Caducidad faltante: {name}",
+ "banner_no_expiry_detail": "Este producto no tiene fecha de caducidad. ¿Quieres añadir una o confirmar que no caduca?",
+ "banner_no_expiry_action_set": "Establecer fecha de caducidad",
+ "banner_no_expiry_action_dismiss": "No caduca ✓",
+ "banner_no_expiry_toast_dismissed": "Marcado como «sin fecha de caducidad»",
+ "banner_expiring_title": "Caduca pronto",
+ "banner_expiring_today": "¡Caduca hoy!",
+ "banner_expiring_tomorrow": "Caduca mañana",
+ "banner_expiring_days": "Caduca en {days} días",
+ "banner_expiring_action_use": "Usar ahora",
+ "banner_finished_title": "¿terminado?",
+ "banner_finished_detail": "Registré que {name} llegó a cero de stock. ¿Realmente se acabó o todavía tienes algo?",
+ "banner_finished_action_yes": "Sí, se acabó",
+ "banner_finished_action_no": "No, todavía tengo",
+ "banner_review_unusual_pkg_title": "Tamaño de paquete inusual",
+ "banner_review_unusual_pkg_detail": "Configuraste un paquete de {qty} {unit} — el tamaño parece muy grande. Comprueba si es correcto o edita.",
+ "banner_review_low_qty_title": "Cantidad muy baja",
+ "banner_review_low_qty_detail": "Solo tienes {qty} en stock — parece muy poco, puede ser un error de escritura. Confirma si es correcto.",
+ "banner_review_high_qty_title": "Cantidad inusualmente alta",
+ "banner_review_high_qty_detail": "Tienes {qty} en stock — la cifra parece muy alta. Confirma si es correcto o edita.",
+ "banner_prediction_rate_day": "Media ~{n} {unit}/día",
+ "banner_prediction_rate_week": "Media ~{n} {unit}/semana",
+ "banner_prediction_days_ago": "Hace {n} días reabasteciste",
+ "banner_prediction_more": "estimación anterior: {expected} {unit}{time}; cantidad actual: {actual} {unit}.",
+ "banner_prediction_less": "estimación: {expected} {unit}{time}; cantidad actual: {actual} {unit}. Si tu ritmo de uso cambió, la previsión se actualiza automáticamente.",
+ "banner_finished_zero": "El inventario muestra cero, pero los movimientos registrados sugieren que no debería estar vacío.",
+ "banner_finished_expected": "Según los registros deberías tener todavía {qty} {unit}.",
+ "banner_finished_check": "¿Puedes comprobarlo?",
+ "banner_anomaly_phantom_title": "tienes más stock del esperado",
+ "banner_anomaly_phantom_detail": "El inventario indica {inv_qty} {unit}, pero según los registros solo deberías tener {expected_qty} {unit}. ¿Añadiste stock sin registrarlo?",
+ "banner_anomaly_untracked_title": "stock no registrado como entrada",
+ "banner_anomaly_untracked_detail": "Tienes {inv_qty} {unit} en inventario, pero las salidas registradas superan las entradas — el stock inicial probablemente nunca se añadió como transacción «entrada». Puedes corregir la cantidad o registrar las entradas faltantes.",
+ "banner_anomaly_ghost_title": "tienes menos stock del esperado",
+ "banner_anomaly_ghost_detail": "Según las operaciones registradas deberías tener {expected_qty} {unit} de {name}, pero el inventario solo muestra {inv_qty} {unit}. ¿Tomaste stock sin registrarlo?",
+ "consumed": "Consumido: {n} ({pct}%)",
+ "wasted": "Desperdiciado: {n} ({pct}%)",
+ "more_opened": "y {n} más abiertos...",
+ "banner_expired_detail": "{when} · aún tienes {qty}.",
+ "banner_opened_detail": "{when} en {location} · aún tienes {qty}.",
+ "banner_explain_title": "Pedir explicación a Gemini",
+ "banner_explain_btn": "Explicar",
+ "banner_analyzing": "🤖 Analizando…"
+ },
+ "inventory": {
+ "title": "Despensa",
+ "filter_all": "Todo",
+ "search_placeholder": "🔍 Buscar producto...",
+ "recent_title": "🕐 Usados recientemente",
+ "popular_title": "⭐ Más usados",
+ "empty": "No hay productos aquí.\n¡Escanea un producto para añadirlo!",
+ "no_items_found": "No se encontraron artículos",
+ "qty_remainder_suffix": "restante",
+ "vacuum_badge": "🫙 Al vacío",
+ "opened_badge": "📭 Abierto",
+ "label_expiry": "📅 Caducidad",
+ "label_storage": "🫙 Almacenamiento",
+ "label_status": "📭 Estado",
+ "opened_since": "Abierto desde el {date}",
+ "label_position": "📍 Ubicación",
+ "label_quantity": "📦 Cantidad",
+ "label_added": "📅 Añadido",
+ "empty_text": "No hay productos aquí. ¡Escanea un producto para añadirlo!",
+ "empty_db": "No hay productos en la base de datos. ¡Escanea un producto para empezar!",
+ "qty_trace": "< 1"
+ },
+ "scan": {
+ "title": "Escáner",
+ "mode_shopping": "🛒 Modo compras",
+ "mode_shopping_end": "✅ Finalizar compras",
+ "spesa_btn": "🛒 Compras",
+ "zoom": "Zoom",
+ "tab_barcode": "Código de barras",
+ "tab_name": "Nombre",
+ "tab_ai": "IA",
+ "recents_label": "Recientes",
+ "torch_hint": "Linterna",
+ "torch_on": "Linterna encendida",
+ "torch_off": "Linterna apagada",
+ "torch_unavailable": "Linterna no disponible en este dispositivo",
+ "flip_hint": "Cambiar cámara",
+ "flip_front": "Cámara frontal",
+ "flip_back": "Cámara trasera",
+ "num_ocr_btn": "🔢 Leer números con IA",
+ "num_ocr_searching": "Buscando código de barras con IA...",
+ "num_ocr_found": "Código encontrado: {code}",
+ "num_ocr_not_found": "No se encontró código de barras en la imagen",
+ "barcode_placeholder": "Introduce el código de barras...",
+ "quick_name_divider": "o escribe el nombre",
+ "quick_name_placeholder": "Ej.: Manzanas, Calabacín, Pan...",
+ "manual_entry": "✏️ Entrada manual",
+ "ai_identify": "🤖 Identificar con IA",
+ "hint": "Escanea el código de barras, escribe el nombre del producto o usa la IA para identificarlo",
+ "debug_toggle": "🐛 Registro de depuración",
+ "barcode_acquired": "🔖 Código de barras escaneado: {code}",
+ "scan_barcode": "🔖 Escanear código de barras",
+ "create_named": "Crear {name}",
+ "new_without_barcode": "Nuevo producto sin código de barras",
+ "status_ready": "Apunta la cámara al código de barras",
+ "status_scanning": "Escaneando...",
+ "status_partial": "Detectado: {code} — verificando...",
+ "status_invalid": "Inválido: {code} — reintentando",
+ "status_confirmed": "Confirmado!",
+ "status_parallel": "Escaneo combinado activo...",
+ "ai_fallback_searching": "Identificación de IA en curso...",
+ "ai_fallback_found": "Producto identificado por IA",
+ "ai_fallback_not_found": "IA: producto no reconocido"
+ },
+ "action": {
+ "title": "¿Qué quieres hacer?",
+ "add_btn": "📥 AÑADIR",
+ "add_sub": "a la despensa/nevera",
+ "use_btn": "📤 USAR / CONSUMIR",
+ "use_sub": "de la despensa/nevera",
+ "have_title": "📦 ¡Ya en stock!",
+ "add_more_sub": "añadir más",
+ "use_qty_sub": "cuánto usaste",
+ "throw_btn": "🗑️ DESECHAR",
+ "throw_sub": "tirar",
+ "edit_sub": "caducidad, ubicación…",
+ "create_recipe_btn": "Receta"
+ },
+ "add": {
+ "title": "Añadir a la despensa",
+ "location_label": "📍 ¿Dónde lo guardas?",
+ "quantity_label": "📦 Cantidad",
+ "conf_size_label": "📦 Cada paquete contiene:",
+ "conf_size_placeholder": "ej. 300",
+ "vacuum_label": "🫙 Al vacío",
+ "vacuum_hint": "La fecha de caducidad se ampliará automáticamente",
+ "submit": "✅ Añadir",
+ "purchase_type_label": "🛒 Este producto es...",
+ "new_btn": "🆕 Recién comprado",
+ "existing_btn": "📦 Ya lo tenía",
+ "remaining_label": "📦 Cantidad restante",
+ "remaining_hint": "¿Aproximadamente cuánto queda?",
+ "remaining_full": "🟢 Lleno",
+ "remaining_half": "🟠 Por la mitad",
+ "estimated_expiry": "Caducidad estimada:",
+ "suffix_freezer": "(congelador)",
+ "suffix_vacuum": "(al vacío)",
+ "hint_modify": "📝 Puedes cambiar la fecha o escanearla con la cámara",
+ "scan_expiry_title": "📷 Escanear fecha de caducidad",
+ "product_added": "✅ ¡{name} añadido!{qty}",
+ "suffix_freezer_vacuum": "(congelador + al vacío)",
+ "history_badge_tip": "Media de {n} entradas anteriores",
+ "vacuum_question": "¿Al vacío?",
+ "vacuum_saved": "🔒 ¡Al vacío!"
+ },
+ "use": {
+ "title": "Usar / Consumir",
+ "location_label": "📍 ¿De dónde?",
+ "quantity_label": "¿Cuánto usaste?",
+ "change": "cambiar",
+ "partial_hint": "O especifica la cantidad usada:",
+ "partial_piece_hint": "¿Usaste solo una parte?",
+ "piece": "unidad",
+ "one_whole": "1 entero",
+ "use_all": "🗑️ Todo usado / Terminado",
+ "submit": "📤 Usar esta cantidad",
+ "available": "📦 Disponible:",
+ "opened_badge": "ABIERTO",
+ "not_in_inventory": "⚠️ Producto no en inventario.",
+ "expiry_warning": "⚠️ ¡Usa primero el{loc} que caduca el {date} — {when}!",
+ "expiry_warning_opened": "⚠️ El{loc} lleva abierto {when} — ¡úsalo primero!",
+ "throw_title": "🗑️ Desechar producto",
+ "throw_all": "🗑️ Desechar TODO ({qty})",
+ "throw_qty_label": "¿Cuánto desechar?",
+ "throw_qty_hint": "o introduce una cantidad:",
+ "throw_partial_btn": "🗑️ Desechar esta cantidad",
+ "when_expired": "caducado hace {n} días",
+ "when_today": "caduca hoy",
+ "when_tomorrow": "caduca mañana",
+ "when_days": "caduca en {n} días",
+ "toast_used": "📤 {qty} de {name} usado",
+ "toast_bring": "🛒 Producto terminado → añadido a Bring!",
+ "toast_opened_finished": "🔓 ¡Paquete abierto de {name} terminado!",
+ "disambiguation_hint": "¿Qué quieres decir con «todo terminado»?",
+ "disambiguation_all": "🗑️ Terminar TODO ({qty})",
+ "error_exceeds_stock": "⚠️ ¡No puedes usar más de lo que tienes disponible!",
+ "use_all_confirm_title": "✅ Terminar todo",
+ "use_all_confirm_msg": "Confirma que has terminado el producto:",
+ "use_all_confirm_btn": "✅ Sí, terminado",
+ "throw_all_confirm_title": "🗑️ Desechar todo",
+ "throw_all_confirm_msg": "¿Realmente quieres tirar todo el producto?",
+ "throw_all_confirm_btn": "🗑️ Sí, desechar"
+ },
+ "product": {
+ "title_new": "Nuevo producto",
+ "title_edit": "Editar producto",
+ "ai_fill": "📷 Sacar foto e identificar con IA",
+ "ai_fill_hint": "La IA rellenará automáticamente los campos del producto",
+ "name_label": "🏷️ Nombre del producto *",
+ "name_placeholder": "Ej.: Leche entera, Pasta penne...",
+ "brand_label": "🏢 Marca",
+ "brand_placeholder": "Ej.: Barilla, Danone, Heinz...",
+ "category_label": "📂 Categoría",
+ "unit_label": "📏 Unidad de medida",
+ "default_qty_label": "🔢 Cantidad por defecto",
+ "conf_size_label": "📦 Cada paquete contiene:",
+ "conf_size_placeholder": "ej. 300",
+ "notes_label": "📝 Notas",
+ "notes_placeholder": "Ej.: sin lactosa, ecológico, guardar en nevera tras abrir...",
+ "barcode_label": "🔖 Código de barras",
+ "barcode_placeholder": "Código de barras (si disponible)",
+ "barcode_hint": "⚠️ ¡Añade el código de barras para la próxima vez solo tendrás que escanear!",
+ "submit": "💾 Guardar producto",
+ "name_required": "Introduce el nombre del producto",
+ "conf_size_required": "Especifica el contenido del paquete",
+ "expiry_estimated": "Caducidad estimada:",
+ "scan_expiry": "Escanear fecha de caducidad",
+ "expiry_hint": "📝 Puedes editar la fecha o escanearla con la cámara",
+ "add_batch": "📦 + Lote con fecha diferente",
+ "package_info": "📦 Paquete: {info}",
+ "edit_catalog": "⚙️ Editar información del producto (nombre, marca, categoría…)",
+ "not_recognized": "⚠️ Producto no reconocido",
+ "edit_info": "✏️ Editar información",
+ "modify_details": "EDITAR\ncaducidad, ubicación…",
+ "already_in_pantry": "📋 Ya en la despensa",
+ "no_barcode": "Sin código de barras",
+ "unknown_product": "Producto no reconocido",
+ "edit_name_brand": "Editar nombre/marca",
+ "weight_label": "Peso",
+ "origin_label": "Origen",
+ "labels_label": "Etiquetas",
+ "select_variant": "Selecciona la variante exacta o usa los datos de IA:"
+ },
+ "products": {
+ "title": "📦 Todos los productos",
+ "search_placeholder": "🔍 Buscar producto...",
+ "empty": "No hay productos en la base de datos.\n¡Escanea un producto para empezar!",
+ "no_category": "No hay productos en esta categoría"
+ },
+ "recipes": {
+ "title": "🍳 Recetas",
+ "generate": "✨ Generar nueva receta",
+ "archive_empty": "No hay recetas guardadas. ¡Genera tu primera receta!",
+ "dialog_title": "🍳 Receta",
+ "dialog_desc": "Generaré una receta saludable usando ingredientes de la despensa, priorizando los productos que caducan.",
+ "meal_label": "🕐 ¿Qué comida?",
+ "persons_label": "👥 ¿Para cuántas personas?",
+ "meal_type_label": "🎯 Tipo de comida",
+ "opt_fast": "⚡ Comida rápida",
+ "opt_light": "🥗 Apetito ligero",
+ "opt_expiry": "⏰ Priorizar productos que caducan",
+ "opt_healthy": "💚 Extra saludable",
+ "opt_opened": "📦 Priorizar productos abiertos",
+ "opt_zero_waste": "♻️ Cero desperdicio",
+ "generate_btn": "✨ Generar receta",
+ "loading_msg": "Preparando tu receta...",
+ "start_cooking": "👨🍳 Modo cocina",
+ "regenerate": "🔄 Generar otra",
+ "regen_choice_title": "¿Qué quieres hacer con esta receta?",
+ "regen_replace": "🔄 Generar otra (descartar esta)",
+ "regen_save_new": "💾 Guardar en el archivo y generar una nueva",
+ "close_btn": "✅ Cerrar",
+ "ingredients_title": "🧾 Ingredientes",
+ "tools_title": "Equipo necesario",
+ "steps_title": "👨🍳 Pasos",
+ "no_steps": "No hay pasos disponibles",
+ "generate_error": "Error de generación",
+ "persons_short": "com.",
+ "use_ingredient_title": "Usar ingrediente",
+ "recipe_qty_label": "Receta",
+ "from_where_label": "¿De dónde?",
+ "amount_label": "Cuánto",
+ "use_amount_btn": "Usar esta cantidad",
+ "use_all_btn": "Usar TODO / Terminado",
+ "packs_label": "Paquetes",
+ "quantity_in_total": "Cantidad en {unit} (total: {total})",
+ "packs_of_have": "Paquetes de {size} (tienes {count} paquetes)",
+ "scale_wait_stable": "Espera 10s de peso estable para el relleno automático…",
+ "ingredient_scaled_toast": "📦 ¡Ingrediente deducido de la despensa!",
+ "finished_added_bring_toast": "🛒 Producto terminado → ¡añadido a Bring!",
+ "load_error": "Error de carga",
+ "favorite": "Añadir a favoritos",
+ "unfavorite": "Quitar de favoritos",
+ "adjust_persons": "Personas"
+ },
+ "shopping": {
+ "title": "🛒 Lista de la compra",
+ "bring_loading": "Conectando a Bring!...",
+ "bring_not_configured": "Bring! no está configurado. Añade tu email y contraseña en los ajustes.",
+ "tab_to_buy": "🛍️ Por comprar",
+ "tab_forecast": "🧠 Previsión",
+ "total_label": "💰 Total estimado",
+ "section_to_buy": "🛍️ Por comprar",
+ "suggestions_title": "💡 Sugerencias IA",
+ "suggestions_add": "✅ Añadir selección a Bring!",
+ "search_prices": "🔍 Buscar todos los precios",
+ "suggest_btn": "Sugerir qué comprar",
+ "smart_title": "🧠 Predicciones inteligentes",
+ "smart_empty": "No hay predicciones disponibles. Añade productos a tu despensa para recibir predicciones inteligentes.",
+ "smart_filter_all": "Todo",
+ "smart_filter_critical": "🔴 Urgente",
+ "smart_filter_high": "🟠 Pronto",
+ "smart_filter_medium": "🟡 Planificar",
+ "smart_filter_low": "🟢 Previsión",
+ "smart_add": "🛒 Añadir selección a Bring!",
+ "empty": "¡Lista de la compra vacía!\nUsa el botón de abajo para generar sugerencias.",
+ "already_in_list": "🛒 \"{name}\" ya está en la lista de la compra",
+ "already_in_list_short": "ℹ️ Ya en la lista de la compra",
+ "add_prompt": "¿Quieres añadirlo a la lista de la compra?",
+ "smart_already": "📊 Las predicciones de compra ya predicen {name}",
+ "all_searched": "Todos los productos ya han sido buscados. Usa 🔄 para buscar individualmente.",
+ "search_complete": "Búsqueda completada: {count} productos",
+ "removed_sufficient": "🧹 {removed} producto(s) con stock suficiente eliminado(s) de la lista",
+ "suggest_buy": "🛒 Comprar: {qty} {unit}",
+ "suggest_buy_approx": "🛒 Al menos: {qty} {unit}",
+ "suggest_buy_tip": "Cantidad sugerida basada en tus últimos 14 días de consumo",
+ "suggest_buy_approx_tip": "Estimación mínima basada en el consumo (compra el tamaño de paquete más cercano)",
+ "bring_badge": "🛒 Ya en Bring!",
+ "add_urgent_toast": "🔴 {n} producto(s) urgente(s) añadido(s) automáticamente a Bring!",
+ "migration_done": "✅ {migrated} actualizado(s), {skipped} ya estaban ok",
+ "added_to_bring": "🛒 {n} productos añadidos a Bring!",
+ "added_to_bring_skip": "{n} ya presentes",
+ "all_on_bring": "¡Todos los productos ya estaban en Bring!",
+ "freq_high": "📈 Frecuente",
+ "freq_regular": "📊 Regular",
+ "freq_occasional": "📉 Ocasional",
+ "out_of_stock": "Sin stock",
+ "scan_toast": "📷 Escanear: {name}",
+ "empty_category": "No hay productos en esta categoría",
+ "session_empty": "🛒 Sin productos aún",
+ "urgency_critical": "Urgente",
+ "urgency_high": "Pronto",
+ "urgency_medium": "Planificar",
+ "urgency_low": "Previsión",
+ "urgency_medium_short": "Medio",
+ "urgency_low_short": "Ok",
+ "tag_urgent": "🔴 Urgente",
+ "tag_priority": "⭐ Prioridad",
+ "tag_check": "✅ Comprobar",
+ "smart_already_predicted": "📊 Las predicciones inteligentes ya predicen {name}{urgency}.",
+ "item_removed": "✅ ¡{name} eliminado de la lista!",
+ "urgency_spec_critical": "⚡ Urgente",
+ "urgency_spec_high": "🟠 Pronto",
+ "bring_add_n": "Añadir {n} a Bring!",
+ "bring_add_selected": "Añadir selección a Bring!",
+ "bring_adding": "Añadiendo...",
+ "bring_added_one": "1 producto añadido a Bring!",
+ "bring_added_many": "{n} productos añadidos a Bring!",
+ "bring_skipped": "({n} ya en la lista)",
+ "force_sync": "Forzar sincronización de Bring!",
+ "scan_target_label": "Estás buscando",
+ "scan_target_found": "¡Encontrado! Eliminar de la lista",
+ "bring_add_one": "Añadir 1 producto a Bring!",
+ "bring_add_many": "Añadir {n} productos a Bring!",
+ "syncing": "Sincronizando…",
+ "sync_done": "Sincronización completada",
+ "price_searching": "Buscando...",
+ "search_action": "Buscar",
+ "open_action": "Abrir",
+ "not_found": "No encontrado",
+ "search_price": "Buscar precio",
+ "tap_to_scan": "Toca para escanear",
+ "tag_title": "Etiqueta",
+ "remove_title": "Eliminar",
+ "found_count": "{found}/{total} productos encontrados",
+ "savings_offers": "· 🏷️ Ahorras {amount}€ con ofertas",
+ "searching_progress": "Buscando {current}/{total}...",
+ "remove_error": "Error al eliminar",
+ "btn_fetch_prices": "Buscar precios",
+ "price_total_label": "💰 Total estimado:",
+ "price_loading": "Buscando precios…",
+ "price_not_found": "precio n/d",
+ "suggest_loading": "Analizando...",
+ "suggest_error": "Error al generar sugerencias",
+ "priority_high": "Alta",
+ "priority_medium": "Media",
+ "priority_low": "Baja",
+ "smart_last_update": "Actualizado {time}",
+ "names_already_updated": "Todos los nombres ya están actualizados"
+ },
+ "ai": {
+ "title": "🤖 Identificación IA",
+ "capture": "📸 Sacar foto",
+ "retake": "🔄 Repetir",
+ "hint": "Saca una foto del producto y la IA intentará identificarlo",
+ "identifying": "🤖 Identificando producto...",
+ "no_api_key": "⚠️ Clave API de Gemini no configurada.\nAñade GEMINI_API_KEY al archivo .env en el servidor.",
+ "fields_filled": "✅ Campos rellenados por IA",
+ "use_data": "✅ Usar datos de IA",
+ "use_data_no_barcode": "✅ Usar datos de IA (sin código de barras)"
+ },
+ "log": {
+ "title": "📒 Registro de operaciones",
+ "type_added": "Añadido",
+ "type_waste": "Desechado",
+ "type_used": "Usado",
+ "type_bring": "Añadido a Bring!",
+ "undone_badge": "Deshecho",
+ "undo_title": "Deshacer esta operación",
+ "load_error": "Error al cargar el registro",
+ "empty": "No hay operaciones registradas.",
+ "undo_action_remove": "eliminación de",
+ "undo_action_restore": "reabastecimiento de",
+ "undo_confirm": "¿Deshacer esta operación?\n→ {action} {name}",
+ "undo_success": "↩ Operación deshecha para {name}",
+ "already_undone": "Operación ya deshecha",
+ "too_old": "No se pueden deshacer operaciones de más de 24 horas",
+ "undo_error": "Error al deshacer",
+ "recipe_prefix": "Receta"
+ },
+ "chat": {
+ "title": "Chef Gemini",
+ "welcome": "¡Hola! Soy tu asistente de cocina",
+ "welcome_desc": "¡Pídeme que te prepare un zumo, un snack, un plato rápido… Conozco tu despensa, tus electrodomésticos y tus preferencias!",
+ "suggestion_snack": "🍿 Snack rápido",
+ "suggestion_juice": "🥤 Zumo/Batido",
+ "suggestion_light": "🥗 Algo ligero",
+ "suggestion_expiry": "⏰ Usar productos que caducan",
+ "clear": "Nueva conversación",
+ "placeholder": "Pregunta algo...",
+ "cleared": "Chat borrado",
+ "suggestion_snack_text": "¿Qué puedo preparar como snack rápido?",
+ "suggestion_juice_text": "Hazme un zumo o batido con lo que tengo",
+ "suggestion_light_text": "Tengo hambre pero quiero algo ligero",
+ "suggestion_expiry_text": "¿Qué está a punto de caducar y cómo puedo usarlo?",
+ "transfer_to_recipes": "Transferir a recetas",
+ "transferring": "Transfiriendo...",
+ "transferred": "¡Añadido a recetas!",
+ "open_recipe": "Abrir receta",
+ "quick_recipe_prompt": "¡Sugiere una receta rápida PARA UNA PERSONA usando los productos que caducan primero! Ignora el congelador, céntrate en la nevera y la despensa."
+ },
+ "cooking": {
+ "close": "Cerrar",
+ "tts_btn": "Leer en voz alta",
+ "restart": "↺ Reiniciar",
+ "replay": "🔊 Repetir",
+ "timer": "⏱️ {time} · Temporizador",
+ "prev": "◀ Anterior",
+ "next": "Siguiente ▶",
+ "ingredient_used": "✔️ Deducido",
+ "ingredient_use_btn": "📦 Usar",
+ "ingredient_deduct_title": "Deducir de la despensa",
+ "timer_expired_tts": "¡Temporizador {label} finalizado!",
+ "timer_warning_tts": "¡Atención! {label}: ¡10 segundos restantes!",
+ "recipe_done_tts": "¡Receta completada! ¡Buen provecho!",
+ "expires_chip": "cad. {date}",
+ "finish": "✅ Finalizar",
+ "step_fallback": "Paso {n}",
+ "zerowaste_label": "♻️ Desperdicio",
+ "zerowaste_tip_title": "Consejo sin desperdicios"
+ },
+ "settings": {
+ "title": "⚙️ Ajustes",
+ "tab_api": "Claves API",
+ "tab_bring": "Bring!",
+ "tab_recipe": "Recetas",
+ "tab_mealplan": "Plan semanal",
+ "tab_appliances": "Electrodomésticos",
+ "tab_spesa": "Compra online",
+ "tab_camera": "Cámara",
+ "tab_security": "Seguridad",
+ "tab_tts": "Voz (TTS)",
+ "tab_language": "Idioma",
+ "tab_scale": "Báscula inteligente",
"gemini": {
- "chat_title": "Chat con Gemini",
- "not_configured": "🤖 Gemini no configurado — establece GEMINI_API_KEY en los ajustes"
+ "title": "🤖 Google Gemini IA",
+ "hint": "Clave API para identificación de productos, fechas de caducidad y recetas.",
+ "key_label": "Clave API Gemini"
+ },
+ "bring": {
+ "title": "🛒 Lista de la compra Bring!",
+ "hint": "Credenciales para la integración con la lista de la compra Bring!",
+ "email_label": "📧 Email Bring!",
+ "password_label": "🔒 Contraseña Bring!"
+ },
+ "price": {
+ "title": "💰 Estimación de precios (IA)",
+ "hint": "Mostrar el coste estimado por producto en la lista de la compra usando IA.",
+ "enabled_label": "Activar estimación de precios",
+ "country_label": "🌍 País de referencia",
+ "currency_label": "💱 Moneda",
+ "update_label": "🔄 Actualizar precios cada",
+ "update_suffix": "meses"
+ },
+ "recipe": {
+ "title": "🍳 Preferencias de recetas",
+ "hint": "Configura las opciones predeterminadas para la generación de recetas.",
+ "persons_label": "👥 Raciones por defecto",
+ "options_label": "🎯 Opciones de receta por defecto",
+ "fast": "⚡ Comida rápida",
+ "light": "🥗 Comida ligera",
+ "expiry": "⏰ Prioridad caducidad",
+ "healthy": "💚 Extra saludable",
+ "opened": "📦 Prioridad productos abiertos",
+ "zerowaste": "♻️ Cero desperdicio",
+ "dietary_label": "🚫 Intolerancias / Restricciones",
+ "dietary_placeholder": "Ej.: sin gluten, sin lactosa, vegetariano..."
+ },
+ "mealplan": {
+ "title": "📅 Plan de comidas semanal",
+ "hint": "Establece el tipo de comida para cada día. Se usará como guía en la generación de recetas.",
+ "enabled": "✅ Activar plan semanal",
+ "legend": "🌤️ = Almuerzo · 🌙 = Cena · Toca un badge para cambiarlo.",
+ "types_title": "📋 Tipos disponibles",
+ "reset_btn": "↺ Restaurar valores por defecto"
},
"appliances": {
- "empty": "No hay electrodomésticos añadidos"
+ "title": "🔌 Electrodomésticos disponibles",
+ "hint": "Indica los electrodomésticos que tienes. Se tendrán en cuenta en la generación de recetas.",
+ "new_placeholder": "Ej.: Panificadora, Thermomix, Freidora de aire...",
+ "quick_title": "Añadir rápido:",
+ "oven": "🔥 Horno",
+ "microwave": "📡 Microondas",
+ "air_fryer": "🍟 Freidora de aire",
+ "bread_maker": "🍞 Panificadora",
+ "bimby": "🤖 Thermomix/Cookeo",
+ "mixer": "🌀 Robot de cocina",
+ "steamer": "♨️ Vaporera",
+ "pressure_cooker": "🫕 Olla a presión",
+ "toaster": "🍞 Tostadora",
+ "blender": "🍹 Batidora",
+ "empty": "No hay electrodomésticos añadidos"
},
- "about": {
- "title": "Acerca de",
- "version": "Versión",
- "report_bug": "Reportar un error",
- "report_bug_hint": "¿Algo no funciona? Envíanos un informe directamente desde la app.",
- "report_bug_modal_title": "Reportar un error",
- "report_type_bug": "Error",
- "report_type_feature": "Función",
- "report_type_question": "Pregunta",
- "report_field_title": "Título",
- "report_field_title_ph": "Breve descripción del problema",
- "report_field_desc": "Descripción",
- "report_field_desc_ph": "Describe el problema en detalle…",
- "report_field_steps": "Pasos para reproducir (opcional)",
- "report_field_steps_ph": "1. Ir a…\n2. Tocar…\n3. Ver el error…",
- "report_auto_info": "Adjuntado automáticamente: versión {version}, idioma {lang}.",
- "report_send_btn": "Enviar informe",
- "report_bug_sending": "Enviando…",
- "report_bug_sent": "¡Informe enviado — gracias!",
- "report_bug_error": "No se pudo enviar el informe. Comprueba tu conexión.",
- "changelog": "Registro de cambios",
- "github": "Repositorio GitHub"
+ "spesa": {
+ "title": "🛍️ Compra online",
+ "hint": "Configura el proveedor de compra online.",
+ "provider_label": "🏪 Proveedor",
+ "email_label": "📧 Email",
+ "password_label": "🔒 Contraseña",
+ "login_btn": "🔐 Iniciar sesión",
+ "ai_prompt_label": "🤖 Prompt IA de selección de producto",
+ "ai_prompt_placeholder": "Instrucciones para la IA al elegir entre varios productos...",
+ "ai_prompt_hint": "La IA usa este prompt para elegir el producto más apropiado de los resultados. Deja vacío para el comportamiento por defecto.",
+ "configure_first": "Configura primero la compra online en ajustes",
+ "missing_credentials": "Introduce email y contraseña",
+ "login_in_progress": "Iniciando sesión...",
+ "login_error_prefix": "Error:",
+ "login_network_error_prefix": "Error de red:",
+ "login_success_default": "¡Inicio de sesión exitoso!",
+ "result_name_label": "Nombre",
+ "result_card_label": "Tarjeta",
+ "result_pickup_label": "Punto de recogida",
+ "result_points_label": "Puntos de fidelidad",
+ "connected_relogin": "✅ Conectado — Iniciar sesión de nuevo",
+ "connected_as": "Conectado como {name}"
},
- "export": {
- "title": "Exportar inventario",
- "hint": "Descarga el inventario actual en CSV o abre la versión imprimible (PDF).",
- "btn_csv": "Descargar CSV",
- "btn_pdf": "PDF / Imprimir",
- "btn_title": "Exportar"
+ "camera": {
+ "title": "📷 Cámara",
+ "hint": "Elige qué cámara usar para el escaneo de códigos de barras e identificación IA.",
+ "device_label": "📸 Cámara por defecto",
+ "back": "📱 Trasera (por defecto)",
+ "front": "🤳 Frontal",
+ "devices_hint": "Si tienes varias cámaras, puedes seleccionar una específica de la lista de arriba tras conceder los permisos.",
+ "detect_btn": "🔄 Detectar cámaras",
+ "ai_fallback_label": "Identificación visual IA (repuesto 5s)",
+ "ai_fallback_hint": "Si no se lee ningún código de barras en 5 segundos, se envía automáticamente un fotograma a la IA para identificar el producto visualmente. Requiere Gemini configurado."
},
- "startup": {
- "connecting": "Conectando al servidor...",
- "check_php_memory": "Memoria PHP",
- "check_php_timeout": "Tiempo de espera PHP",
- "check_php_upload": "Upload PHP",
- "check_data_dir": "Carpeta de datos",
- "check_rate_limits": "Dir. rate limits",
- "check_backups": "Dir. copias de seguridad",
- "check_write_test": "Prueba escritura disco",
- "check_disk_space": "Espacio en disco",
- "check_db_connect": "Conexión base de datos",
- "check_db_tables": "Tablas de la BD",
- "check_db_integrity": "Integridad BD",
- "check_db_wal": "Modo WAL",
- "check_db_size": "Tamaño de la BD",
- "check_db_rows": "Datos del inventario",
- "check_env": "Archivo .env",
- "check_gemini": "Clave Gemini AI",
- "check_bring_creds": "Credenciales Bring!",
- "check_bring_token": "Token de Bring!",
- "check_curl_ssl": "cURL SSL",
- "check_internet": "Conexión a internet",
- "fresh_install": "instalación nueva",
- "warnings_found": "avisos detectados",
- "all_ok": "Sistema OK",
- "critical_error_short": "Error crítico",
- "critical_error": "Error crítico: la aplicación no puede iniciarse. Revisa los registros del servidor.",
- "error_network": "No se puede contactar con el servidor. Comprueba tu conexión de red.",
- "retry": "Reintentar",
- "syncing_local": "Sincronizando datos locales...",
- "sync_done": "Datos locales sincronizados"
+ "security": {
+ "title": "🔒 Certificado HTTPS",
+ "hint": "Si el navegador muestra el error «Tu conexión no es privada» (ERR_CERT_AUTHORITY_INVALID), necesitas instalar el certificado CA en el dispositivo.",
+ "download_btn": "📥 Descargar certificado CA",
+ "token_title": "🔑 Token de ajustes",
+ "token_label": "Token de acceso",
+ "token_hint": "Si `SETTINGS_TOKEN` está configurado en el `.env` del servidor, introduce el token aquí antes de guardar los ajustes. Deja vacío si no está configurado.",
+ "token_placeholder": "(vacío = sin protección)",
+ "token_required_hint": "🔒 Este servidor requiere un token para guardar los ajustes.",
+ "cert_instructions": "Instrucciones para Chrome (Android): 1. Descarga el certificado de arriba 2. Ve a Ajustes → Seguridad y privacidad → Más ajustes de seguridad → Instalar desde almacenamiento 3. Selecciona el archivo EverShelf_CA.crt descargado 4. Elige «CA» y confirma 5. Reinicia Chrome
Instrucciones para Chrome (PC): 1. Descarga el certificado de arriba 2. Ve a chrome://settings/certificates 3. Pestaña «Autoridades» → Importar → selecciona el archivo 4. Marca «Confiar en este certificado para identificar sitios web» 5. Reinicia Chrome"
},
- "stats_monthly": {
- "title": "Estadísticas Mensuales",
- "consumed": "productos usados",
- "trend_up": "+{pct}% vs {prev}",
- "trend_down": "-{pct}% vs {prev}",
- "trend_same": "mismo ritmo que el mes pasado",
- "added": "añadidos",
- "wasted": "desperdiciados",
- "top_used": "más usado",
- "top_cats": "Categorías principales",
- "source": "Historial de transacciones · mes actual"
+ "tts": {
+ "title": "🔊 Voz & TTS",
+ "hint": "Configura la síntesis de voz mediante cualquier API REST externa. Los pasos de la receta y los temporizadores expirados se enviarán al endpoint configurado.",
+ "enabled": "✅ Activar TTS",
+ "engine_label": "⚙️ Motor TTS",
+ "engine_browser": "🔇 Navegador (sin conexión, sin configuración requerida)",
+ "engine_server": "🌐 Servidor externo (Home Assistant, API REST...)",
+ "voice_label": "🗣️ Voz",
+ "rate_label": "⚡ Velocidad",
+ "pitch_label": "🎵 Tono",
+ "url_label": "🌐 URL del endpoint",
+ "method_label": "📡 Método HTTP",
+ "auth_label": "🔐 Autenticación",
+ "auth_bearer": "Bearer Token",
+ "auth_custom": "Cabecera personalizada",
+ "auth_none": "Ninguna",
+ "token_label": "🔑 Bearer Token",
+ "custom_header_name": "📋 Nombre de cabecera",
+ "custom_header_value": "📋 Valor de cabecera",
+ "content_type_label": "📄 Content-Type",
+ "payload_key_label": "🗝️ Campo de texto en el payload",
+ "payload_key_hint": "Nombre del campo JSON que contendrá el texto a leer (ej.: message, text).",
+ "extra_fields_label": "➕ Campos adicionales (JSON)",
+ "extra_fields_placeholder": "{\"entity_id\": \"media_player.salon\"}",
+ "extra_fields_hint": "Campos adicionales a incluir en el payload, en formato JSON. Deja vacío si no es necesario.",
+ "test_btn": "🔊 Enviar voz de prueba",
+ "voices_loading": "Cargando voces…",
+ "voice_not_supported": "Voz no compatible con este navegador",
+ "voices_none": "No hay voces disponibles en este dispositivo",
+ "voices_hint": "Las voces disponibles dependen del SO y el navegador. Pulsa ↺ si la lista no carga.",
+ "url_missing": "⚠️ URL del endpoint faltante.",
+ "test_sending": "⏳ Enviando…",
+ "test_ok": "✅ Respuesta {code} — comprueba que el altavoz haya hablado.",
+ "heard_question": "¿Has escuchado la voz?",
+ "heard_yes": "Sí, la escuché",
+ "heard_no": "No, no escuché nada",
+ "test_ok_kiosk": "TTS funcionando.",
+ "test_fail_steps": "Comprueba: 1) el volumen del multimedia no es 0; 2) Google Text-to-Speech está instalado y actualizado; 3) el paquete de voz español está descargado en la configuración TTS de Android."
+ },
+ "language": {
+ "title": "🌐 Idioma",
+ "hint": "Selecciona el idioma de la interfaz.",
+ "label": "🌐 Idioma",
+ "restart_notice": "La página se recargará para aplicar el nuevo idioma."
+ },
+ "screensaver": {
+ "label": "Activar protector de pantalla",
+ "card_title": "🌙 Protector de pantalla",
+ "card_hint": "Muestra un reloj con información útil después de 5 minutos de inactividad. Desactivado por defecto.",
+ "timeout_1": "1 minuto",
+ "timeout_2": "2 minutos",
+ "timeout_5": "5 minutos",
+ "timeout_10": "10 minutos",
+ "timeout_15": "15 minutos",
+ "timeout_30": "30 minutos",
+ "timeout_60": "1 hora",
+ "start_after": "⏱️ Iniciar tras"
+ },
+ "scale": {
+ "title": "⚖️ Báscula inteligente",
+ "hint": "Conecta una báscula Bluetooth mediante la pasarela Android para leer el peso automáticamente.",
+ "tab": "Báscula inteligente",
+ "enabled": "✅ Activar báscula inteligente",
+ "url_label": "🌐 URL de la pasarela WebSocket",
+ "url_placeholder": "ws://192.168.1.x:8765",
+ "url_hint": "URL mostrada por la app Android (misma red Wi-Fi). Ej.:",
+ "test_btn": "🔗 Probar conexión",
+ "download_btn": "📥 Descargar pasarela Android (APK)",
+ "download_hint": "App Android que conecta tu báscula BLE con EverShelf.",
+ "download_sub": "Fuente: evershelf-scale-gateway/ en la raíz del proyecto",
+ "live_weight": "peso en tiempo real",
+ "auto_reconnect": "🔁 Reconexión: automática",
+ "kiosk_title": "📡 Báscula BLE integrada en el kiosco",
+ "kiosk_hint": "La báscula está gestionada directamente por la pasarela BLE interna del kiosco. Para vincular un nuevo dispositivo, usa el asistente de configuración.",
+ "kiosk_reconfigure": "🔄 Reconfigurar báscula BLE",
+ "ble_protocols": "
🔌 Protocolos BLE soportados:
Bluetooth SIG Weight Scale (0x181D)
Bluetooth SIG Body Composition (0x181B) — peso, grasa, IMC
Xiaomi Mi Body Composition Scale 2
Genérico — heurística automática para 100+ modelos
"
+ },
+ "kiosk": {
+ "hint": "Convierte una tableta Android en un panel EverShelf permanente con pasarela BLE integrada.",
+ "download_btn": "📥 Descargar EverShelf Kiosk (APK)",
+ "download_sub": "Modo kiosco a pantalla completa + pasarela de báscula integrada. Fuente: evershelf-kiosk/",
+ "native_title": "Configuración del kiosco",
+ "native_hint": "URL del servidor, báscula BLE, protector de pantalla y asistente de configuración.",
+ "native_btn": "Abrir configuración del kiosco",
+ "native_tap_hint": "Toca el botón de engranaje en la parte superior derecha",
+ "native_update_hint": "Actualiza la app del kiosco para usar esta función",
+ "update_title": "Actualización del kiosco",
+ "check_updates_btn": "🔍 Buscar actualizaciones",
+ "needs_update": "⚠️ El kiosco instalado no admite esta función. Actualiza la app del kiosco para activarla."
+ },
+ "saved": "✅ ¡Configuración guardada!",
+ "saved_local": "✅ Configuración guardada localmente",
+ "saved_local_error": "⚠️ Guardado localmente, error del servidor: {error}",
+ "theme": {
+ "title": "🌙 Apariencia",
+ "hint": "Elige el tema de la interfaz.",
+ "label": "🌙 Tema",
+ "off": "☀️ Claro",
+ "on": "🌙 Oscuro",
+ "auto": "🔄 Automático (sistema)"
+ },
+ "zerowaste": {
+ "card_title": "♻️ Consejos sin desperdicios",
+ "card_hint": "Durante la cocción, muestra consejos sobre cómo reutilizar los restos generados en cada paso (peladuras, agua de cocción, etc.). Desactivado por defecto.",
+ "label": "Mostrar consejos durante la cocción"
+ },
+ "backup": {
+ "tab": "Copia de seguridad",
+ "local_title": "Copia local",
+ "local_hint": "Instantánea diaria de la base de datos. Configura cuántos días de copias de seguridad conservar.",
+ "enabled": "Activar copia de seguridad diaria automática",
+ "retention_days": "Retención (días)",
+ "retention_info": "Las copias se conservan durante",
+ "backup_now": "Hacer copia ahora",
+ "backing_up": "Haciendo copia…",
+ "backed_up": "Copia completada",
+ "backup_error": "Error en la copia",
+ "last_backup": "Última copia",
+ "no_backup_yet": "Aún no se ha creado ninguna copia",
+ "list_empty": "No hay copias disponibles",
+ "restore_btn": "Restaurar",
+ "restore_confirm": "Restaurar la copia",
+ "delete_btn": "Eliminar",
+ "delete_confirm": "Eliminar la copia",
+ "gdrive_title": "Google Drive",
+ "gdrive_hint": "Copias de seguridad automáticas en Google Drive via OAuth 2.0. No se requieren bibliotecas externas.",
+ "gdrive_enabled": "Activar copia en Google Drive",
+ "gdrive_folder_id": "ID de carpeta de Drive",
+ "gdrive_folder_id_hint": "Copia el ID desde la URL de la carpeta de Drive: …/folders/ID",
+ "gdrive_retention_days": "Retención en Drive (días, 0=mantener todo)",
+ "gdrive_test": "Probar conexión",
+ "gdrive_ok": "Conexión exitosa!",
+ "gdrive_error": "Conexión fallida",
+ "gdrive_push_now": "Subir a Drive ahora",
+ "gdrive_pushing": "Subiendo…",
+ "gdrive_pushed": "Subido a Drive",
+ "gdrive_wizard_hint": "Opcional: copia de seguridad diaria automática en Google Drive via OAuth 2.0.",
+ "gdrive_skip": "Omitir — configurar después en Ajustes",
+ "gdrive_client_id": "Client ID",
+ "gdrive_client_secret": "Client Secret",
+ "gdrive_redirect_uri_hint": "Agrega http://localhost como URI de redireccionamiento autorizado en Google Cloud Console. Funciona en cualquier servidor, incluso sin dominio público.",
+ "gdrive_code_title": "Pegar la URL o el código de autorización",
+ "gdrive_code_hint": "Tras autorizar, el navegador abrirá http://localhost y puede mostrar un error de conexión — es normal. Copia la URL de la barra de direcciones (ej. http://localhost/?code=4%2F0A...) y pégala aquí.",
+ "gdrive_code_submit": "Confirmar",
+ "gdrive_code_empty": "Pega primero la URL o el código de autorización",
+ "gdrive_redirect_uri_label": "URI de redirección (agregar en Google Cloud Console):",
+ "gdrive_oauth_authorize": "Autorizar con Google",
+ "gdrive_oauth_authorized": "Autorizado",
+ "gdrive_oauth_not_authorized": "Aún no autorizado",
+ "gdrive_oauth_window_opened": "Ventana abierta — autoriza y regresa aquí",
+ "gdrive_oauth_how_to": "Cómo configurar OAuth 2.0 (paso a paso)",
+ "gdrive_oauth_steps": "
Habilita la API de Google Drive: API y servicios → Habilitar API → Google Drive API
Ve a API y servicios → Credenciales → Crear credenciales → ID de cliente OAuth
Tipo de aplicación: Aplicación web; agrega la URL mostrada abajo como URI de redirección autorizado
Copia el Client ID y el Client Secret en los campos de arriba y guarda
Haz clic en Autorizar con Google: inicia sesión en tu cuenta de Google y concede acceso
La ventana se cierra automáticamente al finalizar y las copias de seguridad están listas
"
+ },
+ "shopping": {
+ "tab": "Lista de la compra",
+ "title": "Lista de la compra",
+ "hint": "Configura la lista de la compra integrada o conecta Bring!.",
+ "enable_label": "Activar lista de la compra",
+ "mode_label": "Proveedor",
+ "mode_internal": "Integrado (sin Bring!)",
+ "mode_bring": "Bring! (app externa)",
+ "bring_section_title": "Configuración de Bring!",
+ "ai_section_title": "Asistencia IA",
+ "smart_suggestions_label": "Sugerencias IA",
+ "forecast_label": "Previsión de productos por agotar",
+ "auto_add_label": "Añadir automáticamente cuando",
+ "auto_add_suffix": "restante en stock (0 = solo cuando se agota)"
+ },
+ "ha": {
+ "tab": "Home Assistant",
+ "title": "Home Assistant",
+ "hint": "Conecta EverShelf a Home Assistant para automatizaciones, notificaciones push y sensores REST.",
+ "enabled": "Activar integración con Home Assistant",
+ "connection_title": "Conexión",
+ "url_label": "URL de Home Assistant",
+ "url_placeholder": "http://192.168.1.50:8123",
+ "url_hint": "URL base de tu instancia de Home Assistant.",
+ "token_label": "Token de acceso de larga duración",
+ "token_hint": "Genera desde Perfil HA → Seguridad → Tokens de acceso de larga duración.",
+ "token_placeholder": "eyJhbGci...",
+ "token_saved": "Token guardado (oculto por seguridad)",
+ "test_btn": "Probar conexión",
+ "test_ok": "Conectado a {version}",
+ "test_fail": "Conexión fallida: {error}",
+ "test_bad_token": "HA accesible pero el token no es válido",
+ "testing": "Probando…",
+ "error_no_url": "Por favor, introduce primero la URL de Home Assistant.",
+ "tts_title": "TTS en altavoz inteligente",
+ "tts_hint": "Lee los pasos de la receta en un reproductor de medios de Home Assistant.",
+ "tts_entity_label": "Entity ID del reproductor multimedia",
+ "tts_entity_placeholder": "media_player.salon",
+ "tts_entity_hint": "ID de entidad del reproductor multimedia HA. Encuéntralo en HA: Herramientas para desarrolladores → Estados.",
+ "tts_platform_label": "Plataforma TTS",
+ "tts_platform_speak": "tts.speak (recomendado)",
+ "tts_platform_notify": "notify.* (servicio de notificaciones)",
+ "tts_apply_btn": "Aplicar preset HA a la pestaña TTS",
+ "tts_apply_hint": "Pre-rellena la pestaña TTS con la URL y el token de Home Assistant.",
+ "tts_preset_applied": "Preset HA aplicado a la pestaña TTS.",
+ "webhook_title": "Automatizaciones Webhook",
+ "webhook_hint": "Envía datos a Home Assistant cuando ocurren eventos en la despensa.",
+ "webhook_id_label": "ID de Webhook",
+ "webhook_id_placeholder": "evershelf_webhook_abc123",
+ "webhook_id_hint": "ID del webhook creado en HA. Copia desde: HA → Ajustes → Automatizaciones → Crear → Disparador Webhook.",
+ "webhook_events_label": "Notificar en estos eventos",
+ "event_expiry": "Productos próximos a caducar (diario)",
+ "event_shopping": "Artículo añadido a la lista de compras",
+ "event_stock": "Nivel de stock actualizado",
+ "expiry_days_label": "Antelación de caducidad (días)",
+ "expiry_days_hint": "Enviar alerta de caducidad N días antes de la fecha.",
+ "webhook_help": "En HA: Ajustes → Automatizaciones → Crear automatización → Disparador: Webhook → copia el ID generado.",
+ "notify_title": "Notificaciones push",
+ "notify_hint": "Envía notificaciones push a tu teléfono mediante un servicio notify de Home Assistant.",
+ "notify_service_label": "Servicio notify",
+ "notify_service_placeholder": "notify.mobile_app_mi_telefono",
+ "notify_service_hint": "Nombre del servicio notify de HA. Déjalo vacío para desactivar.",
+ "sensor_title": "Sensores REST",
+ "sensor_hint": "Añade a configuration.yaml para crear sensores de EverShelf en Home Assistant.",
+ "sensor_copy_btn": "Copiar YAML",
+ "sensor_copied": "¡YAML copiado al portapapeles!",
+ "save_btn": "Guardar ajustes HA",
+ "ha_hint": "Si usas Home Assistant, utiliza la pestaña Home Assistant para configurar TTS, webhooks y sensores."
}
+ },
+ "expiry": {
+ "today": "HOY",
+ "tomorrow": "Mañana",
+ "days": "{days} días",
+ "expired_days": "hace {days}d",
+ "expired_yesterday": "Ayer",
+ "expired_today": "Hoy",
+ "badge_today": "⚠️ ¡Caduca hoy!",
+ "badge_tomorrow": "⏰ Mañana",
+ "badge_tomorrow_long": "⏰ Caduca mañana",
+ "badge_days": "⏰ {n} días",
+ "badge_expired_ago": "⚠️ Caducado hace {n}d",
+ "badge_expired": "⛔ ¡Caducado!",
+ "badge_stable": "✅ Estable",
+ "badge_expiring_short": "⏰ Cad. en {n}d",
+ "badge_ok_still": "✅ Aún {n}d",
+ "badge_expires_red": "🔴 Cad. en {n}d",
+ "badge_expires_yellow": "🟡 Cad. en {n}d",
+ "badge_expired_bare": "⚠️ Caducado",
+ "badge_expires_warn": "⚠️ Cad. en {n}d",
+ "badge_days_left": "⏳ ~{n}d restantes",
+ "days_approx": "~{n} días",
+ "weeks_approx": "~{n} semanas",
+ "months_approx": "~{n} meses",
+ "years_approx": "~{n} años",
+ "expired_today_long": "Caducado hoy",
+ "expired_ago_long": "Caducado hace {n} días",
+ "expired_suffix": "— ¡Caducado!",
+ "expired_suffix_ok": "— Caducado (aún ok)",
+ "expired_suffix_warning": "— Caducado (comprobar primero)",
+ "opened_ago_long": "Abierto hace {n} días",
+ "opened_today_long": "Abierto hoy",
+ "opened_suffix": "— ¡Abierto demasiado tiempo!",
+ "opened_suffix_ok": "— Abierto (aún ok)",
+ "opened_suffix_warning": "— Abierto (comprobar primero)",
+ "days_compact": "{n}d",
+ "badge_check_soon": "Comprobar pronto"
+ },
+ "status": {
+ "ok": "OK",
+ "check": "Comprobar",
+ "discard": "Desechar",
+ "tip_freezer_ok": "En congelador: aún seguro (~{n}d de margen)",
+ "tip_freezer_check": "En el congelador mucho tiempo, puede haber perdido calidad. Consumir pronto",
+ "tip_freezer_danger": "En el congelador demasiado tiempo, riesgo de quemadura por congelación y degradación",
+ "tip_highRisk_check": "Caducado recientemente, comprueba el olor y el aspecto antes de consumir",
+ "tip_highRisk_danger": "Producto perecedero caducado: desechar por seguridad",
+ "tip_medRisk_check1": "Comprueba el aspecto y el olor antes de consumir",
+ "tip_medRisk_check2": "Caducado hace tiempo, comprueba cuidadosamente antes de usar",
+ "tip_medRisk_danger": "Demasiado tiempo desde la caducidad, mejor desechar",
+ "tip_lowRisk_ok": "Producto de larga duración, aún seguro para consumir",
+ "tip_lowRisk_check": "Caducado hace más de un mes, comprueba la integridad del envase",
+ "tip_lowRisk_danger": "Caducado hace demasiado tiempo, mejor no arriesgarse"
+ },
+ "toast": {
+ "product_saved": "¡Producto guardado!",
+ "product_created": "¡Producto creado!",
+ "product_updated": "✅ ¡Producto actualizado!",
+ "product_removed": "Producto eliminado",
+ "updated": "¡Actualizado!",
+ "quantity_confirmed": "✓ Cantidad confirmada",
+ "added_to_inventory": "✅ ¡{name} añadido!",
+ "removed_from_list": "✅ ¡{name} eliminado de la lista!",
+ "removed_from_list_short": "Eliminado de la lista",
+ "added_to_shopping": "🛒 ¡Añadido a la lista de la compra!",
+ "removed_from_shopping": "🛒 Eliminado de la lista de la compra",
+ "finished_to_bring": "🛒 Producto terminado → ¡añadido a Bring!",
+ "thrown_away": "🗑️ ¡{name} tirado!",
+ "thrown_away_partial": "🗑️ {qty} {unit} de {name} tirado(s)",
+ "finished_all": "📤 ¡{name} terminado!",
+ "product_finished_confirmed": "✅ Eliminado — añádelo de nuevo cuando reabastezcas",
+ "appliance_added": "Electrodoméstico añadido",
+ "item_added": "{name} añadido"
+ },
+ "antiwaste": {
+ "title": "🌱 Informe anti-desperdicio",
+ "grade_label": "Nota",
+ "you": "Tú",
+ "avg_label": "Media",
+ "better": "🎉 ¡Desperdicias un {diff}% menos que la media {country}!",
+ "worse": "⚠️ Desperdicias más que la media {country}. ¡Hay margen de mejora!",
+ "on_par": "→ Estás en la media {country}. ¡Puedes hacerlo mejor!",
+ "saved_money": "~{amount}/mes ahorrado",
+ "saved_meals": "~{n} comidas salvadas",
+ "saved_co2": "{n} kg CO₂ evitados",
+ "trend_title": "Tendencia (últimos 3 meses)",
+ "months_ago_2": "-60 días",
+ "months_ago_1": "-30 días",
+ "this_month": "Ahora",
+ "country_it": "Media italiana",
+ "country_de": "Media alemana",
+ "country_en": "Media estadounidense",
+ "source": "Fuentes: REDUCE, Eurostat, USDA 2021",
+ "live_on": "Datos en vivo",
+ "live_off": "Sin conexión",
+ "meals": "comidas",
+ "annual_info": "📅 Tú ~{you} kg/año · media ~{avg} kg/año",
+ "badge_rate": "tasa de pérdida",
+ "badge_saved_money": "ahorrado vs media",
+ "badge_wasted": "artículos perdidos",
+ "badge_better": "menos que la media"
+ },
+ "error": {
+ "generic": "Error",
+ "network": "Error de red",
+ "no_api_key": "Configura la clave API en los ajustes",
+ "loading": "Error al cargar el producto",
+ "not_found": "Producto no encontrado",
+ "not_found_manual": "Producto no encontrado. Introdúcelo manualmente.",
+ "search": "Error de búsqueda. Inténtalo de nuevo.",
+ "search_short": "Error de búsqueda",
+ "save": "Error al guardar",
+ "connection": "Error de conexión",
+ "camera": "No se puede acceder a la cámara",
+ "bring_add": "Error al añadir a Bring!",
+ "bring_connection": "Error de conexión con Bring!",
+ "identification": "Error de identificación",
+ "ai_quota": "Cuota de IA agotada. Inténtalo de nuevo en unos minutos.",
+ "barcode_empty": "Introduce un código de barras",
+ "barcode_format": "El código de barras solo puede contener números (4-14 dígitos)",
+ "min_chars": "Escribe al menos 2 caracteres",
+ "not_in_inventory": "Producto no en inventario",
+ "appliance_exists": "El electrodoméstico ya existe",
+ "already_exists": "Ya existe",
+ "network_retry": "Error de conexión. Inténtalo de nuevo.",
+ "select_items": "Selecciona al menos un producto",
+ "server_offline": "Conexión con el servidor perdida",
+ "server_restored": "Conexión con el servidor restaurada",
+ "server_retry": "Reintentar",
+ "unknown": "Error desconocido",
+ "prefix": "Error",
+ "no_inventory_entry": "No se encontró ninguna entrada de inventario",
+ "offline_title": "Sin conexión",
+ "offline_subtitle": "La app no puede conectar con el servidor. Verifica tu conexión Wi-Fi.",
+ "offline_checking": "Verificando conexión…",
+ "offline_restored": "¡Conexión restaurada!",
+ "offline_continue": "Continuar en modo sin conexión",
+ "offline_reading_cache": "Leyendo desde caché local",
+ "offline_ops_pending": "{n} operaciones pendientes",
+ "offline_synced": "{n} operaciones sincronizadas",
+ "offline_ai_disabled": "No disponible sin conexión",
+ "offline_cache_ready": "Offline — {n} productos en caché"
+ },
+ "confirm_placeholder_search": null,
+ "confirm": {
+ "remove_item": "¿Realmente quieres eliminar este producto del inventario?",
+ "kiosk_exit": "¿Salir del modo kiosco?",
+ "cancel": "Cancelar",
+ "proceed": "Confirmar",
+ "discard_one": "Tirar 1 unidad"
+ },
+ "location": {
+ "dispensa": "Despensa",
+ "frigo": "Nevera",
+ "freezer": "Congelador"
+ },
+ "edit": {
+ "title": "Editar {name}",
+ "unknown_hint": "Introduce el nombre del producto y la información",
+ "label_name": "🏷️ Nombre del producto",
+ "choose_location_title": "¿Qué ubicación?",
+ "choose_location_hint": "Elige la ubicación a editar:",
+ "confirm_large_qty": "Estás configurando la cantidad a {qty} {unit}. Esto parece inusualmente alto. ¿Confirmar?"
+ },
+ "screensaver": {
+ "recipe_btn": "Recetas",
+ "scan_btn": "Escanear producto"
+ },
+ "days": {
+ "mon": "Lunes",
+ "tue": "Martes",
+ "wed": "Miércoles",
+ "thu": "Jueves",
+ "fri": "Viernes",
+ "sat": "Sábado",
+ "sun": "Domingo",
+ "mon_short": "Lun",
+ "tue_short": "Mar",
+ "wed_short": "Mié",
+ "thu_short": "Jue",
+ "fri_short": "Vie",
+ "sat_short": "Sáb",
+ "sun_short": "Dom"
+ },
+ "meal_types": {
+ "lunch": "Almuerzo",
+ "dinner": "Cena",
+ "colazione": "Desayuno",
+ "merenda": "Merienda",
+ "dolce": "Postre",
+ "succo": "Zumo de fruta",
+ "pranzo": "Almuerzo",
+ "cena": "Cena"
+ },
+ "scale": {
+ "status_connected": "Báscula conectada",
+ "status_searching": "Pasarela conectada, esperando báscula…",
+ "status_disconnected": "Pasarela de báscula inaccesible",
+ "status_error": "Error de conexión con la pasarela",
+ "not_connected": "Pasarela de báscula no conectada",
+ "read_btn": "⚖️ Leer desde báscula",
+ "reading_title": "Lectura de báscula",
+ "place_on_scale": "Coloca el producto en la báscula…",
+ "waiting_stable": "El peso se capturará automáticamente cuando la lectura sea estable.",
+ "no_url": "Introduce la URL de la pasarela",
+ "testing": "⏳ Probando conexión…",
+ "connected_ok": "¡Conexión con la pasarela exitosa!",
+ "timeout": "Tiempo de espera agotado: sin respuesta de la pasarela",
+ "error_connect": "No se puede conectar a la pasarela",
+ "tab": "Báscula inteligente",
+ "low_weight": "Peso < 10 g · introduce manualmente\n(la lectura automática requiere al menos 10 g)",
+ "density_hint": "(densidad {density} g/ml)",
+ "ml_hint": "(se convertirá a ml)",
+ "weight_detected": "Peso detectado — espera 10s de estabilidad…",
+ "weight_too_low": "Peso demasiado bajo — esperando…",
+ "stable": "✓ Estable",
+ "auto_confirm": "✅ {val} {unit} — confirmación automática en 5s (toca para cancelar)",
+ "cancelled_replace": "Cancelado — vuelve a colocar el ingrediente en la báscula para continuar"
+ },
+ "prediction": {
+ "expected_qty": "Esperado: {expected} {unit}",
+ "actual_qty": "Actual: {actual} {unit}",
+ "check_suggestion": "Comprueba o pesa la cantidad restante"
+ },
+ "date": {
+ "today": "📅 Hoy",
+ "yesterday": "📅 Ayer"
+ },
+ "scanner": {
+ "title_barcode": "🔖 Escanear código de barras",
+ "barcode_hint": "Encuadra el código de barras del producto",
+ "barcode_manual_placeholder": "O introduce manualmente...",
+ "barcode_use_btn": "✅ Usar este código",
+ "ai_identifying": "🤖 Identificando producto...",
+ "ai_analyzing": "🤖 Análisis IA en curso...",
+ "product_label_hint": "Encuadra la etiqueta del producto",
+ "expiry_label_hint": "Encuadra la fecha de caducidad impresa en el producto",
+ "capture_btn": "📸 Capturar",
+ "capture_photo_btn": "📸 Sacar foto",
+ "retake_btn": "🔄 Repetir",
+ "camera_error_hint": "Asegúrate de usar HTTPS y haber concedido los permisos de cámara. Puedes introducir el código de barras manualmente o usar la identificación IA.",
+ "no_barcode": "Sin código de barras",
+ "save_new_btn": "🆕 Ninguno de estos — guardar como nuevo"
+ },
+ "lowstock": {
+ "title": "⚠️ ¡Stock bajo!",
+ "message": "{name} se está agotando — solo quedan {qty}.",
+ "question": "¿Quieres añadirlo a la lista de la compra?",
+ "yes": "🛒 Sí, añadir a Bring!",
+ "no": "No, por ahora estoy bien"
+ },
+ "move": {
+ "title": "📦 ¿Mover el resto?",
+ "question": "¿Quieres mover {thing} de {name} a otra ubicación?",
+ "question_short": "¿Quieres mover {thing} a otra ubicación?",
+ "thing_opened": "el paquete abierto",
+ "thing_rest": "el resto",
+ "stay_btn": "No, quedarse en {location}",
+ "moved_toast": "📦 Paquete abierto movido a {location}",
+ "vacuum_restore": "🫙 Restaurar al vacío",
+ "vacuum_seal_rest": "🔒 Sellar el resto al vacío"
+ },
+ "nova": {
+ "1": "Sin procesar",
+ "2": "Ingrediente culinario",
+ "3": "Procesado",
+ "4": "Ultraprocesado"
+ },
+ "meal_plan_types": {
+ "pasta": "Pasta",
+ "riso": "Arroz",
+ "carne": "Carne",
+ "pesce": "Pescado",
+ "legumi": "Legumbres",
+ "uova": "Huevos",
+ "formaggio": "Queso",
+ "pizza": "Pizza",
+ "affettati": "Fiambres",
+ "verdure": "Verduras",
+ "zuppa": "Sopa",
+ "insalata": "Ensalada",
+ "pane": "Pan/Bocadillo",
+ "dolce": "Postre",
+ "libero": "Libre"
+ },
+ "meal_sub": {
+ "dolce_torta": "Tarta",
+ "dolce_crema": "Crema / Pudín",
+ "dolce_crumble": "Crumble / Tarta",
+ "dolce_biscotti": "Galletas / Pastelería",
+ "dolce_frutta": "Postre de fruta",
+ "succo_dolce": "Dulce / Afrutado",
+ "succo_energizzante": "Energizante",
+ "succo_detox": "Detox / Verde",
+ "succo_rinfrescante": "Refrescante",
+ "succo_vitaminico": "Vitamínico / Cítricos"
+ },
+ "meal_plan": {
+ "reset_success": "Plan semanal restablecido",
+ "not_available": "no disponible en la despensa",
+ "suggested_by": "sugerido por el plan semanal"
+ },
+ "nutrition": {
+ "title": "🥗 Análisis alimentario",
+ "score_excellent": "😄 Excelente",
+ "score_good": "🙂 Bien",
+ "score_improve": "😬 Mejorable",
+ "label_health": "🌿 Salud",
+ "label_variety": "🎨 Variedad",
+ "label_fresh": "❄️ Fresco",
+ "source": "Basado en {n} productos en tu despensa · EverShelf",
+ "products_count": "productos",
+ "today_title": "🥗 Tu despensa hoy",
+ "products_n": "{n} productos",
+ "macros_title": "Macronutrientes estimados",
+ "macros_proteins": "Proteínas",
+ "macros_carbs": "Carbohidratos",
+ "macros_fat": "Grasas",
+ "macros_fiber": "Fibra",
+ "macros_source": "Estimación basada en {n} productos en despensa"
+ },
+ "facts": {
+ "greeting_morning": "Buenos días",
+ "greeting_afternoon": "Buenas tardes",
+ "greeting_evening": "Buenas noches",
+ "pantry_waiting": "¡{greeting}! Tu despensa te espera.",
+ "expired_one": "Tienes 1 producto caducado en tu despensa. ¡Compruébalo!",
+ "expired_many": "Tienes {n} productos caducados en tu despensa. ¡Compruébalos!",
+ "expired_list": "Productos caducados: {names}",
+ "expired_list_more": "y {n} más",
+ "freezer_expired_ok": "{name} está caducado, pero al estar en el congelador puede estar bien. ¡Compruébalo!",
+ "freezer_expired_old": "{name} en el congelador lleva demasiado tiempo caducado. Mejor tirarlo.",
+ "fridge_expired_one": "¡Tienes 1 producto caducado en la nevera!",
+ "fridge_expired_many": "¡Tienes {n} productos caducados en la nevera!",
+ "expiring_today": "¡{name} caduca hoy! Úsalo enseguida.",
+ "expiring_tomorrow": "{name} caduca mañana. ¡Planifica!",
+ "expiring_days": "{name} caduca en {days} días.",
+ "expiring_many": "Tienes {n} productos que caducan pronto.",
+ "expiring_this_week": "¡{n} productos caducan esta semana. Planifica tus comidas en consecuencia!",
+ "expiring_item_loc": "{name} ({loc}) caduca en {days} {dayslabel}.",
+ "expiring_this_month": "{n} productos caducarán este mes.",
+ "shopping_add": "Añadir a la lista: {names} 🛒",
+ "shopping_more": "y {n} más",
+ "shopping_empty": "¡Lista de la compra vacía. Todo en stock! ✅",
+ "in_fridge": "En la nevera: {name}.",
+ "in_freezer": "En el congelador: {name}. ¡No lo olvides!",
+ "top_category": "La categoría principal es {icon} {cat} con {n} productos.",
+ "cat_meat": "Tienes {n} productos cárnicos. 🥩",
+ "cat_dairy": "Tienes {n} productos lácteos en casa. 🥛",
+ "cat_veggies": "Tienes {n} tipos de verduras. ¡Genial para la salud! 🥬",
+ "cat_fruit": "Tienes {n} tipos de fruta. 🍎",
+ "cat_drinks": "Tienes {n} bebidas disponibles. 🥤",
+ "cat_frozen": "Tienes {n} artículos congelados. ❄️",
+ "cat_pasta": "Tienes {n} tipos de pasta. 🍝 ¿Y si hacemos una carbonara?",
+ "cat_canned": "Tienes {n} conservas en la despensa. 🥫",
+ "cat_snacks": "Tienes {n} snacks. ¡Resiste la tentación! 🍪",
+ "cat_condiments": "Tienes {n} condimentos disponibles. 🧂",
+ "item_random": "¿Sabías que tienes {name} en {loc}?",
+ "item_qty": "{name}: tienes {qty}.",
+ "no_expiry_count": "{n} productos no tienen fecha de caducidad.",
+ "furthest_expiry": "El producto con la fecha de caducidad más lejana es {name}: {months} meses.",
+ "high_qty": "¡Tienes un buen stock de {name}: {qty}!",
+ "low_qty_item": "{name} se está agotando. ¿Añadirlo a tu lista de la compra?",
+ "low_qty_count": "{n} productos están casi agotados.",
+ "morning_bread": "¡Buenos días! Tienes pan para el desayuno. 🍞",
+ "morning_milk": "¿Hay leche en la nevera para un café con leche? ☕🥛",
+ "morning_fruit": "¡Buenos días! Algo de fruta fresca es un gran comienzo. 🍎",
+ "noon_pasta": "Es hora de comer… ¿Y si preparamos un buen plato de pasta? 🍝",
+ "noon_salad": "¿Una ensalada fresca para comer? ¡Tienes {n} verduras! 🥗",
+ "evening_meat": "Para cenar podrías usar la carne que tienes. 🥩",
+ "evening_fish": "¿Qué tal pescado para cenar? 🐟",
+ "evening_expiring": "Tienes {n} productos que caducan esta semana — ¡úsalos esta noche!",
+ "night_reminder": "¡Buenas noches! Recuerda usar mañana: {names}.",
+ "weekly_balance": "Balance semanal: +{in} añadidos, −{out} consumidos.",
+ "weekly_added": "Has añadido {n} productos esta semana.",
+ "weekly_consumed": "Has consumido {n} productos esta semana. ¡Bien hecho!",
+ "tip_freezer": "💡 Los productos congelados duran mucho más que la fecha de caducidad.",
+ "tip_bread": "💡 El pan congelado conserva su frescura durante semanas.",
+ "tip_fifo": "💡 Para evitar desperdicios, usa primero los productos más cercanos a la caducidad (FIFO).",
+ "tip_meat": "💡 La carne en el congelador puede durar hasta 6 meses con seguridad.",
+ "tip_no_refreeze": "💡 Nunca vuelvas a congelar un producto descongelado. ¡Cocínalo enseguida!",
+ "tip_fridge": "💡 Una nevera ordenada te ahorra tiempo y dinero.",
+ "tip_canned": "💡 Las conservas abiertas deben ir a la nevera y consumirse en pocos días.",
+ "top_brand": "La marca más común en tu despensa es {brand} con {n} productos.",
+ "combo_pasta": "Tienes pasta y condimentos: ¡listo para un primer plato! 🍝",
+ "combo_sandwich": "Pan y carne: ¡un sándwich rápido siempre es buena idea! 🥪",
+ "combo_balanced": "Verduras y carne: ¡tienes todo para una comida equilibrada! 🥗🥩",
+ "pantry_empty": "¡La despensa está vacía! Es hora de ir al supermercado. 🛒",
+ "pantry_empty_scan": "No hay productos registrados. ¡Escanea algo para empezar!",
+ "location_distribution": "Distribución: {parts}",
+ "day": "día",
+ "days": "días"
+ },
+ "kiosk_session": {
+ "first_item": "¡Primer artículo: {name}!",
+ "items_two_four": "{n} artículos — arrancando 🚀",
+ "items_five_nine": "{n} artículos — ¡buen ritmo! 💪",
+ "items_ten_twenty": "{n} artículos — casi un récord 🏆",
+ "items_twenty_plus": "{n} artículos — ¡compra épica! 🛒🔥",
+ "duplicates_one": "1 duplicado (mismo artículo dos veces)",
+ "duplicates_many": "{n} duplicados (cogido varias veces)",
+ "top_category": "Categoría principal: {cat} ({count}×)",
+ "items_fallback": "{n} artículo{plural} añadido{plural}"
+ },
+ "kiosk": {
+ "check_btn": "🔍 Buscar actualizaciones",
+ "checking": "⏳ Comprobando…",
+ "error_check": "Error durante la comprobación de actualizaciones",
+ "error_start_install": "Error al iniciar la instalación",
+ "version_installed": "Instalado: {v}",
+ "update_available": "⬆️ Nueva versión disponible: {latest} (instalada: {current})",
+ "up_to_date": "✅ Estás actualizado — versión {v}",
+ "too_old": "⚠️ El kiosco instalado es demasiado antiguo para la comprobación automática de actualizaciones. Pulsa el botón de abajo para descargar e instalar la nueva versión directamente.",
+ "manual_install": "⚠️ Este kiosco no admite instalación automática. Procedimiento manual: 1. Sal del kiosco (botón ✕ arriba a la izquierda) 2. Desinstala la app EverShelf Kiosk 3. Descarga e instala el nuevo APK desde GitHub:",
+ "starting_download": "⏳ Iniciando descarga…",
+ "install_btn": "⬇️ Instalar actualización",
+ "exit_title": "Salir del kiosco",
+ "refresh_title": "Actualizar página"
+ },
+ "update": {
+ "new_version": "Nueva versión",
+ "btn": "Actualizar"
+ },
+ "gemini": {
+ "chat_title": "Chat con Gemini",
+ "not_configured": "🤖 Gemini no configurado — establece GEMINI_API_KEY en los ajustes"
+ },
+ "appliances": {
+ "empty": "No hay electrodomésticos añadidos"
+ },
+ "about": {
+ "title": "Acerca de",
+ "version": "Versión",
+ "report_bug": "Reportar un error",
+ "report_bug_hint": "¿Algo no funciona? Envíanos un informe directamente desde la app.",
+ "report_bug_modal_title": "Reportar un error",
+ "report_type_bug": "Error",
+ "report_type_feature": "Función",
+ "report_type_question": "Pregunta",
+ "report_field_title": "Título",
+ "report_field_title_ph": "Breve descripción del problema",
+ "report_field_desc": "Descripción",
+ "report_field_desc_ph": "Describe el problema en detalle…",
+ "report_field_steps": "Pasos para reproducir (opcional)",
+ "report_field_steps_ph": "1. Ir a…\n2. Tocar…\n3. Ver el error…",
+ "report_auto_info": "Adjuntado automáticamente: versión {version}, idioma {lang}.",
+ "report_send_btn": "Enviar informe",
+ "report_bug_sending": "Enviando…",
+ "report_bug_sent": "¡Informe enviado — gracias!",
+ "report_bug_error": "No se pudo enviar el informe. Comprueba tu conexión.",
+ "changelog": "Registro de cambios",
+ "github": "Repositorio GitHub"
+ },
+ "export": {
+ "title": "Exportar inventario",
+ "hint": "Descarga el inventario actual en CSV o abre la versión imprimible (PDF).",
+ "btn_csv": "Descargar CSV",
+ "btn_pdf": "PDF / Imprimir",
+ "btn_title": "Exportar"
+ },
+ "startup": {
+ "connecting": "Conectando al servidor...",
+ "check_php_memory": "Memoria PHP",
+ "check_php_timeout": "Tiempo de espera PHP",
+ "check_php_upload": "Upload PHP",
+ "check_data_dir": "Carpeta de datos",
+ "check_rate_limits": "Dir. rate limits",
+ "check_backups": "Dir. copias de seguridad",
+ "check_write_test": "Prueba escritura disco",
+ "check_disk_space": "Espacio en disco",
+ "check_db_connect": "Conexión base de datos",
+ "check_db_tables": "Tablas de la BD",
+ "check_db_integrity": "Integridad BD",
+ "check_db_wal": "Modo WAL",
+ "check_db_size": "Tamaño de la BD",
+ "check_db_rows": "Datos del inventario",
+ "check_env": "Archivo .env",
+ "check_gemini": "Clave Gemini AI",
+ "check_bring_creds": "Credenciales Bring!",
+ "check_bring_token": "Token de Bring!",
+ "check_curl_ssl": "cURL SSL",
+ "check_internet": "Conexión a internet",
+ "fresh_install": "instalación nueva",
+ "warnings_found": "avisos detectados",
+ "all_ok": "Sistema OK",
+ "critical_error_short": "Error crítico",
+ "critical_error": "Error crítico: la aplicación no puede iniciarse. Revisa los registros del servidor.",
+ "error_network": "No se puede contactar con el servidor. Comprueba tu conexión de red.",
+ "retry": "Reintentar",
+ "syncing_local": "Sincronizando datos locales...",
+ "sync_done": "Datos locales sincronizados"
+ },
+ "stats_monthly": {
+ "title": "Estadísticas Mensuales",
+ "consumed": "productos usados",
+ "trend_up": "+{pct}% vs {prev}",
+ "trend_down": "-{pct}% vs {prev}",
+ "trend_same": "mismo ritmo que el mes pasado",
+ "added": "añadidos",
+ "wasted": "desperdiciados",
+ "top_used": "más usado",
+ "top_cats": "Categorías principales",
+ "source": "Historial de transacciones · mes actual"
+ }
}
\ No newline at end of file
diff --git a/translations/fr.json b/translations/fr.json
index eda3281..1070b5b 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -1,1401 +1,1406 @@
{
- "app": {
- "name": "EverShelf",
- "loading": "Chargement..."
- },
- "nav": {
- "title": "EverShelf",
- "home": "Accueil",
- "inventory": "Garde-manger",
- "recipes": "Recettes",
- "shopping": "Courses",
- "log": "Journal",
- "settings": "Paramètres"
- },
- "btn": {
- "back": "← Retour",
- "save": "💾 Enregistrer",
- "cancel": "✕ Annuler",
- "close": "Fermer",
- "add": "✅ Ajouter",
- "delete": "Supprimer",
- "edit": "✏️ Modifier",
- "use": "Utiliser",
- "edit_item": "Modifier",
- "search": "🔍 Rechercher",
- "go": "✅ Valider",
- "toggle_password": "👁️ Afficher/Masquer",
- "load_more": "Charger plus...",
- "save_config": "💾 Enregistrer la configuration",
- "save_product": "💾 Enregistrer le produit",
- "restart": "↺ Redémarrer",
- "reset_default": "↺ Rétablir les valeurs par défaut",
- "save_info": "💾 Enregistrer les informations",
- "retry": "🔄 Réessayer",
- "yes_short": "Oui",
- "no_short": "Non"
- },
- "form": {
- "select_placeholder": "-- Sélectionner --"
- },
- "locations": {
- "dispensa": "Garde-manger",
- "frigo": "Réfrigérateur",
- "freezer": "Congélateur",
- "altro": "Autre"
- },
- "categories": {
- "latticini": "Produits laitiers",
- "carne": "Viande",
- "pesce": "Poisson",
- "frutta": "Fruits",
- "verdura": "Légumes",
- "pasta": "Pâtes & Riz",
- "pane": "Pain & Boulangerie",
- "surgelati": "Surgelés",
- "bevande": "Boissons",
- "condimenti": "Condiments",
- "snack": "Snacks & Sucreries",
- "conserve": "Conserves",
- "cereali": "Céréales & Légumineuses",
- "igiene": "Hygiène",
- "pulizia": "Entretien",
- "altro": "Autre",
- "select": "-- Sélectionner --"
- },
- "units": {
- "pz": "pcs",
- "conf": "pkg",
- "g": "g",
- "ml": "ml",
- "pieces": "Pièces",
- "grams": "Grammes",
- "box": "Paquet",
- "boxes": "Paquets",
- "millilitres": "Millilitres",
- "from": "de"
- },
- "shopping_sections": {
- "frutta_verdura": "Fruits & Légumes",
- "carne_pesce": "Viande & Poisson",
- "latticini": "Produits laitiers & Frais",
- "pane_dolci": "Pain & Pâtisseries",
- "pasta": "Pâtes & Céréales",
- "conserve": "Conserves & Sauces",
- "surgelati": "Surgelés",
- "bevande": "Boissons",
- "pulizia_igiene": "Nettoyage & Hygiène",
- "altro": "Autre"
- },
- "dashboard": {
- "expired_title": "🚫 Périmé",
- "expiring_title": "⏰ Expire bientôt",
- "stats_period": "📊 30 derniers jours",
- "opened_title": "📦 Produits ouverts",
- "review_title": "🔍 À vérifier",
- "review_hint": "Quantités inhabituelles. Confirmez si elles sont correctes ou modifiez-les.",
- "quick_recipe": "Recette rapide avec les produits qui expirent",
- "banner_review_title": "Quantité anormale",
- "banner_review_action_ok": "C'est correct",
- "banner_review_action_finish": "🗑️ Tout fini",
- "banner_review_action_edit": "Corriger",
- "banner_review_action_weigh": "Peser",
- "banner_review_dismiss": "Ignorer",
- "banner_prediction_title": "Consommation à vérifier",
- "banner_prediction_hint": "L'estimation de consommation s'adapte aux données récentes : confirmez uniquement si la quantité actuelle est correcte.",
- "banner_prediction_action_confirm": "Confirmer {qty} {unit}",
- "banner_prediction_action_weigh": "Peser maintenant",
- "banner_prediction_action_edit": "Mettre à jour la quantité",
- "banner_expired_title": "Produit périmé",
- "banner_expired_today": "Périmé aujourd'hui",
- "banner_expired_days": "Périmé il y a {days} jours",
- "banner_expired_action_use": "Utiliser quand même",
- "banner_expired_action_finished": "Je l'ai terminé !",
- "banner_expired_action_throw": "Je l'ai jeté",
- "banner_expired_action_edit": "Corriger la date",
- "banner_anomaly_action_edit": "Corriger l'inventaire",
- "banner_anomaly_action_dismiss": "La quantité est correcte",
- "banner_no_expiry_title": "Date manquante : {name}",
- "banner_no_expiry_detail": "Ce produit n'a pas de date de péremption. Voulez-vous en ajouter une ou confirmer qu'il ne périme pas ?",
- "banner_no_expiry_action_set": "Définir une date de péremption",
- "banner_no_expiry_action_dismiss": "Ne périme pas ✓",
- "banner_no_expiry_toast_dismissed": "Marqué comme « sans date limite »",
- "banner_expiring_title": "Expire bientôt",
- "banner_expiring_today": "Expire aujourd'hui !",
- "banner_expiring_tomorrow": "Expire demain",
- "banner_expiring_days": "Expire dans {days} jours",
- "banner_expiring_action_use": "Utiliser maintenant",
- "banner_finished_title": "terminé ?",
- "banner_finished_detail": "J'ai enregistré que {name} a atteint zéro stock. Est-il vraiment épuisé ou en avez-vous encore ?",
- "banner_finished_action_yes": "Oui, c'est fini",
- "banner_finished_action_no": "Non, j'en ai encore",
- "banner_review_unusual_pkg_title": "Taille de paquet inhabituelle",
- "banner_review_unusual_pkg_detail": "Vous avez défini un paquet de {qty} {unit} — la taille semble très grande. Vérifiez si c'est correct ou modifiez.",
- "banner_review_low_qty_title": "Quantité très faible",
- "banner_review_low_qty_detail": "Vous n'avez que {qty} en stock — cela semble très peu, peut-être une erreur de saisie. Confirmez si c'est correct.",
- "banner_review_high_qty_title": "Quantité inhabituellement élevée",
- "banner_review_high_qty_detail": "Vous avez {qty} en stock — le chiffre semble très élevé. Confirmez si c'est correct ou modifiez.",
- "banner_prediction_rate_day": "Moyenne ~{n} {unit}/jour",
- "banner_prediction_rate_week": "Moyenne ~{n} {unit}/semaine",
- "banner_prediction_days_ago": "Il y a {n} jours vous avez réapprovisionné",
- "banner_prediction_more": "estimation précédente : {expected} {unit}{time} ; quantité actuelle : {actual} {unit}.",
- "banner_prediction_less": "estimation : {expected} {unit}{time} ; quantité actuelle : {actual} {unit}. Si votre rythme d'utilisation a changé, la prévision se met à jour automatiquement.",
- "banner_finished_zero": "L'inventaire indique zéro, mais les mouvements enregistrés suggèrent qu'il ne devrait pas être vide.",
- "banner_finished_expected": "D'après les enregistrements vous devriez avoir encore {qty} {unit}.",
- "banner_finished_check": "Pouvez-vous vérifier ?",
- "banner_anomaly_phantom_title": "vous avez plus de stock que prévu",
- "banner_anomaly_phantom_detail": "L'inventaire indique {inv_qty} {unit}, mais selon les enregistrements vous ne devriez avoir que {expected_qty} {unit}. Avez-vous ajouté du stock sans l'enregistrer ?",
- "banner_anomaly_untracked_title": "stock non enregistré comme entrée",
- "banner_anomaly_untracked_detail": "Vous avez {inv_qty} {unit} en inventaire, mais les sorties enregistrées dépassent les entrées — le stock initial n'a probablement jamais été ajouté comme transaction « entrée ». Vous pouvez corriger la quantité ou saisir les entrées manquantes.",
- "banner_anomaly_ghost_title": "vous avez moins de stock que prévu",
- "banner_anomaly_ghost_detail": "D'après les opérations enregistrées vous devriez avoir {expected_qty} {unit} de {name}, mais l'inventaire n'en montre que {inv_qty} {unit}. Avez-vous pris du stock sans l'enregistrer ?",
- "consumed": "Consommé : {n} ({pct}%)",
- "wasted": "Gaspillé : {n} ({pct}%)",
- "more_opened": "et {n} autres ouverts...",
- "banner_expired_detail": "{when} · il vous reste encore {qty}.",
- "banner_opened_detail": "{when} dans {location} · il vous reste encore {qty}.",
- "banner_explain_title": "Demander une explication à Gemini",
- "banner_explain_btn": "Expliquer",
- "banner_analyzing": "🤖 Analyse en cours…"
- },
- "inventory": {
- "title": "Garde-manger",
- "filter_all": "Tout",
- "search_placeholder": "🔍 Rechercher un produit...",
- "recent_title": "🕐 Utilisés récemment",
- "popular_title": "⭐ Les plus utilisés",
- "empty": "Aucun produit ici.\nScannez un produit pour l'ajouter !",
- "no_items_found": "Aucun article trouvé",
- "qty_remainder_suffix": "restant",
- "vacuum_badge": "🫙 Sous vide",
- "opened_badge": "📭 Ouvert",
- "label_expiry": "📅 Péremption",
- "label_storage": "🫙 Conservation",
- "label_status": "📭 État",
- "opened_since": "Ouvert depuis le {date}",
- "label_position": "📍 Emplacement",
- "label_quantity": "📦 Quantité",
- "label_added": "📅 Ajouté",
- "empty_text": "Aucun produit ici. Scannez un produit pour l'ajouter !",
- "empty_db": "Aucun produit dans la base de données. Scannez un produit pour commencer !",
- "qty_trace": "< 1"
- },
- "scan": {
- "title": "Scanner",
- "mode_shopping": "🛒 Mode courses",
- "mode_shopping_end": "✅ Terminer les courses",
- "spesa_btn": "🛒 Courses",
- "zoom": "Zoom",
- "tab_barcode": "Code-barres",
- "tab_name": "Nom",
- "tab_ai": "IA",
- "recents_label": "Récents",
- "torch_hint": "Torche",
- "torch_on": "Torche activée",
- "torch_off": "Torche désactivée",
- "torch_unavailable": "Torche non disponible sur cet appareil",
- "flip_hint": "Retourner la caméra",
- "flip_front": "Caméra frontale",
- "flip_back": "Caméra arrière",
- "num_ocr_btn": "🔢 Lire les chiffres avec l'IA",
- "num_ocr_searching": "Recherche du code-barres avec l'IA...",
- "num_ocr_found": "Code trouvé : {code}",
- "num_ocr_not_found": "Aucun code-barres trouvé dans l'image",
- "barcode_placeholder": "Entrez le code-barres...",
- "quick_name_divider": "ou tapez le nom",
- "quick_name_placeholder": "Ex. : Pommes, Courgettes, Pain...",
- "manual_entry": "✏️ Saisie manuelle",
- "ai_identify": "🤖 Identifier avec l'IA",
- "hint": "Scannez le code-barres, tapez le nom du produit ou utilisez l'IA pour l'identifier",
- "debug_toggle": "🐛 Journal de débogage",
- "barcode_acquired": "🔖 Code-barres scanné : {code}",
- "scan_barcode": "🔖 Scanner le code-barres",
- "create_named": "Créer {name}",
- "new_without_barcode": "Nouveau produit sans code-barres",
- "status_ready": "Pointez la caméra sur le code-barres",
- "status_scanning": "Scan en cours...",
- "status_partial": "Lu : {code} — vérification...",
- "status_invalid": "Invalide : {code} — nouvel essai",
- "status_confirmed": "Confirmé !",
- "status_parallel": "Scan combiné actif..."
- },
- "action": {
- "title": "Que voulez-vous faire ?",
- "add_btn": "📥 AJOUTER",
- "add_sub": "au garde-manger/réfrigérateur",
- "use_btn": "📤 UTILISER / CONSOMMER",
- "use_sub": "depuis le garde-manger/réfrigérateur",
- "have_title": "📦 Déjà en stock !",
- "add_more_sub": "en ajouter",
- "use_qty_sub": "combien vous avez utilisé",
- "throw_btn": "🗑️ JETER",
- "throw_sub": "jeter",
- "edit_sub": "péremption, emplacement…",
- "create_recipe_btn": "Recette"
- },
- "add": {
- "title": "Ajouter au garde-manger",
- "location_label": "📍 Où le rangez-vous ?",
- "quantity_label": "📦 Quantité",
- "conf_size_label": "📦 Chaque paquet contient :",
- "conf_size_placeholder": "ex. 300",
- "vacuum_label": "🫙 Sous vide",
- "vacuum_hint": "La date de péremption sera automatiquement prolongée",
- "submit": "✅ Ajouter",
- "purchase_type_label": "🛒 Ce produit est...",
- "new_btn": "🆕 Vient d'être acheté",
- "existing_btn": "📦 Je l'avais déjà",
- "remaining_label": "📦 Quantité restante",
- "remaining_hint": "Approximativement combien en reste-t-il ?",
- "remaining_full": "🟢 Plein",
- "remaining_half": "🟠 À moitié",
- "estimated_expiry": "Date de péremption estimée :",
- "suffix_freezer": "(congélateur)",
- "suffix_vacuum": "(sous vide)",
- "hint_modify": "📝 Vous pouvez modifier la date ou la scanner avec la caméra",
- "scan_expiry_title": "📷 Scanner la date de péremption",
- "product_added": "✅ {name} ajouté !{qty}",
- "suffix_freezer_vacuum": "(congélateur + sous vide)",
- "history_badge_tip": "Moyenne de {n} entrées précédentes",
- "vacuum_question": "Sous vide ?",
- "vacuum_saved": "🔒 Sous vide !"
- },
- "use": {
- "title": "Utiliser / Consommer",
- "location_label": "📍 Depuis où ?",
- "quantity_label": "Combien avez-vous utilisé ?",
- "change": "modifier",
- "partial_hint": "Ou précisez la quantité utilisée :",
- "partial_piece_hint": "Avez-vous utilisé seulement une partie ?",
- "piece": "pièce",
- "one_whole": "1 entier",
- "use_all": "🗑️ Tout utilisé / Terminé",
- "submit": "📤 Utiliser cette quantité",
- "available": "📦 Disponible :",
- "opened_badge": "OUVERT",
- "not_in_inventory": "⚠️ Produit absent de l'inventaire.",
- "expiry_warning": "⚠️ Utilisez en premier celui{loc} qui expire le {date} — {when} !",
- "expiry_warning_opened": "⚠️ Celui{loc} est ouvert depuis {when} — utilisez-le en premier !",
- "throw_title": "🗑️ Jeter le produit",
- "throw_all": "🗑️ Tout jeter ({qty})",
- "throw_qty_label": "Quelle quantité jeter ?",
- "throw_qty_hint": "ou entrez une quantité :",
- "throw_partial_btn": "🗑️ Jeter cette quantité",
- "when_expired": "périmé il y a {n} jours",
- "when_today": "expire aujourd'hui",
- "when_tomorrow": "expire demain",
- "when_days": "expire dans {n} jours",
- "toast_used": "📤 {qty} de {name} utilisé",
- "toast_bring": "🛒 Produit terminé → ajouté à Bring !",
- "toast_opened_finished": "🔓 Emballage ouvert de {name} terminé !",
- "disambiguation_hint": "Que voulez-vous dire par « tout fini » ?",
- "disambiguation_all": "🗑️ Tout finir ({qty})",
- "error_exceeds_stock": "⚠️ Vous ne pouvez pas utiliser plus que ce que vous avez disponible !",
- "use_all_confirm_title": "✅ Tout terminer",
- "use_all_confirm_msg": "Confirmez que vous avez terminé le produit :",
- "use_all_confirm_btn": "✅ Oui, terminé",
- "throw_all_confirm_title": "🗑️ Tout jeter",
- "throw_all_confirm_msg": "Voulez-vous vraiment jeter tout le produit ?",
- "throw_all_confirm_btn": "🗑️ Oui, jeter"
- },
- "product": {
- "title_new": "Nouveau produit",
- "title_edit": "Modifier le produit",
- "ai_fill": "📷 Prendre une photo et identifier avec l'IA",
- "ai_fill_hint": "L'IA remplira automatiquement les champs du produit",
- "name_label": "🏷️ Nom du produit *",
- "name_placeholder": "Ex. : Lait entier, Pâtes penne...",
- "brand_label": "🏢 Marque",
- "brand_placeholder": "Ex. : Barilla, Danone, Heinz...",
- "category_label": "📂 Catégorie",
- "unit_label": "📏 Unité de mesure",
- "default_qty_label": "🔢 Quantité par défaut",
- "conf_size_label": "📦 Chaque paquet contient :",
- "conf_size_placeholder": "ex. 300",
- "notes_label": "📝 Notes",
- "notes_placeholder": "Ex. : sans lactose, biologique, conserver au réfrigérateur après ouverture...",
- "barcode_label": "🔖 Code-barres",
- "barcode_placeholder": "Code-barres (si disponible)",
- "barcode_hint": "⚠️ Ajoutez le code-barres pour la prochaine fois, il suffira de scanner !",
- "submit": "💾 Enregistrer le produit",
- "name_required": "Entrez le nom du produit",
- "conf_size_required": "Précisez le contenu du paquet",
- "expiry_estimated": "Date de péremption estimée :",
- "scan_expiry": "Scanner la date de péremption",
- "expiry_hint": "📝 Vous pouvez modifier la date ou la scanner avec la caméra",
- "add_batch": "📦 + Lot avec une date différente",
- "package_info": "📦 Paquet : {info}",
- "edit_catalog": "⚙️ Modifier les infos produit (nom, marque, catégorie…)",
- "not_recognized": "⚠️ Produit non reconnu",
- "edit_info": "✏️ Modifier les informations",
- "modify_details": "MODIFIER\npéremption, emplacement…",
- "already_in_pantry": "📋 Déjà dans le garde-manger",
- "no_barcode": "Pas de code-barres",
- "unknown_product": "Produit non reconnu",
- "edit_name_brand": "Modifier nom/marque",
- "weight_label": "Poids",
- "origin_label": "Origine",
- "labels_label": "Labels",
- "select_variant": "Sélectionnez la variante exacte ou utilisez les données IA :"
- },
- "products": {
- "title": "📦 Tous les produits",
- "search_placeholder": "🔍 Rechercher un produit...",
- "empty": "Aucun produit dans la base de données.\nScannez un produit pour commencer !",
- "no_category": "Aucun produit dans cette catégorie"
- },
- "recipes": {
- "title": "🍳 Recettes",
- "generate": "✨ Générer une nouvelle recette",
- "archive_empty": "Aucune recette enregistrée. Générez votre première recette !",
- "dialog_title": "🍳 Recette",
- "dialog_desc": "Je vais générer une recette saine en utilisant les ingrédients du garde-manger, en priorisant les produits qui expirent.",
- "meal_label": "🕐 Quel repas ?",
- "persons_label": "👥 Pour combien de personnes ?",
- "meal_type_label": "🎯 Type de repas",
- "opt_fast": "⚡ Repas rapide",
- "opt_light": "🥗 Appétit léger",
- "opt_expiry": "⏰ Prioriser les produits qui expirent",
- "opt_healthy": "💚 Extra sain",
- "opt_opened": "📦 Prioriser les produits ouverts",
- "opt_zero_waste": "♻️ Zéro déchet",
- "generate_btn": "✨ Générer la recette",
- "loading_msg": "Préparation de votre recette...",
- "start_cooking": "👨🍳 Mode cuisine",
- "regenerate": "🔄 En générer une autre",
- "regen_choice_title": "Que veux-tu faire de cette recette ?",
- "regen_replace": "🔄 En générer une autre (ignorer celle-ci)",
- "regen_save_new": "💾 Sauvegarder dans l'archive et en générer une nouvelle",
- "close_btn": "✅ Fermer",
- "ingredients_title": "🧾 Ingrédients",
- "tools_title": "Matériel nécessaire",
- "steps_title": "👨🍳 Étapes",
- "no_steps": "Aucune étape disponible",
- "generate_error": "Erreur de génération",
- "persons_short": "pers.",
- "use_ingredient_title": "Utiliser l'ingrédient",
- "recipe_qty_label": "Recette",
- "from_where_label": "Depuis où ?",
- "amount_label": "Combien",
- "use_amount_btn": "Utiliser cette quantité",
- "use_all_btn": "Tout utiliser / Terminé",
- "packs_label": "Paquets",
- "quantity_in_total": "Quantité en {unit} (total : {total})",
- "packs_of_have": "Paquets de {size} (vous en avez {count})",
- "scale_wait_stable": "Attendez 10s de poids stable pour le remplissage automatique…",
- "ingredient_scaled_toast": "📦 Ingrédient déduit du garde-manger !",
- "finished_added_bring_toast": "🛒 Produit terminé → ajouté à Bring !",
- "load_error": "Erreur de chargement",
- "favorite": "Ajouter aux favoris",
- "unfavorite": "Retirer des favoris",
- "adjust_persons": "Personnes"
- },
- "shopping": {
- "title": "🛒 Liste de courses",
- "bring_loading": "Connexion à Bring !...",
- "bring_not_configured": "Bring ! n'est pas configuré. Ajoutez votre e-mail et mot de passe dans les paramètres.",
- "tab_to_buy": "🛍️ À acheter",
- "tab_forecast": "🧠 Prévision",
- "total_label": "💰 Total estimé",
- "section_to_buy": "🛍️ À acheter",
- "suggestions_title": "💡 Suggestions IA",
- "suggestions_add": "✅ Ajouter la sélection à Bring !",
- "search_prices": "🔍 Rechercher tous les prix",
- "suggest_btn": "Suggérer des achats",
- "smart_title": "🧠 Prévisions intelligentes",
- "smart_empty": "Aucune prévision disponible. Ajoutez des produits à votre garde-manger pour recevoir des prévisions intelligentes.",
- "smart_filter_all": "Tout",
- "smart_filter_critical": "🔴 Urgent",
- "smart_filter_high": "🟠 Bientôt",
- "smart_filter_medium": "🟡 Planifier",
- "smart_filter_low": "🟢 Prévision",
- "smart_add": "🛒 Ajouter la sélection à Bring !",
- "empty": "Liste de courses vide !\nUtilisez le bouton ci-dessous pour générer des suggestions.",
- "already_in_list": "🛒 \"{name}\" est déjà dans la liste de courses",
- "already_in_list_short": "ℹ️ Déjà dans la liste de courses",
- "add_prompt": "Voulez-vous l'ajouter à la liste de courses ?",
- "smart_already": "📊 Les prévisions achats prédisent déjà {name}",
- "all_searched": "Tous les produits ont déjà été recherchés. Utilisez 🔄 pour rechercher individuellement.",
- "search_complete": "Recherche terminée : {count} produits",
- "removed_sufficient": "🧹 {removed} produit(s) avec stock suffisant retiré(s) de la liste",
- "suggest_buy": "🛒 Acheter : {qty} {unit}",
- "suggest_buy_approx": "🛒 Au minimum : {qty} {unit}",
- "suggest_buy_tip": "Quantité suggérée basée sur vos 14 derniers jours de consommation",
- "suggest_buy_approx_tip": "Estimation minimale basée sur la consommation (achetez le format le plus proche)",
- "bring_badge": "🛒 Déjà sur Bring !",
- "add_urgent_toast": "🔴 {n} produit(s) urgent(s) automatiquement ajouté(s) à Bring !",
- "migration_done": "✅ {migrated} mis à jour, {skipped} déjà ok",
- "added_to_bring": "🛒 {n} produits ajoutés à Bring !",
- "added_to_bring_skip": "{n} déjà présents",
- "all_on_bring": "Tous les produits étaient déjà sur Bring !",
- "freq_high": "📈 Fréquent",
- "freq_regular": "📊 Régulier",
- "freq_occasional": "📉 Occasionnel",
- "out_of_stock": "Rupture de stock",
- "scan_toast": "📷 Scanner : {name}",
- "empty_category": "Aucun produit dans cette catégorie",
- "session_empty": "🛒 Aucun produit encore",
- "urgency_critical": "Urgent",
- "urgency_high": "Bientôt",
- "urgency_medium": "Planifier",
- "urgency_low": "Prévision",
- "urgency_medium_short": "Moyen",
- "urgency_low_short": "Ok",
- "tag_urgent": "🔴 Urgent",
- "tag_priority": "⭐ Priorité",
- "tag_check": "✅ Vérifier",
- "smart_already_predicted": "📊 Les prévisions achats prédisent déjà {name}{urgency}.",
- "item_removed": "✅ {name} retiré de la liste !",
- "urgency_spec_critical": "⚡ Urgent",
- "urgency_spec_high": "🟠 Bientôt",
- "bring_add_n": "Ajouter {n} à Bring !",
- "bring_add_selected": "Ajouter la sélection à Bring !",
- "bring_adding": "Ajout en cours...",
- "bring_added_one": "1 produit ajouté à Bring !",
- "bring_added_many": "{n} produits ajoutés à Bring !",
- "bring_skipped": "({n} déjà dans la liste)",
- "force_sync": "Forcer la synchronisation Bring !",
- "scan_target_label": "Vous cherchez",
- "scan_target_found": "Trouvé ! Retirer de la liste",
- "bring_add_one": "Ajouter 1 produit à Bring !",
- "bring_add_many": "Ajouter {n} produits à Bring !",
- "syncing": "Synchronisation…",
- "sync_done": "Synchronisation terminée",
- "price_searching": "Recherche en cours...",
- "search_action": "Rechercher",
- "open_action": "Ouvrir",
- "not_found": "Non trouvé",
- "search_price": "Rechercher le prix",
- "tap_to_scan": "Appuyez pour scanner",
- "tag_title": "Tag",
- "remove_title": "Retirer",
- "found_count": "{found}/{total} produits trouvés",
- "savings_offers": "· 🏷️ Vous économisez {amount}€ avec les offres",
- "searching_progress": "Recherche {current}/{total}...",
- "remove_error": "Erreur de suppression",
- "btn_fetch_prices": "Trouver les prix",
- "price_total_label": "💰 Total estimé :",
- "price_loading": "Recherche des prix…",
- "price_not_found": "prix n/d",
- "suggest_loading": "Analyse en cours...",
- "suggest_error": "Erreur de génération des suggestions",
- "priority_high": "Élevée",
- "priority_medium": "Moyenne",
- "priority_low": "Faible",
- "smart_last_update": "Mis à jour {time}",
- "names_already_updated": "Tous les noms sont déjà à jour"
- },
- "ai": {
- "title": "🤖 Identification IA",
- "capture": "📸 Prendre une photo",
- "retake": "🔄 Reprendre",
- "hint": "Prenez une photo du produit et l'IA essaiera de l'identifier",
- "identifying": "🤖 Identification du produit...",
- "no_api_key": "⚠️ Clé API Gemini non configurée.\nAjoutez GEMINI_API_KEY au fichier .env sur le serveur.",
- "fields_filled": "✅ Champs remplis par l'IA",
- "use_data": "✅ Utiliser les données IA",
- "use_data_no_barcode": "✅ Utiliser les données IA (sans code-barres)"
- },
- "log": {
- "title": "📒 Journal des opérations",
- "type_added": "Ajouté",
- "type_waste": "Jeté",
- "type_used": "Utilisé",
- "type_bring": "Ajouté à Bring !",
- "undone_badge": "Annulé",
- "undo_title": "Annuler cette opération",
- "load_error": "Erreur de chargement du journal",
- "empty": "Aucune opération enregistrée.",
- "undo_action_remove": "suppression de",
- "undo_action_restore": "réapprovisionnement de",
- "undo_confirm": "Annuler cette opération ?\n→ {action} {name}",
- "undo_success": "↩ Opération annulée pour {name}",
- "already_undone": "Opération déjà annulée",
- "too_old": "Impossible d'annuler des opérations de plus de 24 heures",
- "undo_error": "Erreur lors de l'annulation",
- "recipe_prefix": "Recette"
- },
- "chat": {
- "title": "Chef Gemini",
- "welcome": "Bonjour ! Je suis votre assistant cuisine",
- "welcome_desc": "Demandez-moi de préparer un jus, un snack, un plat rapide… Je connais votre garde-manger, vos appareils et vos préférences !",
- "suggestion_snack": "🍿 Snack rapide",
- "suggestion_juice": "🥤 Jus/Smoothie",
- "suggestion_light": "🥗 Quelque chose de léger",
- "suggestion_expiry": "⏰ Utiliser les produits qui expirent",
- "clear": "Nouvelle conversation",
- "placeholder": "Demandez quelque chose...",
- "cleared": "Chat effacé",
- "suggestion_snack_text": "Que puis-je préparer comme snack rapide ?",
- "suggestion_juice_text": "Faites-moi un jus ou un smoothie avec ce que j'ai",
- "suggestion_light_text": "J'ai faim mais je veux quelque chose de léger",
- "suggestion_expiry_text": "Qu'est-ce qui va bientôt expirer et comment l'utiliser ?",
- "transfer_to_recipes": "Transférer aux recettes",
- "transferring": "Transfert en cours...",
- "transferred": "Ajouté aux recettes !",
- "open_recipe": "Ouvrir la recette",
- "quick_recipe_prompt": "Suggérez une recette rapide POUR UNE PERSONNE en utilisant les produits qui expirent en premier ! Ignorez les congélateurs, concentrez-vous sur le réfrigérateur et le garde-manger."
- },
- "cooking": {
- "close": "Fermer",
- "tts_btn": "Lire à voix haute",
- "restart": "↺ Recommencer",
- "replay": "🔊 Rejouer",
- "timer": "⏱️ {time} · Minuterie",
- "prev": "◀ Précédent",
- "next": "Suivant ▶",
- "ingredient_used": "✔️ Déduit",
- "ingredient_use_btn": "📦 Utiliser",
- "ingredient_deduct_title": "Déduire du garde-manger",
- "timer_expired_tts": "Minuterie {label} terminée !",
- "timer_warning_tts": "Attention ! {label} : 10 secondes restantes !",
- "recipe_done_tts": "Recette terminée ! Bon appétit !",
- "expires_chip": "exp. {date}",
- "finish": "✅ Terminer",
- "step_fallback": "Étape {n}",
- "zerowaste_label": "♻️ Déchet",
- "zerowaste_tip_title": "Conseil zéro déchet"
- },
- "settings": {
- "title": "⚙️ Paramètres",
- "tab_api": "Clés API",
- "tab_bring": "Bring !",
- "tab_recipe": "Recettes",
- "tab_mealplan": "Plan hebdomadaire",
- "tab_appliances": "Appareils",
- "tab_spesa": "Courses en ligne",
- "tab_camera": "Caméra",
- "tab_security": "Sécurité",
- "tab_tts": "Voix (TTS)",
- "tab_language": "Langue",
- "tab_scale": "Balance connectée",
- "gemini": {
- "title": "🤖 Google Gemini IA",
- "hint": "Clé API pour l'identification des produits, les dates de péremption et les recettes.",
- "key_label": "Clé API Gemini"
- },
- "bring": {
- "title": "🛒 Liste de courses Bring !",
- "hint": "Identifiants pour l'intégration de la liste de courses Bring !",
- "email_label": "📧 E-mail Bring !",
- "password_label": "🔒 Mot de passe Bring !"
- },
- "price": {
- "title": "💰 Estimation des prix (IA)",
- "hint": "Afficher le coût estimé par produit dans la liste de courses à l'aide de l'IA.",
- "enabled_label": "Activer l'estimation des prix",
- "country_label": "🌍 Pays de référence",
- "currency_label": "💱 Devise",
- "update_label": "🔄 Actualiser les prix tous les",
- "update_suffix": "mois"
- },
- "recipe": {
- "title": "🍳 Préférences de recettes",
- "hint": "Configurez les options par défaut pour la génération de recettes.",
- "persons_label": "👥 Portions par défaut",
- "options_label": "🎯 Options de recette par défaut",
- "fast": "⚡ Repas rapide",
- "light": "🥗 Repas léger",
- "expiry": "⏰ Priorité péremption",
- "healthy": "💚 Extra sain",
- "opened": "📦 Priorité produits ouverts",
- "zerowaste": "♻️ Zéro déchet",
- "dietary_label": "🚫 Intolérances / Restrictions",
- "dietary_placeholder": "Ex. : sans gluten, sans lactose, végétarien..."
- },
- "mealplan": {
- "title": "📅 Plan de repas hebdomadaire",
- "hint": "Définissez le type de repas pour chaque jour. Il sera utilisé comme guide pour la génération de recettes.",
- "enabled": "✅ Activer le plan hebdomadaire",
- "legend": "🌤️ = Déjeuner · 🌙 = Dîner · Appuyez sur un badge pour le modifier.",
- "types_title": "📋 Types disponibles",
- "reset_btn": "↺ Restaurer les valeurs par défaut"
- },
- "appliances": {
- "title": "🔌 Appareils disponibles",
- "hint": "Indiquez les appareils que vous possédez. Ils seront pris en compte lors de la génération de recettes.",
- "new_placeholder": "Ex. : Machine à pain, Thermomix, Friteuse à air...",
- "quick_title": "Ajout rapide :",
- "oven": "🔥 Four",
- "microwave": "📡 Micro-ondes",
- "air_fryer": "🍟 Friteuse à air",
- "bread_maker": "🍞 Machine à pain",
- "bimby": "🤖 Thermomix/Cookeo",
- "mixer": "🌀 Robot pâtissier",
- "steamer": "♨️ Cuiseur vapeur",
- "pressure_cooker": "🫕 Cocotte-minute",
- "toaster": "🍞 Grille-pain",
- "blender": "🍹 Mixeur",
- "empty": "Aucun appareil ajouté"
- },
- "spesa": {
- "title": "🛍️ Courses en ligne",
- "hint": "Configurez le fournisseur de courses en ligne.",
- "provider_label": "🏪 Fournisseur",
- "email_label": "📧 E-mail",
- "password_label": "🔒 Mot de passe",
- "login_btn": "🔐 Connexion",
- "ai_prompt_label": "🤖 Prompt IA de sélection de produit",
- "ai_prompt_placeholder": "Instructions pour l'IA lors du choix entre plusieurs produits...",
- "ai_prompt_hint": "L'IA utilise ce prompt pour choisir le produit le plus approprié parmi les résultats. Laissez vide pour le comportement par défaut.",
- "configure_first": "Configurez d'abord les courses en ligne dans les paramètres",
- "missing_credentials": "Entrez l'e-mail et le mot de passe",
- "login_in_progress": "Connexion en cours...",
- "login_error_prefix": "Erreur :",
- "login_network_error_prefix": "Erreur réseau :",
- "login_success_default": "Connexion réussie !",
- "result_name_label": "Nom",
- "result_card_label": "Carte",
- "result_pickup_label": "Point de retrait",
- "result_points_label": "Points fidélité",
- "connected_relogin": "✅ Connecté — Se reconnecter",
- "connected_as": "Connecté en tant que {name}"
- },
- "camera": {
- "title": "📷 Caméra",
- "hint": "Choisissez la caméra à utiliser pour le scan de code-barres et l'identification IA.",
- "device_label": "📸 Caméra par défaut",
- "back": "📱 Arrière (par défaut)",
- "front": "🤳 Frontale",
- "devices_hint": "Si vous avez plusieurs caméras, vous pouvez en sélectionner une dans la liste ci-dessus après avoir accordé les permissions.",
- "detect_btn": "🔄 Détecter les caméras"
- },
- "security": {
- "title": "🔒 Certificat HTTPS",
- "hint": "Si le navigateur affiche l'erreur « Votre connexion n'est pas privée » (ERR_CERT_AUTHORITY_INVALID), vous devez installer le certificat CA sur l'appareil.",
- "download_btn": "📥 Télécharger le certificat CA",
- "token_title": "🔑 Token de paramètres",
- "token_label": "Token d'accès",
- "token_hint": "Si `SETTINGS_TOKEN` est configuré dans le `.env` du serveur, entrez le token ici avant de sauvegarder les paramètres. Laissez vide si non configuré.",
- "token_placeholder": "(vide = pas de protection)",
- "token_required_hint": "🔒 Ce serveur nécessite un token pour sauvegarder les paramètres.",
- "cert_instructions": "Instructions pour Chrome (Android) : 1. Téléchargez le certificat ci-dessus 2. Allez dans Paramètres → Sécurité & Confidentialité → Plus de paramètres de sécurité → Installer depuis le stockage 3. Sélectionnez le fichier EverShelf_CA.crt téléchargé 4. Choisissez « CA » et confirmez 5. Redémarrez Chrome
Instructions pour Chrome (PC) : 1. Téléchargez le certificat ci-dessus 2. Allez dans chrome://settings/certificates 3. Onglet « Autorités » → Importer → sélectionnez le fichier 4. Cochez « Approuver ce certificat pour identifier les sites web » 5. Redémarrez Chrome"
- },
- "tts": {
- "title": "🔊 Voix & TTS",
- "hint": "Configurez la synthèse vocale via une API REST externe. Les étapes de recette et les minuteries expirées seront envoyées à l'endpoint configuré.",
- "enabled": "✅ Activer la TTS",
- "engine_label": "⚙️ Moteur TTS",
- "engine_browser": "🔇 Navigateur (hors ligne, aucune configuration requise)",
- "engine_server": "🌐 Serveur externe (Home Assistant, API REST...)",
- "voice_label": "🗣️ Voix",
- "rate_label": "⚡ Vitesse",
- "pitch_label": "🎵 Tonalité",
- "url_label": "🌐 URL de l'endpoint",
- "method_label": "📡 Méthode HTTP",
- "auth_label": "🔐 Authentification",
- "auth_bearer": "Bearer Token",
- "auth_custom": "En-tête personnalisé",
- "auth_none": "Aucune",
- "token_label": "🔑 Bearer Token",
- "custom_header_name": "📋 Nom de l'en-tête",
- "custom_header_value": "📋 Valeur de l'en-tête",
- "content_type_label": "📄 Content-Type",
- "payload_key_label": "🗝️ Champ texte dans le payload",
- "payload_key_hint": "Nom du champ JSON qui contiendra le texte à lire (ex. : message, text).",
- "extra_fields_label": "➕ Champs supplémentaires (JSON)",
- "extra_fields_placeholder": "{\"entity_id\": \"media_player.salon\"}",
- "extra_fields_hint": "Champs supplémentaires à inclure dans le payload, en format JSON. Laissez vide si non nécessaire.",
- "test_btn": "🔊 Envoyer une voix de test",
- "voices_loading": "Chargement des voix…",
- "voice_not_supported": "Voix non supportée par ce navigateur",
- "voices_none": "Aucune voix disponible sur cet appareil",
- "voices_hint": "Les voix disponibles dépendent du système d'exploitation et du navigateur. Appuyez sur ↺ si la liste ne se charge pas.",
- "url_missing": "⚠️ URL de l'endpoint manquante.",
- "test_sending": "⏳ Envoi…",
- "test_ok": "✅ Réponse {code} — vérifiez que le haut-parleur a parlé.",
- "heard_question": "Avez-vous entendu la voix ?",
- "heard_yes": "Oui, je l'ai entendu",
- "heard_no": "Non, je n'ai rien entendu",
- "test_ok_kiosk": "TTS fonctionne.",
- "test_fail_steps": "Vérifiez : 1) le volume média n'est pas 0 ; 2) Google Text-to-Speech est installé et mis à jour ; 3) le pack vocal français est téléchargé dans les paramètres TTS Android."
- },
- "language": {
- "title": "🌐 Langue",
- "hint": "Sélectionnez la langue de l'interface.",
- "label": "🌐 Langue",
- "restart_notice": "La page sera rechargée pour appliquer la nouvelle langue."
- },
- "screensaver": {
- "label": "Activer l'économiseur d'écran",
- "card_title": "🌙 Économiseur d'écran",
- "card_hint": "Affiche une horloge avec des informations utiles après 5 minutes d'inactivité. Désactivé par défaut.",
- "timeout_1": "1 minute",
- "timeout_2": "2 minutes",
- "timeout_5": "5 minutes",
- "timeout_10": "10 minutes",
- "timeout_15": "15 minutes",
- "timeout_30": "30 minutes",
- "timeout_60": "1 heure",
- "start_after": "⏱️ Démarrer après"
- },
- "scale": {
- "title": "⚖️ Balance connectée",
- "hint": "Connectez une balance Bluetooth via la passerelle Android pour lire automatiquement le poids.",
- "tab": "Balance connectée",
- "enabled": "✅ Activer la balance connectée",
- "url_label": "🌐 URL de la passerelle WebSocket",
- "url_placeholder": "ws://192.168.1.x:8765",
- "url_hint": "URL affichée par l'application Android (même réseau Wi-Fi). Ex. :",
- "test_btn": "🔗 Tester la connexion",
- "download_btn": "📥 Télécharger la passerelle Android (APK)",
- "download_hint": "Application Android qui connecte votre balance BLE et EverShelf.",
- "download_sub": "Source : evershelf-scale-gateway/ dans la racine du projet",
- "live_weight": "poids en temps réel",
- "auto_reconnect": "🔁 Reconnexion : automatique",
- "kiosk_title": "📡 Balance BLE intégrée dans le kiosque",
- "kiosk_hint": "La balance est directement gérée par la passerelle BLE interne du kiosque. Pour associer un nouvel appareil, utilisez l'assistant de configuration.",
- "kiosk_reconfigure": "🔄 Reconfigurer la balance BLE",
- "ble_protocols": "
🔌 Protocoles BLE supportés :
Bluetooth SIG Weight Scale (0x181D)
Bluetooth SIG Body Composition (0x181B) — poids, graisse, IMC
Xiaomi Mi Body Composition Scale 2
Générique — heuristique automatique pour 100+ modèles
"
- },
- "kiosk": {
- "hint": "Transformez une tablette Android en panneau EverShelf permanent avec passerelle BLE intégrée.",
- "download_btn": "📥 Télécharger EverShelf Kiosk (APK)",
- "download_sub": "Mode kiosque plein écran + passerelle de balance intégrée. Source : evershelf-kiosk/",
- "native_title": "Configuration du kiosque",
- "native_hint": "URL du serveur, balance BLE, économiseur d'écran et assistant de configuration.",
- "native_btn": "Ouvrir la configuration du kiosque",
- "native_tap_hint": "Appuyez sur le bouton engrenage en haut à droite",
- "native_update_hint": "Mettez à jour l'application kiosque pour utiliser cette fonctionnalité",
- "update_title": "Mise à jour du kiosque",
- "check_updates_btn": "🔍 Vérifier les mises à jour",
- "needs_update": "⚠️ Le kiosque installé ne supporte pas cette fonctionnalité. Mettez à jour l'application kiosque pour l'activer."
- },
- "saved": "✅ Configuration enregistrée !",
- "saved_local": "✅ Configuration enregistrée localement",
- "saved_local_error": "⚠️ Enregistré localement, erreur serveur : {error}",
- "theme": {
- "title": "🌙 Apparence",
- "hint": "Choisissez le thème de l'interface.",
- "label": "🌙 Thème",
- "off": "☀️ Clair",
- "on": "🌙 Sombre",
- "auto": "🔄 Automatique (système)"
- },
- "zerowaste": {
- "card_title": "♻️ Conseils zéro déchet",
- "card_hint": "Pendant la cuisson, affichez des conseils pour réutiliser les déchets produits à chaque étape (épluchures, eau de cuisson, etc.). Désactivé par défaut.",
- "label": "Afficher les conseils pendant la cuisson"
- },
- "backup": {
- "tab": "Sauvegarde",
- "local_title": "Sauvegarde locale",
- "local_hint": "Instantané quotidien de la base de données. Configurez le nombre de jours de rétention.",
- "enabled": "Activer la sauvegarde automatique quotidienne",
- "retention_days": "Rétention (jours)",
- "retention_info": "Les sauvegardes sont conservées pendant",
- "backup_now": "Sauvegarder maintenant",
- "backing_up": "Sauvegarde en cours…",
- "backed_up": "Sauvegarde terminée",
- "backup_error": "Erreur de sauvegarde",
- "last_backup": "Dernière sauvegarde",
- "no_backup_yet": "Aucune sauvegarde créée",
- "list_empty": "Aucune sauvegarde disponible",
- "restore_btn": "Restaurer",
- "restore_confirm": "Restaurer la sauvegarde",
- "delete_btn": "Supprimer",
- "delete_confirm": "Supprimer la sauvegarde",
- "gdrive_title": "Google Drive",
- "gdrive_hint": "Sauvegardez automatiquement sur Google Drive via OAuth 2.0. Aucune bibliothèque externe requise.",
- "gdrive_enabled": "Activer la sauvegarde Google Drive",
- "gdrive_folder_id": "ID du dossier Drive",
- "gdrive_folder_id_hint": "Copiez l'ID depuis l'URL du dossier Drive : …/folders/ID",
- "gdrive_retention_days": "Rétention Drive (jours, 0=tout garder)",
- "gdrive_test": "Tester la connexion",
- "gdrive_ok": "Connexion réussie !",
- "gdrive_error": "Échec de la connexion",
- "gdrive_push_now": "Téléverser sur Drive maintenant",
- "gdrive_pushing": "Téléversement en cours…",
- "gdrive_pushed": "Téléversé sur Drive",
- "gdrive_wizard_hint": "Optionnel : sauvegarde quotidienne automatique sur Google Drive via OAuth 2.0.",
- "gdrive_skip": "Passer — configurer plus tard dans Paramètres",
- "gdrive_client_id": "Client ID",
- "gdrive_client_secret": "Client Secret",
- "gdrive_redirect_uri_hint": "Ajoute http://localhost comme URI de redirection autorisé dans la Google Cloud Console. Fonctionne sur n'importe quel serveur, même sans domaine public.",
- "gdrive_code_title": "Coller l'URL ou le code d'autorisation",
- "gdrive_code_hint": "Après autorisation, le navigateur ouvre http://localhost et peut afficher une erreur de connexion — c'est normal. Copie l'URL dans la barre d'adresse (ex. http://localhost/?code=4%2F0A...) et colle-la ici.",
- "gdrive_code_submit": "Confirmer",
- "gdrive_code_empty": "Coller d'abord l'URL ou le code d'autorisation",
- "gdrive_redirect_uri_label": "URI de redirection (ajouter dans Google Cloud Console) :",
- "gdrive_oauth_authorize": "Autoriser avec Google",
- "gdrive_oauth_authorized": "Autorisé",
- "gdrive_oauth_not_authorized": "Pas encore autorisé",
- "gdrive_oauth_window_opened": "Fenêtre ouverte — autorisez et revenez ici",
- "gdrive_oauth_how_to": "Configurer OAuth 2.0 (étape par étape)",
- "gdrive_oauth_steps": "
Activez l’API Google Drive : API et services → Activer les API → Google Drive API
Allez dans API et services → Identifiants → Créer des identifiants → ID client OAuth
Type d’application : Application Web ; ajoutez l’URL affichée ci-dessous comme URI de redirection autorisé
Copiez le Client ID et le Client Secret dans les champs ci-dessus et enregistrez
Cliquez sur Autoriser avec Google : connectez-vous et accordez l’accès
La fenêtre se ferme automatiquement une fois terminé et les sauvegardes sont prêtes
"
- },
- "shopping": {
- "tab": "Liste de courses",
- "title": "Liste de courses",
- "hint": "Configurez la liste de courses intégrée ou connectez Bring!.",
- "enable_label": "Activer la liste de courses",
- "mode_label": "Fournisseur",
- "mode_internal": "Intégré (sans Bring!)",
- "mode_bring": "Bring! (application externe)",
- "bring_section_title": "Configuration Bring!",
- "ai_section_title": "Assistance IA",
- "smart_suggestions_label": "Suggestions IA",
- "forecast_label": "Prévision des produits bientôt épuisés",
- "auto_add_label": "Ajouter automatiquement quand",
- "auto_add_suffix": "restant en stock (0 = seulement quand épuisé)"
- },
- "ha": {
- "tab": "Home Assistant",
- "title": "Home Assistant",
- "hint": "Connectez EverShelf à Home Assistant pour les automations, les notifications push et les capteurs REST.",
- "enabled": "Activer l'intégration Home Assistant",
- "connection_title": "Connexion",
- "url_label": "URL Home Assistant",
- "url_placeholder": "http://192.168.1.50:8123",
- "url_hint": "URL de base de votre instance Home Assistant.",
- "token_label": "Jeton d'accès longue durée",
- "token_hint": "Générez depuis Profil HA → Sécurité → Jetons d'accès longue durée.",
- "token_placeholder": "eyJhbGci...",
- "token_saved": "Jeton enregistré (masqué pour des raisons de sécurité)",
- "test_btn": "Tester la connexion",
- "test_ok": "Connecté à {version}",
- "test_fail": "Connexion échouée : {error}",
- "test_bad_token": "HA accessible mais le jeton est invalide",
- "testing": "Test en cours…",
- "error_no_url": "Veuillez d'abord saisir l'URL de Home Assistant.",
- "tts_title": "TTS sur enceinte connectée",
- "tts_hint": "Lisez les étapes de recette sur un media player Home Assistant.",
- "tts_entity_label": "Entity ID du lecteur multimédia",
- "tts_entity_placeholder": "media_player.salon",
- "tts_entity_hint": "Entity ID du lecteur multimédia HA. Disponible dans HA : Outils développeur → États.",
- "tts_platform_label": "Plateforme TTS",
- "tts_platform_speak": "tts.speak (recommandé)",
- "tts_platform_notify": "notify.* (service de notification)",
- "tts_apply_btn": "Appliquer le preset HA à l'onglet TTS",
- "tts_apply_hint": "Pré-remplit l'onglet TTS avec l'URL et le jeton de Home Assistant.",
- "tts_preset_applied": "Preset HA appliqué à l'onglet TTS.",
- "webhook_title": "Automations Webhook",
- "webhook_hint": "Envoyez des données à Home Assistant lors d'événements dans le garde-manger.",
- "webhook_id_label": "ID Webhook",
- "webhook_id_placeholder": "evershelf_webhook_abc123",
- "webhook_id_hint": "ID du webhook créé dans HA. Copiez depuis : HA → Paramètres → Automations → Créer → Déclencheur Webhook.",
- "webhook_events_label": "Notifier pour ces événements",
- "event_expiry": "Produits expirant bientôt (quotidien)",
- "event_shopping": "Article ajouté à la liste de courses",
- "event_stock": "Niveau de stock mis à jour",
- "expiry_days_label": "Préavis d'expiration (jours)",
- "expiry_days_hint": "Envoyer l'alerte d'expiration N jours avant la date d'expiration.",
- "webhook_help": "Dans HA : Paramètres → Automations → Créer → Déclencheur : Webhook → copier l'ID généré.",
- "notify_title": "Notifications push",
- "notify_hint": "Envoyez des notifications push sur votre téléphone via un service notify de Home Assistant.",
- "notify_service_label": "Service notify",
- "notify_service_placeholder": "notify.mobile_app_mon_telephone",
- "notify_service_hint": "Nom du service notify HA. Laissez vide pour désactiver.",
- "sensor_title": "Capteurs REST",
- "sensor_hint": "Ajoutez à configuration.yaml pour créer des capteurs EverShelf dans Home Assistant.",
- "sensor_copy_btn": "Copier le YAML",
- "sensor_copied": "YAML copié dans le presse-papiers !",
- "save_btn": "Enregistrer les paramètres HA",
- "ha_hint": "Si vous utilisez Home Assistant, utilisez l'onglet Home Assistant pour configurer TTS, webhooks et capteurs."
- }
- },
- "expiry": {
- "today": "AUJOURD'HUI",
- "tomorrow": "Demain",
- "days": "{days} jours",
- "expired_days": "il y a {days}j",
- "expired_yesterday": "Hier",
- "expired_today": "Aujourd'hui",
- "badge_today": "⚠️ Expire aujourd'hui !",
- "badge_tomorrow": "⏰ Demain",
- "badge_tomorrow_long": "⏰ Expire demain",
- "badge_days": "⏰ {n} jours",
- "badge_expired_ago": "⚠️ Périmé il y a {n}j",
- "badge_expired": "⛔ Périmé !",
- "badge_stable": "✅ Stable",
- "badge_expiring_short": "⏰ Exp. dans {n}j",
- "badge_ok_still": "✅ Encore {n}j",
- "badge_expires_red": "🔴 Exp. dans {n}j",
- "badge_expires_yellow": "🟡 Exp. dans {n}j",
- "badge_expired_bare": "⚠️ Périmé",
- "badge_expires_warn": "⚠️ Exp. dans {n}j",
- "badge_days_left": "⏳ ~{n}j restants",
- "days_approx": "~{n} jours",
- "weeks_approx": "~{n} semaines",
- "months_approx": "~{n} mois",
- "years_approx": "~{n} ans",
- "expired_today_long": "Périmé aujourd'hui",
- "expired_ago_long": "Périmé il y a {n} jours",
- "expired_suffix": "— Périmé !",
- "expired_suffix_ok": "— Périmé (encore ok)",
- "expired_suffix_warning": "— Périmé (vérifier d'abord)",
- "opened_ago_long": "Ouvert il y a {n} jours",
- "opened_today_long": "Ouvert aujourd'hui",
- "opened_suffix": "— Ouvert depuis trop longtemps !",
- "opened_suffix_ok": "— Ouvert (encore ok)",
- "opened_suffix_warning": "— Ouvert (vérifier d'abord)",
- "days_compact": "{n}j",
- "badge_check_soon": "Vérifier prochainement"
- },
- "status": {
- "ok": "OK",
- "check": "Vérifier",
- "discard": "Jeter",
- "tip_freezer_ok": "Au congélateur : encore sûr (~{n}j de marge)",
- "tip_freezer_check": "Au congélateur depuis longtemps, peut avoir perdu en qualité. Consommer rapidement",
- "tip_freezer_danger": "Au congélateur trop longtemps, risque de brûlure de congélation et de dégradation",
- "tip_highRisk_check": "Périmé récemment, vérifiez l'odeur et l'aspect avant de consommer",
- "tip_highRisk_danger": "Produit périssable périmé : jeter pour la sécurité",
- "tip_medRisk_check1": "Vérifiez l'aspect et l'odeur avant de consommer",
- "tip_medRisk_check2": "Périmé depuis un moment, vérifiez soigneusement avant utilisation",
- "tip_medRisk_danger": "Trop longtemps depuis la péremption, mieux vaut jeter",
- "tip_lowRisk_ok": "Produit longue conservation, encore sûr à consommer",
- "tip_lowRisk_check": "Périmé depuis plus d'un mois, vérifiez l'intégrité de l'emballage",
- "tip_lowRisk_danger": "Périmé depuis trop longtemps, mieux vaut ne pas risquer"
- },
- "toast": {
- "product_saved": "Produit enregistré !",
- "product_created": "Produit créé !",
- "product_updated": "✅ Produit mis à jour !",
- "product_removed": "Produit supprimé",
- "updated": "Mis à jour !",
- "quantity_confirmed": "✓ Quantité confirmée",
- "added_to_inventory": "✅ {name} ajouté !",
- "removed_from_list": "✅ {name} retiré de la liste !",
- "removed_from_list_short": "Retiré de la liste",
- "added_to_shopping": "🛒 Ajouté à la liste de courses !",
- "removed_from_shopping": "🛒 Retiré de la liste de courses",
- "finished_to_bring": "🛒 Produit terminé → ajouté à Bring !",
- "thrown_away": "🗑️ {name} jeté !",
- "thrown_away_partial": "🗑️ {qty} {unit} de {name} jeté(s)",
- "finished_all": "📤 {name} terminé !",
- "product_finished_confirmed": "✅ Supprimé — ajoutez-le à nouveau lors du réapprovisionnement",
- "appliance_added": "Appareil ajouté",
- "item_added": "{name} ajouté"
- },
- "antiwaste": {
- "title": "🌱 Rapport anti-gaspi",
- "grade_label": "Note",
- "you": "Vous",
- "avg_label": "Moy.",
- "better": "🎉 Vous gaspillez {diff}% de moins que la moyenne {country} !",
- "worse": "⚠️ Vous gaspillez plus que la moyenne {country}. Des progrès sont possibles !",
- "on_par": "→ Vous êtes dans la moyenne {country}. Vous pouvez faire mieux !",
- "saved_money": "~{amount}/mois économisé",
- "saved_meals": "~{n} repas sauvés",
- "saved_co2": "{n} kg CO₂ évités",
- "trend_title": "Tendance (3 derniers mois)",
- "months_ago_2": "-60 jours",
- "months_ago_1": "-30 jours",
- "this_month": "Maintenant",
- "country_it": "Moy. italienne",
- "country_de": "Moy. allemande",
- "country_en": "Moy. américaine",
- "source": "Sources : REDUCE, Eurostat, USDA 2021",
- "live_on": "Données en direct",
- "live_off": "Hors ligne",
- "meals": "repas",
- "annual_info": "📅 Vous ~{you} kg/an · moy. ~{avg} kg/an",
- "badge_rate": "taux de perte",
- "badge_saved_money": "économisé vs moy.",
- "badge_wasted": "articles perdus",
- "badge_better": "moins que la moy."
- },
- "error": {
- "generic": "Erreur",
- "network": "Erreur réseau",
- "no_api_key": "Configurez la clé API dans les paramètres",
- "loading": "Erreur de chargement du produit",
- "not_found": "Produit introuvable",
- "not_found_manual": "Produit introuvable. Entrez-le manuellement.",
- "search": "Erreur de recherche. Réessayez.",
- "search_short": "Erreur de recherche",
- "save": "Erreur d'enregistrement",
- "connection": "Erreur de connexion",
- "camera": "Impossible d'accéder à la caméra",
- "bring_add": "Erreur d'ajout à Bring !",
- "bring_connection": "Erreur de connexion à Bring !",
- "identification": "Erreur d'identification",
- "ai_quota": "Quota IA épuisé. Réessayez dans quelques minutes.",
- "barcode_empty": "Entrez un code-barres",
- "barcode_format": "Le code-barres ne doit contenir que des chiffres (4-14 chiffres)",
- "min_chars": "Tapez au moins 2 caractères",
- "not_in_inventory": "Produit absent de l'inventaire",
- "appliance_exists": "L'appareil existe déjà",
- "already_exists": "Existe déjà",
- "network_retry": "Erreur de connexion. Réessayez.",
- "select_items": "Sélectionnez au moins un produit",
- "server_offline": "Connexion au serveur perdue",
- "server_restored": "Connexion au serveur rétablie",
- "server_retry": "Réessayer",
- "unknown": "Erreur inconnue",
- "prefix": "Erreur",
- "no_inventory_entry": "Aucune entrée d'inventaire trouvée",
- "offline_title": "Aucune connexion",
- "offline_subtitle": "L'app ne peut pas atteindre le serveur. Vérifiez votre connexion Wi-Fi.",
- "offline_checking": "Vérification de la connexion…",
- "offline_restored": "Connexion rétablie !",
- "offline_continue": "Continuer en mode hors ligne",
- "offline_reading_cache": "Lecture depuis le cache local",
- "offline_ops_pending": "{n} opérations en attente",
- "offline_synced": "{n} opérations synchronisées",
- "offline_ai_disabled": "Indisponible hors ligne",
- "offline_cache_ready": "Offline — {n} produits en cache"
- },
- "confirm_placeholder_search": null,
- "confirm": {
- "remove_item": "Voulez-vous vraiment supprimer ce produit de l'inventaire ?",
- "kiosk_exit": "Quitter le mode kiosque ?",
- "cancel": "Annuler",
- "proceed": "Confirmer",
- "discard_one": "Jeter 1 pièce"
- },
- "location": {
- "dispensa": "Garde-manger",
- "frigo": "Réfrigérateur",
- "freezer": "Congélateur"
- },
- "edit": {
- "title": "Modifier {name}",
- "unknown_hint": "Entrez le nom du produit et les informations",
- "label_name": "🏷️ Nom du produit",
- "choose_location_title": "Quel emplacement ?",
- "choose_location_hint": "Choisissez l'emplacement à modifier :",
- "confirm_large_qty": "Vous définissez la quantité à {qty} {unit}. Cela semble inhabituellement élevé. Confirmer ?"
- },
- "screensaver": {
- "recipe_btn": "Recettes",
- "scan_btn": "Scanner un produit"
- },
- "days": {
- "mon": "Lundi",
- "tue": "Mardi",
- "wed": "Mercredi",
- "thu": "Jeudi",
- "fri": "Vendredi",
- "sat": "Samedi",
- "sun": "Dimanche",
- "mon_short": "Lun",
- "tue_short": "Mar",
- "wed_short": "Mer",
- "thu_short": "Jeu",
- "fri_short": "Ven",
- "sat_short": "Sam",
- "sun_short": "Dim"
- },
- "meal_types": {
- "lunch": "Déjeuner",
- "dinner": "Dîner",
- "colazione": "Petit-déjeuner",
- "merenda": "Goûter",
- "dolce": "Dessert",
- "succo": "Jus de fruits",
- "pranzo": "Déjeuner",
- "cena": "Dîner"
- },
- "scale": {
- "status_connected": "Balance connectée",
- "status_searching": "Passerelle connectée, attente de la balance…",
- "status_disconnected": "Passerelle de balance inaccessible",
- "status_error": "Erreur de connexion à la passerelle",
- "not_connected": "Passerelle de balance non connectée",
- "read_btn": "⚖️ Lire depuis la balance",
- "reading_title": "Lecture de la balance",
- "place_on_scale": "Posez le produit sur la balance…",
- "waiting_stable": "Le poids sera capturé automatiquement une fois la lecture stable.",
- "no_url": "Entrez l'URL de la passerelle",
- "testing": "⏳ Test de connexion…",
- "connected_ok": "Connexion à la passerelle réussie !",
- "timeout": "Délai dépassé : pas de réponse de la passerelle",
- "error_connect": "Impossible de se connecter à la passerelle",
- "tab": "Balance connectée",
- "low_weight": "Poids < 10 g · entrez manuellement\n(la lecture automatique nécessite au moins 10 g)",
- "density_hint": "(densité {density} g/ml)",
- "ml_hint": "(sera converti en ml)",
- "weight_detected": "Poids détecté — attendez 10s de stabilité…",
- "weight_too_low": "Poids trop faible — attente…",
- "stable": "✓ Stable",
- "auto_confirm": "✅ {val} {unit} — confirmation automatique dans 5s (appuyez pour annuler)",
- "cancelled_replace": "Annulé — replacez l'ingrédient sur la balance pour reprendre"
- },
- "prediction": {
- "expected_qty": "Attendu : {expected} {unit}",
- "actual_qty": "Actuel : {actual} {unit}",
- "check_suggestion": "Vérifiez ou pesez la quantité restante"
- },
- "date": {
- "today": "📅 Aujourd'hui",
- "yesterday": "📅 Hier"
- },
- "scanner": {
- "title_barcode": "🔖 Scanner le code-barres",
- "barcode_hint": "Cadrez le code-barres du produit",
- "barcode_manual_placeholder": "Ou entrez manuellement...",
- "barcode_use_btn": "✅ Utiliser ce code",
- "ai_identifying": "🤖 Identification du produit...",
- "ai_analyzing": "🤖 Analyse IA en cours...",
- "product_label_hint": "Cadrez l'étiquette du produit",
- "expiry_label_hint": "Cadrez la date de péremption imprimée sur le produit",
- "capture_btn": "📸 Capturer",
- "capture_photo_btn": "📸 Prendre une photo",
- "retake_btn": "🔄 Reprendre",
- "camera_error_hint": "Assurez-vous d'utiliser HTTPS et d'avoir accordé les permissions caméra. Vous pouvez entrer le code-barres manuellement ou utiliser l'identification IA.",
- "no_barcode": "Pas de code-barres",
- "save_new_btn": "🆕 Aucun de ceux-ci — enregistrer comme nouveau"
- },
- "lowstock": {
- "title": "⚠️ Stock faible !",
- "message": "{name} est presque épuisé — il ne reste que {qty}.",
- "question": "Voulez-vous l'ajouter à la liste de courses ?",
- "yes": "🛒 Oui, ajouter à Bring !",
- "no": "Non, pour l'instant ça va"
- },
- "move": {
- "title": "📦 Déplacer le reste ?",
- "question": "Voulez-vous déplacer {thing} de {name} vers un autre emplacement ?",
- "question_short": "Voulez-vous déplacer {thing} vers un autre emplacement ?",
- "thing_opened": "l'emballage ouvert",
- "thing_rest": "le reste",
- "stay_btn": "Non, rester dans {location}",
- "moved_toast": "📦 Emballage ouvert déplacé vers {location}",
- "vacuum_restore": "🫙 Restaurer sous vide",
- "vacuum_seal_rest": "🔒 Mettre le reste sous vide"
- },
- "nova": {
- "1": "Non transformé",
- "2": "Ingrédient culinaire",
- "3": "Transformé",
- "4": "Ultra-transformé"
- },
- "meal_plan_types": {
- "pasta": "Pâtes",
- "riso": "Riz",
- "carne": "Viande",
- "pesce": "Poisson",
- "legumi": "Légumineuses",
- "uova": "Œufs",
- "formaggio": "Fromage",
- "pizza": "Pizza",
- "affettati": "Charcuterie",
- "verdure": "Légumes",
- "zuppa": "Soupe",
- "insalata": "Salade",
- "pane": "Pain/Sandwich",
- "dolce": "Dessert",
- "libero": "Libre"
- },
- "meal_sub": {
- "dolce_torta": "Gâteau",
- "dolce_crema": "Crème / Pudding",
- "dolce_crumble": "Crumble / Tarte",
- "dolce_biscotti": "Biscuits / Pâtisseries",
- "dolce_frutta": "Dessert aux fruits",
- "succo_dolce": "Sucré / Fruité",
- "succo_energizzante": "Énergisant",
- "succo_detox": "Détox / Vert",
- "succo_rinfrescante": "Rafraîchissant",
- "succo_vitaminico": "Vitaminé / Agrumes"
- },
- "meal_plan": {
- "reset_success": "Plan hebdomadaire réinitialisé",
- "not_available": "non disponible dans le garde-manger",
- "suggested_by": "suggéré par le plan hebdomadaire"
- },
- "nutrition": {
- "title": "🥗 Analyse alimentaire",
- "score_excellent": "😄 Excellent",
- "score_good": "🙂 Bien",
- "score_improve": "😬 Améliorable",
- "label_health": "🌿 Santé",
- "label_variety": "🎨 Variété",
- "label_fresh": "❄️ Frais",
- "source": "Basé sur {n} produits dans votre garde-manger · EverShelf",
- "products_count": "produits",
- "today_title": "🥗 Votre garde-manger aujourd'hui",
- "products_n": "{n} produits",
- "macros_title": "Macronutriments estimés",
- "macros_proteins": "Protéines",
- "macros_carbs": "Glucides",
- "macros_fat": "Lipides",
- "macros_fiber": "Fibres",
- "macros_source": "Estimation basée sur {n} produits en stock"
- },
- "facts": {
- "greeting_morning": "Bonjour",
- "greeting_afternoon": "Bon après-midi",
- "greeting_evening": "Bonsoir",
- "pantry_waiting": "{greeting} ! Votre garde-manger vous attend.",
- "expired_one": "Vous avez 1 produit périmé dans votre garde-manger. Vérifiez-le !",
- "expired_many": "Vous avez {n} produits périmés dans votre garde-manger. Vérifiez-les !",
- "expired_list": "Produits périmés : {names}",
- "expired_list_more": "et {n} autres",
- "freezer_expired_ok": "{name} est périmé, mais étant au congélateur il peut encore être bon ! Vérifiez.",
- "freezer_expired_old": "{name} au congélateur est périmé depuis trop longtemps. Mieux vaut le jeter.",
- "fridge_expired_one": "Vous avez 1 produit périmé dans le réfrigérateur !",
- "fridge_expired_many": "Vous avez {n} produits périmés dans le réfrigérateur !",
- "expiring_today": "{name} expire aujourd'hui ! Utilisez-le immédiatement.",
- "expiring_tomorrow": "{name} expire demain. Planifiez !",
- "expiring_days": "{name} expire dans {days} jours.",
- "expiring_many": "Vous avez {n} produits qui expirent bientôt.",
- "expiring_this_week": "{n} produits expirent cette semaine. Planifiez vos repas en conséquence !",
- "expiring_item_loc": "{name} ({loc}) expire dans {days} {dayslabel}.",
- "expiring_this_month": "{n} produits expireront ce mois-ci.",
- "shopping_add": "Ajouter à la liste : {names} 🛒",
- "shopping_more": "et {n} autres",
- "shopping_empty": "Liste de courses vide. Tout est en stock ! ✅",
- "in_fridge": "Dans le réfrigérateur : {name}.",
- "in_freezer": "Dans le congélateur : {name}. N'oubliez pas !",
- "top_category": "Catégorie principale : {icon} {cat} avec {n} produits.",
- "cat_meat": "Vous avez {n} produits carnés. 🥩",
- "cat_dairy": "Vous avez {n} produits laitiers chez vous. 🥛",
- "cat_veggies": "Vous avez {n} types de légumes. Super pour la santé ! 🥬",
- "cat_fruit": "Vous avez {n} types de fruits. 🍎",
- "cat_drinks": "Vous avez {n} boissons disponibles. 🥤",
- "cat_frozen": "Vous avez {n} articles surgelés. ❄️",
- "cat_pasta": "Vous avez {n} types de pâtes. 🍝 Et si on faisait une carbonara ?",
- "cat_canned": "Vous avez {n} conserves dans le garde-manger. 🥫",
- "cat_snacks": "Vous avez {n} snacks. Résistez à la tentation ! 🍪",
- "cat_condiments": "Vous avez {n} condiments disponibles. 🧂",
- "item_random": "Le saviez-vous ? Vous avez {name} dans {loc}.",
- "item_qty": "{name} : vous en avez {qty}.",
- "no_expiry_count": "{n} produits n'ont pas de date de péremption.",
- "furthest_expiry": "Le produit avec la date de péremption la plus lointaine est {name} : {months} mois.",
- "high_qty": "Vous avez un beau stock de {name} : {qty} !",
- "low_qty_item": "{name} est presque épuisé. L'ajouter à votre liste de courses ?",
- "low_qty_count": "{n} produits sont presque épuisés.",
- "morning_bread": "Bonjour ! Vous avez du pain pour le petit-déjeuner. 🍞",
- "morning_milk": "Y a-t-il du lait dans le réfrigérateur pour un cappuccino ? ☕🥛",
- "morning_fruit": "Bonjour ! Des fruits frais sont un excellent début de journée. 🍎",
- "noon_pasta": "C'est l'heure du déjeuner… Et si on faisait un bon bol de pâtes ? 🍝",
- "noon_salad": "Une salade fraîche pour le déjeuner ? Vous avez {n} légumes ! 🥗",
- "evening_meat": "Pour le dîner, vous pourriez utiliser la viande que vous avez. 🥩",
- "evening_fish": "Et si on mangeait du poisson ce soir ? 🐟",
- "evening_expiring": "Vous avez {n} produits qui expirent cette semaine — utilisez-les ce soir !",
- "night_reminder": "Bonne nuit ! N'oubliez pas d'utiliser demain : {names}.",
- "weekly_balance": "Bilan hebdomadaire : +{in} ajoutés, −{out} consommés.",
- "weekly_added": "Vous avez ajouté {n} produits cette semaine.",
- "weekly_consumed": "Vous avez consommé {n} produits cette semaine. Bravo !",
- "tip_freezer": "💡 Les produits surgelés durent bien plus longtemps que la date de péremption.",
- "tip_bread": "💡 Le pain congelé garde sa fraîcheur pendant des semaines.",
- "tip_fifo": "💡 Pour éviter le gaspillage, utilisez en premier les produits les plus proches de la péremption (FIFO).",
- "tip_meat": "💡 La viande au congélateur peut se conserver jusqu'à 6 mois en toute sécurité.",
- "tip_no_refreeze": "💡 Ne jamais recongeler un produit décongelé. Cuisinez-le immédiatement !",
- "tip_fridge": "💡 Un réfrigérateur bien rangé vous fait gagner du temps et de l'argent.",
- "tip_canned": "💡 Les conserves ouvertes doivent aller au réfrigérateur et être consommées en quelques jours.",
- "top_brand": "La marque la plus courante dans votre garde-manger est {brand} avec {n} produits.",
- "combo_pasta": "Vous avez des pâtes et des condiments : prêt pour un premier plat ! 🍝",
- "combo_sandwich": "Pain et viande : un sandwich rapide est toujours une bonne idée ! 🥪",
- "combo_balanced": "Légumes et viande : vous avez tout pour un repas équilibré ! 🥗🥩",
- "pantry_empty": "Le garde-manger est vide ! Il est temps de faire les courses. 🛒",
- "pantry_empty_scan": "Aucun produit enregistré. Scannez quelque chose pour commencer !",
- "location_distribution": "Distribution : {parts}",
- "day": "jour",
- "days": "jours"
- },
- "kiosk_session": {
- "first_item": "Premier article : {name} !",
- "items_two_four": "{n} articles — on démarre 🚀",
- "items_five_nine": "{n} articles — bon rythme ! 💪",
- "items_ten_twenty": "{n} articles — presque un record 🏆",
- "items_twenty_plus": "{n} articles — courses épiques ! 🛒🔥",
- "duplicates_one": "1 doublon (même article deux fois)",
- "duplicates_many": "{n} doublons (pris plusieurs fois)",
- "top_category": "Catégorie principale : {cat} ({count}×)",
- "items_fallback": "{n} article{plural} ajouté{plural}"
- },
- "kiosk": {
- "check_btn": "🔍 Vérifier les mises à jour",
- "checking": "⏳ Vérification…",
- "error_check": "Erreur lors de la vérification des mises à jour",
- "error_start_install": "Erreur au démarrage de l'installation",
- "version_installed": "Installé : {v}",
- "update_available": "⬆️ Nouvelle version disponible : {latest} (installée : {current})",
- "up_to_date": "✅ Vous êtes à jour — version {v}",
- "too_old": "⚠️ Le kiosque installé est trop ancien pour la vérification automatique des mises à jour. Appuyez sur le bouton ci-dessous pour télécharger et installer la nouvelle version directement.",
- "manual_install": "⚠️ Ce kiosque ne supporte pas l'installation automatique. Procédure manuelle : 1. Quittez le kiosque (bouton ✕ en haut à gauche) 2. Désinstallez l'application EverShelf Kiosk 3. Téléchargez et installez le nouvel APK depuis GitHub :",
- "starting_download": "⏳ Démarrage du téléchargement…",
- "install_btn": "⬇️ Installer la mise à jour",
- "exit_title": "Quitter le kiosque",
- "refresh_title": "Actualiser la page"
- },
- "update": {
- "new_version": "Nouvelle version",
- "btn": "Mettre à jour"
- },
+ "app": {
+ "name": "EverShelf",
+ "loading": "Chargement..."
+ },
+ "nav": {
+ "title": "EverShelf",
+ "home": "Accueil",
+ "inventory": "Garde-manger",
+ "recipes": "Recettes",
+ "shopping": "Courses",
+ "log": "Journal",
+ "settings": "Paramètres"
+ },
+ "btn": {
+ "back": "← Retour",
+ "save": "💾 Enregistrer",
+ "cancel": "✕ Annuler",
+ "close": "Fermer",
+ "add": "✅ Ajouter",
+ "delete": "Supprimer",
+ "edit": "✏️ Modifier",
+ "use": "Utiliser",
+ "edit_item": "Modifier",
+ "search": "🔍 Rechercher",
+ "go": "✅ Valider",
+ "toggle_password": "👁️ Afficher/Masquer",
+ "load_more": "Charger plus...",
+ "save_config": "💾 Enregistrer la configuration",
+ "save_product": "💾 Enregistrer le produit",
+ "restart": "↺ Redémarrer",
+ "reset_default": "↺ Rétablir les valeurs par défaut",
+ "save_info": "💾 Enregistrer les informations",
+ "retry": "🔄 Réessayer",
+ "yes_short": "Oui",
+ "no_short": "Non"
+ },
+ "form": {
+ "select_placeholder": "-- Sélectionner --"
+ },
+ "locations": {
+ "dispensa": "Garde-manger",
+ "frigo": "Réfrigérateur",
+ "freezer": "Congélateur",
+ "altro": "Autre"
+ },
+ "categories": {
+ "latticini": "Produits laitiers",
+ "carne": "Viande",
+ "pesce": "Poisson",
+ "frutta": "Fruits",
+ "verdura": "Légumes",
+ "pasta": "Pâtes & Riz",
+ "pane": "Pain & Boulangerie",
+ "surgelati": "Surgelés",
+ "bevande": "Boissons",
+ "condimenti": "Condiments",
+ "snack": "Snacks & Sucreries",
+ "conserve": "Conserves",
+ "cereali": "Céréales & Légumineuses",
+ "igiene": "Hygiène",
+ "pulizia": "Entretien",
+ "altro": "Autre",
+ "select": "-- Sélectionner --"
+ },
+ "units": {
+ "pz": "pcs",
+ "conf": "pkg",
+ "g": "g",
+ "ml": "ml",
+ "pieces": "Pièces",
+ "grams": "Grammes",
+ "box": "Paquet",
+ "boxes": "Paquets",
+ "millilitres": "Millilitres",
+ "from": "de"
+ },
+ "shopping_sections": {
+ "frutta_verdura": "Fruits & Légumes",
+ "carne_pesce": "Viande & Poisson",
+ "latticini": "Produits laitiers & Frais",
+ "pane_dolci": "Pain & Pâtisseries",
+ "pasta": "Pâtes & Céréales",
+ "conserve": "Conserves & Sauces",
+ "surgelati": "Surgelés",
+ "bevande": "Boissons",
+ "pulizia_igiene": "Nettoyage & Hygiène",
+ "altro": "Autre"
+ },
+ "dashboard": {
+ "expired_title": "🚫 Périmé",
+ "expiring_title": "⏰ Expire bientôt",
+ "stats_period": "📊 30 derniers jours",
+ "opened_title": "📦 Produits ouverts",
+ "review_title": "🔍 À vérifier",
+ "review_hint": "Quantités inhabituelles. Confirmez si elles sont correctes ou modifiez-les.",
+ "quick_recipe": "Recette rapide avec les produits qui expirent",
+ "banner_review_title": "Quantité anormale",
+ "banner_review_action_ok": "C'est correct",
+ "banner_review_action_finish": "🗑️ Tout fini",
+ "banner_review_action_edit": "Corriger",
+ "banner_review_action_weigh": "Peser",
+ "banner_review_dismiss": "Ignorer",
+ "banner_prediction_title": "Consommation à vérifier",
+ "banner_prediction_hint": "L'estimation de consommation s'adapte aux données récentes : confirmez uniquement si la quantité actuelle est correcte.",
+ "banner_prediction_action_confirm": "Confirmer {qty} {unit}",
+ "banner_prediction_action_weigh": "Peser maintenant",
+ "banner_prediction_action_edit": "Mettre à jour la quantité",
+ "banner_expired_title": "Produit périmé",
+ "banner_expired_today": "Périmé aujourd'hui",
+ "banner_expired_days": "Périmé il y a {days} jours",
+ "banner_expired_action_use": "Utiliser quand même",
+ "banner_expired_action_finished": "Je l'ai terminé !",
+ "banner_expired_action_throw": "Je l'ai jeté",
+ "banner_expired_action_edit": "Corriger la date",
+ "banner_anomaly_action_edit": "Corriger l'inventaire",
+ "banner_anomaly_action_dismiss": "La quantité est correcte",
+ "banner_no_expiry_title": "Date manquante : {name}",
+ "banner_no_expiry_detail": "Ce produit n'a pas de date de péremption. Voulez-vous en ajouter une ou confirmer qu'il ne périme pas ?",
+ "banner_no_expiry_action_set": "Définir une date de péremption",
+ "banner_no_expiry_action_dismiss": "Ne périme pas ✓",
+ "banner_no_expiry_toast_dismissed": "Marqué comme « sans date limite »",
+ "banner_expiring_title": "Expire bientôt",
+ "banner_expiring_today": "Expire aujourd'hui !",
+ "banner_expiring_tomorrow": "Expire demain",
+ "banner_expiring_days": "Expire dans {days} jours",
+ "banner_expiring_action_use": "Utiliser maintenant",
+ "banner_finished_title": "terminé ?",
+ "banner_finished_detail": "J'ai enregistré que {name} a atteint zéro stock. Est-il vraiment épuisé ou en avez-vous encore ?",
+ "banner_finished_action_yes": "Oui, c'est fini",
+ "banner_finished_action_no": "Non, j'en ai encore",
+ "banner_review_unusual_pkg_title": "Taille de paquet inhabituelle",
+ "banner_review_unusual_pkg_detail": "Vous avez défini un paquet de {qty} {unit} — la taille semble très grande. Vérifiez si c'est correct ou modifiez.",
+ "banner_review_low_qty_title": "Quantité très faible",
+ "banner_review_low_qty_detail": "Vous n'avez que {qty} en stock — cela semble très peu, peut-être une erreur de saisie. Confirmez si c'est correct.",
+ "banner_review_high_qty_title": "Quantité inhabituellement élevée",
+ "banner_review_high_qty_detail": "Vous avez {qty} en stock — le chiffre semble très élevé. Confirmez si c'est correct ou modifiez.",
+ "banner_prediction_rate_day": "Moyenne ~{n} {unit}/jour",
+ "banner_prediction_rate_week": "Moyenne ~{n} {unit}/semaine",
+ "banner_prediction_days_ago": "Il y a {n} jours vous avez réapprovisionné",
+ "banner_prediction_more": "estimation précédente : {expected} {unit}{time} ; quantité actuelle : {actual} {unit}.",
+ "banner_prediction_less": "estimation : {expected} {unit}{time} ; quantité actuelle : {actual} {unit}. Si votre rythme d'utilisation a changé, la prévision se met à jour automatiquement.",
+ "banner_finished_zero": "L'inventaire indique zéro, mais les mouvements enregistrés suggèrent qu'il ne devrait pas être vide.",
+ "banner_finished_expected": "D'après les enregistrements vous devriez avoir encore {qty} {unit}.",
+ "banner_finished_check": "Pouvez-vous vérifier ?",
+ "banner_anomaly_phantom_title": "vous avez plus de stock que prévu",
+ "banner_anomaly_phantom_detail": "L'inventaire indique {inv_qty} {unit}, mais selon les enregistrements vous ne devriez avoir que {expected_qty} {unit}. Avez-vous ajouté du stock sans l'enregistrer ?",
+ "banner_anomaly_untracked_title": "stock non enregistré comme entrée",
+ "banner_anomaly_untracked_detail": "Vous avez {inv_qty} {unit} en inventaire, mais les sorties enregistrées dépassent les entrées — le stock initial n'a probablement jamais été ajouté comme transaction « entrée ». Vous pouvez corriger la quantité ou saisir les entrées manquantes.",
+ "banner_anomaly_ghost_title": "vous avez moins de stock que prévu",
+ "banner_anomaly_ghost_detail": "D'après les opérations enregistrées vous devriez avoir {expected_qty} {unit} de {name}, mais l'inventaire n'en montre que {inv_qty} {unit}. Avez-vous pris du stock sans l'enregistrer ?",
+ "consumed": "Consommé : {n} ({pct}%)",
+ "wasted": "Gaspillé : {n} ({pct}%)",
+ "more_opened": "et {n} autres ouverts...",
+ "banner_expired_detail": "{when} · il vous reste encore {qty}.",
+ "banner_opened_detail": "{when} dans {location} · il vous reste encore {qty}.",
+ "banner_explain_title": "Demander une explication à Gemini",
+ "banner_explain_btn": "Expliquer",
+ "banner_analyzing": "🤖 Analyse en cours…"
+ },
+ "inventory": {
+ "title": "Garde-manger",
+ "filter_all": "Tout",
+ "search_placeholder": "🔍 Rechercher un produit...",
+ "recent_title": "🕐 Utilisés récemment",
+ "popular_title": "⭐ Les plus utilisés",
+ "empty": "Aucun produit ici.\nScannez un produit pour l'ajouter !",
+ "no_items_found": "Aucun article trouvé",
+ "qty_remainder_suffix": "restant",
+ "vacuum_badge": "🫙 Sous vide",
+ "opened_badge": "📭 Ouvert",
+ "label_expiry": "📅 Péremption",
+ "label_storage": "🫙 Conservation",
+ "label_status": "📭 État",
+ "opened_since": "Ouvert depuis le {date}",
+ "label_position": "📍 Emplacement",
+ "label_quantity": "📦 Quantité",
+ "label_added": "📅 Ajouté",
+ "empty_text": "Aucun produit ici. Scannez un produit pour l'ajouter !",
+ "empty_db": "Aucun produit dans la base de données. Scannez un produit pour commencer !",
+ "qty_trace": "< 1"
+ },
+ "scan": {
+ "title": "Scanner",
+ "mode_shopping": "🛒 Mode courses",
+ "mode_shopping_end": "✅ Terminer les courses",
+ "spesa_btn": "🛒 Courses",
+ "zoom": "Zoom",
+ "tab_barcode": "Code-barres",
+ "tab_name": "Nom",
+ "tab_ai": "IA",
+ "recents_label": "Récents",
+ "torch_hint": "Torche",
+ "torch_on": "Torche activée",
+ "torch_off": "Torche désactivée",
+ "torch_unavailable": "Torche non disponible sur cet appareil",
+ "flip_hint": "Retourner la caméra",
+ "flip_front": "Caméra frontale",
+ "flip_back": "Caméra arrière",
+ "num_ocr_btn": "🔢 Lire les chiffres avec l'IA",
+ "num_ocr_searching": "Recherche du code-barres avec l'IA...",
+ "num_ocr_found": "Code trouvé : {code}",
+ "num_ocr_not_found": "Aucun code-barres trouvé dans l'image",
+ "barcode_placeholder": "Entrez le code-barres...",
+ "quick_name_divider": "ou tapez le nom",
+ "quick_name_placeholder": "Ex. : Pommes, Courgettes, Pain...",
+ "manual_entry": "✏️ Saisie manuelle",
+ "ai_identify": "🤖 Identifier avec l'IA",
+ "hint": "Scannez le code-barres, tapez le nom du produit ou utilisez l'IA pour l'identifier",
+ "debug_toggle": "🐛 Journal de débogage",
+ "barcode_acquired": "🔖 Code-barres scanné : {code}",
+ "scan_barcode": "🔖 Scanner le code-barres",
+ "create_named": "Créer {name}",
+ "new_without_barcode": "Nouveau produit sans code-barres",
+ "status_ready": "Pointez la caméra sur le code-barres",
+ "status_scanning": "Scan en cours...",
+ "status_partial": "Lu : {code} — vérification...",
+ "status_invalid": "Invalide : {code} — nouvel essai",
+ "status_confirmed": "Confirmé !",
+ "status_parallel": "Scan combiné actif...",
+ "ai_fallback_searching": "Identification IA en cours...",
+ "ai_fallback_found": "Produit identifié par l'IA",
+ "ai_fallback_not_found": "IA : produit non reconnu"
+ },
+ "action": {
+ "title": "Que voulez-vous faire ?",
+ "add_btn": "📥 AJOUTER",
+ "add_sub": "au garde-manger/réfrigérateur",
+ "use_btn": "📤 UTILISER / CONSOMMER",
+ "use_sub": "depuis le garde-manger/réfrigérateur",
+ "have_title": "📦 Déjà en stock !",
+ "add_more_sub": "en ajouter",
+ "use_qty_sub": "combien vous avez utilisé",
+ "throw_btn": "🗑️ JETER",
+ "throw_sub": "jeter",
+ "edit_sub": "péremption, emplacement…",
+ "create_recipe_btn": "Recette"
+ },
+ "add": {
+ "title": "Ajouter au garde-manger",
+ "location_label": "📍 Où le rangez-vous ?",
+ "quantity_label": "📦 Quantité",
+ "conf_size_label": "📦 Chaque paquet contient :",
+ "conf_size_placeholder": "ex. 300",
+ "vacuum_label": "🫙 Sous vide",
+ "vacuum_hint": "La date de péremption sera automatiquement prolongée",
+ "submit": "✅ Ajouter",
+ "purchase_type_label": "🛒 Ce produit est...",
+ "new_btn": "🆕 Vient d'être acheté",
+ "existing_btn": "📦 Je l'avais déjà",
+ "remaining_label": "📦 Quantité restante",
+ "remaining_hint": "Approximativement combien en reste-t-il ?",
+ "remaining_full": "🟢 Plein",
+ "remaining_half": "🟠 À moitié",
+ "estimated_expiry": "Date de péremption estimée :",
+ "suffix_freezer": "(congélateur)",
+ "suffix_vacuum": "(sous vide)",
+ "hint_modify": "📝 Vous pouvez modifier la date ou la scanner avec la caméra",
+ "scan_expiry_title": "📷 Scanner la date de péremption",
+ "product_added": "✅ {name} ajouté !{qty}",
+ "suffix_freezer_vacuum": "(congélateur + sous vide)",
+ "history_badge_tip": "Moyenne de {n} entrées précédentes",
+ "vacuum_question": "Sous vide ?",
+ "vacuum_saved": "🔒 Sous vide !"
+ },
+ "use": {
+ "title": "Utiliser / Consommer",
+ "location_label": "📍 Depuis où ?",
+ "quantity_label": "Combien avez-vous utilisé ?",
+ "change": "modifier",
+ "partial_hint": "Ou précisez la quantité utilisée :",
+ "partial_piece_hint": "Avez-vous utilisé seulement une partie ?",
+ "piece": "pièce",
+ "one_whole": "1 entier",
+ "use_all": "🗑️ Tout utilisé / Terminé",
+ "submit": "📤 Utiliser cette quantité",
+ "available": "📦 Disponible :",
+ "opened_badge": "OUVERT",
+ "not_in_inventory": "⚠️ Produit absent de l'inventaire.",
+ "expiry_warning": "⚠️ Utilisez en premier celui{loc} qui expire le {date} — {when} !",
+ "expiry_warning_opened": "⚠️ Celui{loc} est ouvert depuis {when} — utilisez-le en premier !",
+ "throw_title": "🗑️ Jeter le produit",
+ "throw_all": "🗑️ Tout jeter ({qty})",
+ "throw_qty_label": "Quelle quantité jeter ?",
+ "throw_qty_hint": "ou entrez une quantité :",
+ "throw_partial_btn": "🗑️ Jeter cette quantité",
+ "when_expired": "périmé il y a {n} jours",
+ "when_today": "expire aujourd'hui",
+ "when_tomorrow": "expire demain",
+ "when_days": "expire dans {n} jours",
+ "toast_used": "📤 {qty} de {name} utilisé",
+ "toast_bring": "🛒 Produit terminé → ajouté à Bring !",
+ "toast_opened_finished": "🔓 Emballage ouvert de {name} terminé !",
+ "disambiguation_hint": "Que voulez-vous dire par « tout fini » ?",
+ "disambiguation_all": "🗑️ Tout finir ({qty})",
+ "error_exceeds_stock": "⚠️ Vous ne pouvez pas utiliser plus que ce que vous avez disponible !",
+ "use_all_confirm_title": "✅ Tout terminer",
+ "use_all_confirm_msg": "Confirmez que vous avez terminé le produit :",
+ "use_all_confirm_btn": "✅ Oui, terminé",
+ "throw_all_confirm_title": "🗑️ Tout jeter",
+ "throw_all_confirm_msg": "Voulez-vous vraiment jeter tout le produit ?",
+ "throw_all_confirm_btn": "🗑️ Oui, jeter"
+ },
+ "product": {
+ "title_new": "Nouveau produit",
+ "title_edit": "Modifier le produit",
+ "ai_fill": "📷 Prendre une photo et identifier avec l'IA",
+ "ai_fill_hint": "L'IA remplira automatiquement les champs du produit",
+ "name_label": "🏷️ Nom du produit *",
+ "name_placeholder": "Ex. : Lait entier, Pâtes penne...",
+ "brand_label": "🏢 Marque",
+ "brand_placeholder": "Ex. : Barilla, Danone, Heinz...",
+ "category_label": "📂 Catégorie",
+ "unit_label": "📏 Unité de mesure",
+ "default_qty_label": "🔢 Quantité par défaut",
+ "conf_size_label": "📦 Chaque paquet contient :",
+ "conf_size_placeholder": "ex. 300",
+ "notes_label": "📝 Notes",
+ "notes_placeholder": "Ex. : sans lactose, biologique, conserver au réfrigérateur après ouverture...",
+ "barcode_label": "🔖 Code-barres",
+ "barcode_placeholder": "Code-barres (si disponible)",
+ "barcode_hint": "⚠️ Ajoutez le code-barres pour la prochaine fois, il suffira de scanner !",
+ "submit": "💾 Enregistrer le produit",
+ "name_required": "Entrez le nom du produit",
+ "conf_size_required": "Précisez le contenu du paquet",
+ "expiry_estimated": "Date de péremption estimée :",
+ "scan_expiry": "Scanner la date de péremption",
+ "expiry_hint": "📝 Vous pouvez modifier la date ou la scanner avec la caméra",
+ "add_batch": "📦 + Lot avec une date différente",
+ "package_info": "📦 Paquet : {info}",
+ "edit_catalog": "⚙️ Modifier les infos produit (nom, marque, catégorie…)",
+ "not_recognized": "⚠️ Produit non reconnu",
+ "edit_info": "✏️ Modifier les informations",
+ "modify_details": "MODIFIER\npéremption, emplacement…",
+ "already_in_pantry": "📋 Déjà dans le garde-manger",
+ "no_barcode": "Pas de code-barres",
+ "unknown_product": "Produit non reconnu",
+ "edit_name_brand": "Modifier nom/marque",
+ "weight_label": "Poids",
+ "origin_label": "Origine",
+ "labels_label": "Labels",
+ "select_variant": "Sélectionnez la variante exacte ou utilisez les données IA :"
+ },
+ "products": {
+ "title": "📦 Tous les produits",
+ "search_placeholder": "🔍 Rechercher un produit...",
+ "empty": "Aucun produit dans la base de données.\nScannez un produit pour commencer !",
+ "no_category": "Aucun produit dans cette catégorie"
+ },
+ "recipes": {
+ "title": "🍳 Recettes",
+ "generate": "✨ Générer une nouvelle recette",
+ "archive_empty": "Aucune recette enregistrée. Générez votre première recette !",
+ "dialog_title": "🍳 Recette",
+ "dialog_desc": "Je vais générer une recette saine en utilisant les ingrédients du garde-manger, en priorisant les produits qui expirent.",
+ "meal_label": "🕐 Quel repas ?",
+ "persons_label": "👥 Pour combien de personnes ?",
+ "meal_type_label": "🎯 Type de repas",
+ "opt_fast": "⚡ Repas rapide",
+ "opt_light": "🥗 Appétit léger",
+ "opt_expiry": "⏰ Prioriser les produits qui expirent",
+ "opt_healthy": "💚 Extra sain",
+ "opt_opened": "📦 Prioriser les produits ouverts",
+ "opt_zero_waste": "♻️ Zéro déchet",
+ "generate_btn": "✨ Générer la recette",
+ "loading_msg": "Préparation de votre recette...",
+ "start_cooking": "👨🍳 Mode cuisine",
+ "regenerate": "🔄 En générer une autre",
+ "regen_choice_title": "Que veux-tu faire de cette recette ?",
+ "regen_replace": "🔄 En générer une autre (ignorer celle-ci)",
+ "regen_save_new": "💾 Sauvegarder dans l'archive et en générer une nouvelle",
+ "close_btn": "✅ Fermer",
+ "ingredients_title": "🧾 Ingrédients",
+ "tools_title": "Matériel nécessaire",
+ "steps_title": "👨🍳 Étapes",
+ "no_steps": "Aucune étape disponible",
+ "generate_error": "Erreur de génération",
+ "persons_short": "pers.",
+ "use_ingredient_title": "Utiliser l'ingrédient",
+ "recipe_qty_label": "Recette",
+ "from_where_label": "Depuis où ?",
+ "amount_label": "Combien",
+ "use_amount_btn": "Utiliser cette quantité",
+ "use_all_btn": "Tout utiliser / Terminé",
+ "packs_label": "Paquets",
+ "quantity_in_total": "Quantité en {unit} (total : {total})",
+ "packs_of_have": "Paquets de {size} (vous en avez {count})",
+ "scale_wait_stable": "Attendez 10s de poids stable pour le remplissage automatique…",
+ "ingredient_scaled_toast": "📦 Ingrédient déduit du garde-manger !",
+ "finished_added_bring_toast": "🛒 Produit terminé → ajouté à Bring !",
+ "load_error": "Erreur de chargement",
+ "favorite": "Ajouter aux favoris",
+ "unfavorite": "Retirer des favoris",
+ "adjust_persons": "Personnes"
+ },
+ "shopping": {
+ "title": "🛒 Liste de courses",
+ "bring_loading": "Connexion à Bring !...",
+ "bring_not_configured": "Bring ! n'est pas configuré. Ajoutez votre e-mail et mot de passe dans les paramètres.",
+ "tab_to_buy": "🛍️ À acheter",
+ "tab_forecast": "🧠 Prévision",
+ "total_label": "💰 Total estimé",
+ "section_to_buy": "🛍️ À acheter",
+ "suggestions_title": "💡 Suggestions IA",
+ "suggestions_add": "✅ Ajouter la sélection à Bring !",
+ "search_prices": "🔍 Rechercher tous les prix",
+ "suggest_btn": "Suggérer des achats",
+ "smart_title": "🧠 Prévisions intelligentes",
+ "smart_empty": "Aucune prévision disponible. Ajoutez des produits à votre garde-manger pour recevoir des prévisions intelligentes.",
+ "smart_filter_all": "Tout",
+ "smart_filter_critical": "🔴 Urgent",
+ "smart_filter_high": "🟠 Bientôt",
+ "smart_filter_medium": "🟡 Planifier",
+ "smart_filter_low": "🟢 Prévision",
+ "smart_add": "🛒 Ajouter la sélection à Bring !",
+ "empty": "Liste de courses vide !\nUtilisez le bouton ci-dessous pour générer des suggestions.",
+ "already_in_list": "🛒 \"{name}\" est déjà dans la liste de courses",
+ "already_in_list_short": "ℹ️ Déjà dans la liste de courses",
+ "add_prompt": "Voulez-vous l'ajouter à la liste de courses ?",
+ "smart_already": "📊 Les prévisions achats prédisent déjà {name}",
+ "all_searched": "Tous les produits ont déjà été recherchés. Utilisez 🔄 pour rechercher individuellement.",
+ "search_complete": "Recherche terminée : {count} produits",
+ "removed_sufficient": "🧹 {removed} produit(s) avec stock suffisant retiré(s) de la liste",
+ "suggest_buy": "🛒 Acheter : {qty} {unit}",
+ "suggest_buy_approx": "🛒 Au minimum : {qty} {unit}",
+ "suggest_buy_tip": "Quantité suggérée basée sur vos 14 derniers jours de consommation",
+ "suggest_buy_approx_tip": "Estimation minimale basée sur la consommation (achetez le format le plus proche)",
+ "bring_badge": "🛒 Déjà sur Bring !",
+ "add_urgent_toast": "🔴 {n} produit(s) urgent(s) automatiquement ajouté(s) à Bring !",
+ "migration_done": "✅ {migrated} mis à jour, {skipped} déjà ok",
+ "added_to_bring": "🛒 {n} produits ajoutés à Bring !",
+ "added_to_bring_skip": "{n} déjà présents",
+ "all_on_bring": "Tous les produits étaient déjà sur Bring !",
+ "freq_high": "📈 Fréquent",
+ "freq_regular": "📊 Régulier",
+ "freq_occasional": "📉 Occasionnel",
+ "out_of_stock": "Rupture de stock",
+ "scan_toast": "📷 Scanner : {name}",
+ "empty_category": "Aucun produit dans cette catégorie",
+ "session_empty": "🛒 Aucun produit encore",
+ "urgency_critical": "Urgent",
+ "urgency_high": "Bientôt",
+ "urgency_medium": "Planifier",
+ "urgency_low": "Prévision",
+ "urgency_medium_short": "Moyen",
+ "urgency_low_short": "Ok",
+ "tag_urgent": "🔴 Urgent",
+ "tag_priority": "⭐ Priorité",
+ "tag_check": "✅ Vérifier",
+ "smart_already_predicted": "📊 Les prévisions achats prédisent déjà {name}{urgency}.",
+ "item_removed": "✅ {name} retiré de la liste !",
+ "urgency_spec_critical": "⚡ Urgent",
+ "urgency_spec_high": "🟠 Bientôt",
+ "bring_add_n": "Ajouter {n} à Bring !",
+ "bring_add_selected": "Ajouter la sélection à Bring !",
+ "bring_adding": "Ajout en cours...",
+ "bring_added_one": "1 produit ajouté à Bring !",
+ "bring_added_many": "{n} produits ajoutés à Bring !",
+ "bring_skipped": "({n} déjà dans la liste)",
+ "force_sync": "Forcer la synchronisation Bring !",
+ "scan_target_label": "Vous cherchez",
+ "scan_target_found": "Trouvé ! Retirer de la liste",
+ "bring_add_one": "Ajouter 1 produit à Bring !",
+ "bring_add_many": "Ajouter {n} produits à Bring !",
+ "syncing": "Synchronisation…",
+ "sync_done": "Synchronisation terminée",
+ "price_searching": "Recherche en cours...",
+ "search_action": "Rechercher",
+ "open_action": "Ouvrir",
+ "not_found": "Non trouvé",
+ "search_price": "Rechercher le prix",
+ "tap_to_scan": "Appuyez pour scanner",
+ "tag_title": "Tag",
+ "remove_title": "Retirer",
+ "found_count": "{found}/{total} produits trouvés",
+ "savings_offers": "· 🏷️ Vous économisez {amount}€ avec les offres",
+ "searching_progress": "Recherche {current}/{total}...",
+ "remove_error": "Erreur de suppression",
+ "btn_fetch_prices": "Trouver les prix",
+ "price_total_label": "💰 Total estimé :",
+ "price_loading": "Recherche des prix…",
+ "price_not_found": "prix n/d",
+ "suggest_loading": "Analyse en cours...",
+ "suggest_error": "Erreur de génération des suggestions",
+ "priority_high": "Élevée",
+ "priority_medium": "Moyenne",
+ "priority_low": "Faible",
+ "smart_last_update": "Mis à jour {time}",
+ "names_already_updated": "Tous les noms sont déjà à jour"
+ },
+ "ai": {
+ "title": "🤖 Identification IA",
+ "capture": "📸 Prendre une photo",
+ "retake": "🔄 Reprendre",
+ "hint": "Prenez une photo du produit et l'IA essaiera de l'identifier",
+ "identifying": "🤖 Identification du produit...",
+ "no_api_key": "⚠️ Clé API Gemini non configurée.\nAjoutez GEMINI_API_KEY au fichier .env sur le serveur.",
+ "fields_filled": "✅ Champs remplis par l'IA",
+ "use_data": "✅ Utiliser les données IA",
+ "use_data_no_barcode": "✅ Utiliser les données IA (sans code-barres)"
+ },
+ "log": {
+ "title": "📒 Journal des opérations",
+ "type_added": "Ajouté",
+ "type_waste": "Jeté",
+ "type_used": "Utilisé",
+ "type_bring": "Ajouté à Bring !",
+ "undone_badge": "Annulé",
+ "undo_title": "Annuler cette opération",
+ "load_error": "Erreur de chargement du journal",
+ "empty": "Aucune opération enregistrée.",
+ "undo_action_remove": "suppression de",
+ "undo_action_restore": "réapprovisionnement de",
+ "undo_confirm": "Annuler cette opération ?\n→ {action} {name}",
+ "undo_success": "↩ Opération annulée pour {name}",
+ "already_undone": "Opération déjà annulée",
+ "too_old": "Impossible d'annuler des opérations de plus de 24 heures",
+ "undo_error": "Erreur lors de l'annulation",
+ "recipe_prefix": "Recette"
+ },
+ "chat": {
+ "title": "Chef Gemini",
+ "welcome": "Bonjour ! Je suis votre assistant cuisine",
+ "welcome_desc": "Demandez-moi de préparer un jus, un snack, un plat rapide… Je connais votre garde-manger, vos appareils et vos préférences !",
+ "suggestion_snack": "🍿 Snack rapide",
+ "suggestion_juice": "🥤 Jus/Smoothie",
+ "suggestion_light": "🥗 Quelque chose de léger",
+ "suggestion_expiry": "⏰ Utiliser les produits qui expirent",
+ "clear": "Nouvelle conversation",
+ "placeholder": "Demandez quelque chose...",
+ "cleared": "Chat effacé",
+ "suggestion_snack_text": "Que puis-je préparer comme snack rapide ?",
+ "suggestion_juice_text": "Faites-moi un jus ou un smoothie avec ce que j'ai",
+ "suggestion_light_text": "J'ai faim mais je veux quelque chose de léger",
+ "suggestion_expiry_text": "Qu'est-ce qui va bientôt expirer et comment l'utiliser ?",
+ "transfer_to_recipes": "Transférer aux recettes",
+ "transferring": "Transfert en cours...",
+ "transferred": "Ajouté aux recettes !",
+ "open_recipe": "Ouvrir la recette",
+ "quick_recipe_prompt": "Suggérez une recette rapide POUR UNE PERSONNE en utilisant les produits qui expirent en premier ! Ignorez les congélateurs, concentrez-vous sur le réfrigérateur et le garde-manger."
+ },
+ "cooking": {
+ "close": "Fermer",
+ "tts_btn": "Lire à voix haute",
+ "restart": "↺ Recommencer",
+ "replay": "🔊 Rejouer",
+ "timer": "⏱️ {time} · Minuterie",
+ "prev": "◀ Précédent",
+ "next": "Suivant ▶",
+ "ingredient_used": "✔️ Déduit",
+ "ingredient_use_btn": "📦 Utiliser",
+ "ingredient_deduct_title": "Déduire du garde-manger",
+ "timer_expired_tts": "Minuterie {label} terminée !",
+ "timer_warning_tts": "Attention ! {label} : 10 secondes restantes !",
+ "recipe_done_tts": "Recette terminée ! Bon appétit !",
+ "expires_chip": "exp. {date}",
+ "finish": "✅ Terminer",
+ "step_fallback": "Étape {n}",
+ "zerowaste_label": "♻️ Déchet",
+ "zerowaste_tip_title": "Conseil zéro déchet"
+ },
+ "settings": {
+ "title": "⚙️ Paramètres",
+ "tab_api": "Clés API",
+ "tab_bring": "Bring !",
+ "tab_recipe": "Recettes",
+ "tab_mealplan": "Plan hebdomadaire",
+ "tab_appliances": "Appareils",
+ "tab_spesa": "Courses en ligne",
+ "tab_camera": "Caméra",
+ "tab_security": "Sécurité",
+ "tab_tts": "Voix (TTS)",
+ "tab_language": "Langue",
+ "tab_scale": "Balance connectée",
"gemini": {
- "chat_title": "Discussion avec Gemini",
- "not_configured": "🤖 Gemini non configuré — définissez GEMINI_API_KEY dans les paramètres"
+ "title": "🤖 Google Gemini IA",
+ "hint": "Clé API pour l'identification des produits, les dates de péremption et les recettes.",
+ "key_label": "Clé API Gemini"
+ },
+ "bring": {
+ "title": "🛒 Liste de courses Bring !",
+ "hint": "Identifiants pour l'intégration de la liste de courses Bring !",
+ "email_label": "📧 E-mail Bring !",
+ "password_label": "🔒 Mot de passe Bring !"
+ },
+ "price": {
+ "title": "💰 Estimation des prix (IA)",
+ "hint": "Afficher le coût estimé par produit dans la liste de courses à l'aide de l'IA.",
+ "enabled_label": "Activer l'estimation des prix",
+ "country_label": "🌍 Pays de référence",
+ "currency_label": "💱 Devise",
+ "update_label": "🔄 Actualiser les prix tous les",
+ "update_suffix": "mois"
+ },
+ "recipe": {
+ "title": "🍳 Préférences de recettes",
+ "hint": "Configurez les options par défaut pour la génération de recettes.",
+ "persons_label": "👥 Portions par défaut",
+ "options_label": "🎯 Options de recette par défaut",
+ "fast": "⚡ Repas rapide",
+ "light": "🥗 Repas léger",
+ "expiry": "⏰ Priorité péremption",
+ "healthy": "💚 Extra sain",
+ "opened": "📦 Priorité produits ouverts",
+ "zerowaste": "♻️ Zéro déchet",
+ "dietary_label": "🚫 Intolérances / Restrictions",
+ "dietary_placeholder": "Ex. : sans gluten, sans lactose, végétarien..."
+ },
+ "mealplan": {
+ "title": "📅 Plan de repas hebdomadaire",
+ "hint": "Définissez le type de repas pour chaque jour. Il sera utilisé comme guide pour la génération de recettes.",
+ "enabled": "✅ Activer le plan hebdomadaire",
+ "legend": "🌤️ = Déjeuner · 🌙 = Dîner · Appuyez sur un badge pour le modifier.",
+ "types_title": "📋 Types disponibles",
+ "reset_btn": "↺ Restaurer les valeurs par défaut"
},
"appliances": {
- "empty": "Aucun appareil ajouté"
+ "title": "🔌 Appareils disponibles",
+ "hint": "Indiquez les appareils que vous possédez. Ils seront pris en compte lors de la génération de recettes.",
+ "new_placeholder": "Ex. : Machine à pain, Thermomix, Friteuse à air...",
+ "quick_title": "Ajout rapide :",
+ "oven": "🔥 Four",
+ "microwave": "📡 Micro-ondes",
+ "air_fryer": "🍟 Friteuse à air",
+ "bread_maker": "🍞 Machine à pain",
+ "bimby": "🤖 Thermomix/Cookeo",
+ "mixer": "🌀 Robot pâtissier",
+ "steamer": "♨️ Cuiseur vapeur",
+ "pressure_cooker": "🫕 Cocotte-minute",
+ "toaster": "🍞 Grille-pain",
+ "blender": "🍹 Mixeur",
+ "empty": "Aucun appareil ajouté"
},
- "about": {
- "title": "À propos",
- "version": "Version",
- "report_bug": "Signaler un bug",
- "report_bug_hint": "Quelque chose ne fonctionne pas ? Envoyez-nous un rapport directement depuis l'application.",
- "report_bug_modal_title": "Signaler un bug",
- "report_type_bug": "Bug",
- "report_type_feature": "Fonctionnalité",
- "report_type_question": "Question",
- "report_field_title": "Titre",
- "report_field_title_ph": "Brève description du problème",
- "report_field_desc": "Description",
- "report_field_desc_ph": "Décrivez le problème en détail…",
- "report_field_steps": "Étapes pour reproduire (optionnel)",
- "report_field_steps_ph": "1. Aller à…\n2. Appuyer sur…\n3. Voir l'erreur…",
- "report_auto_info": "Joint automatiquement : version {version}, langue {lang}.",
- "report_send_btn": "Envoyer le rapport",
- "report_bug_sending": "Envoi…",
- "report_bug_sent": "Rapport envoyé — merci !",
- "report_bug_error": "Impossible d'envoyer le rapport. Vérifiez votre connexion.",
- "changelog": "Journal des modifications",
- "github": "Dépôt GitHub"
+ "spesa": {
+ "title": "🛍️ Courses en ligne",
+ "hint": "Configurez le fournisseur de courses en ligne.",
+ "provider_label": "🏪 Fournisseur",
+ "email_label": "📧 E-mail",
+ "password_label": "🔒 Mot de passe",
+ "login_btn": "🔐 Connexion",
+ "ai_prompt_label": "🤖 Prompt IA de sélection de produit",
+ "ai_prompt_placeholder": "Instructions pour l'IA lors du choix entre plusieurs produits...",
+ "ai_prompt_hint": "L'IA utilise ce prompt pour choisir le produit le plus approprié parmi les résultats. Laissez vide pour le comportement par défaut.",
+ "configure_first": "Configurez d'abord les courses en ligne dans les paramètres",
+ "missing_credentials": "Entrez l'e-mail et le mot de passe",
+ "login_in_progress": "Connexion en cours...",
+ "login_error_prefix": "Erreur :",
+ "login_network_error_prefix": "Erreur réseau :",
+ "login_success_default": "Connexion réussie !",
+ "result_name_label": "Nom",
+ "result_card_label": "Carte",
+ "result_pickup_label": "Point de retrait",
+ "result_points_label": "Points fidélité",
+ "connected_relogin": "✅ Connecté — Se reconnecter",
+ "connected_as": "Connecté en tant que {name}"
},
- "export": {
- "title": "Exporter l'inventaire",
- "hint": "Téléchargez l'inventaire actuel en CSV ou ouvrez la version imprimable (PDF).",
- "btn_csv": "Télécharger CSV",
- "btn_pdf": "PDF / Imprimer",
- "btn_title": "Exporter"
+ "camera": {
+ "title": "📷 Caméra",
+ "hint": "Choisissez la caméra à utiliser pour le scan de code-barres et l'identification IA.",
+ "device_label": "📸 Caméra par défaut",
+ "back": "📱 Arrière (par défaut)",
+ "front": "🤳 Frontale",
+ "devices_hint": "Si vous avez plusieurs caméras, vous pouvez en sélectionner une dans la liste ci-dessus après avoir accordé les permissions.",
+ "detect_btn": "🔄 Détecter les caméras",
+ "ai_fallback_label": "Identification visuelle IA (repli 5s)",
+ "ai_fallback_hint": "Si aucun code-barres n'est lu en 5 secondes, une image est automatiquement envoyée à l'IA pour identifier visuellement le produit. Nécessite Gemini configuré."
},
- "startup": {
- "connecting": "Connexion au serveur...",
- "check_php_memory": "Mémoire PHP",
- "check_php_timeout": "Délai PHP",
- "check_php_upload": "Upload PHP",
- "check_data_dir": "Dossier de données",
- "check_rate_limits": "Dossier rate limits",
- "check_backups": "Dossier sauvegardes",
- "check_write_test": "Test d'écriture disque",
- "check_disk_space": "Espace disque",
- "check_db_connect": "Connexion base de données",
- "check_db_tables": "Tables de la BDD",
- "check_db_integrity": "Intégrité BDD",
- "check_db_wal": "Mode WAL",
- "check_db_size": "Taille de la BDD",
- "check_db_rows": "Données inventaire",
- "check_env": "Fichier .env",
- "check_gemini": "Clé Gemini AI",
- "check_bring_creds": "Identifiants Bring!",
- "check_bring_token": "Token Bring!",
- "check_curl_ssl": "cURL SSL",
- "check_internet": "Connexion internet",
- "fresh_install": "nouvelle installation",
- "warnings_found": "avertissements détectés",
- "all_ok": "Système OK",
- "critical_error_short": "Erreur critique",
- "critical_error": "Erreur critique : l'application ne peut pas démarrer. Vérifiez les logs.",
- "error_network": "Impossible de contacter le serveur. Vérifiez votre connexion réseau.",
- "retry": "Réessayer",
- "syncing_local": "Synchronisation des données locales...",
- "sync_done": "Données locales synchronisées"
+ "security": {
+ "title": "🔒 Certificat HTTPS",
+ "hint": "Si le navigateur affiche l'erreur « Votre connexion n'est pas privée » (ERR_CERT_AUTHORITY_INVALID), vous devez installer le certificat CA sur l'appareil.",
+ "download_btn": "📥 Télécharger le certificat CA",
+ "token_title": "🔑 Token de paramètres",
+ "token_label": "Token d'accès",
+ "token_hint": "Si `SETTINGS_TOKEN` est configuré dans le `.env` du serveur, entrez le token ici avant de sauvegarder les paramètres. Laissez vide si non configuré.",
+ "token_placeholder": "(vide = pas de protection)",
+ "token_required_hint": "🔒 Ce serveur nécessite un token pour sauvegarder les paramètres.",
+ "cert_instructions": "Instructions pour Chrome (Android) : 1. Téléchargez le certificat ci-dessus 2. Allez dans Paramètres → Sécurité & Confidentialité → Plus de paramètres de sécurité → Installer depuis le stockage 3. Sélectionnez le fichier EverShelf_CA.crt téléchargé 4. Choisissez « CA » et confirmez 5. Redémarrez Chrome
Instructions pour Chrome (PC) : 1. Téléchargez le certificat ci-dessus 2. Allez dans chrome://settings/certificates 3. Onglet « Autorités » → Importer → sélectionnez le fichier 4. Cochez « Approuver ce certificat pour identifier les sites web » 5. Redémarrez Chrome"
},
- "stats_monthly": {
- "title": "Statistiques Mensuelles",
- "consumed": "produits utilisés",
- "trend_up": "+{pct}% vs {prev}",
- "trend_down": "-{pct}% vs {prev}",
- "trend_same": "même rythme que le mois dernier",
- "added": "ajoutés",
- "wasted": "gaspillés",
- "top_used": "le plus utilisé",
- "top_cats": "Catégories principales",
- "source": "Historique des transactions · mois en cours"
+ "tts": {
+ "title": "🔊 Voix & TTS",
+ "hint": "Configurez la synthèse vocale via une API REST externe. Les étapes de recette et les minuteries expirées seront envoyées à l'endpoint configuré.",
+ "enabled": "✅ Activer la TTS",
+ "engine_label": "⚙️ Moteur TTS",
+ "engine_browser": "🔇 Navigateur (hors ligne, aucune configuration requise)",
+ "engine_server": "🌐 Serveur externe (Home Assistant, API REST...)",
+ "voice_label": "🗣️ Voix",
+ "rate_label": "⚡ Vitesse",
+ "pitch_label": "🎵 Tonalité",
+ "url_label": "🌐 URL de l'endpoint",
+ "method_label": "📡 Méthode HTTP",
+ "auth_label": "🔐 Authentification",
+ "auth_bearer": "Bearer Token",
+ "auth_custom": "En-tête personnalisé",
+ "auth_none": "Aucune",
+ "token_label": "🔑 Bearer Token",
+ "custom_header_name": "📋 Nom de l'en-tête",
+ "custom_header_value": "📋 Valeur de l'en-tête",
+ "content_type_label": "📄 Content-Type",
+ "payload_key_label": "🗝️ Champ texte dans le payload",
+ "payload_key_hint": "Nom du champ JSON qui contiendra le texte à lire (ex. : message, text).",
+ "extra_fields_label": "➕ Champs supplémentaires (JSON)",
+ "extra_fields_placeholder": "{\"entity_id\": \"media_player.salon\"}",
+ "extra_fields_hint": "Champs supplémentaires à inclure dans le payload, en format JSON. Laissez vide si non nécessaire.",
+ "test_btn": "🔊 Envoyer une voix de test",
+ "voices_loading": "Chargement des voix…",
+ "voice_not_supported": "Voix non supportée par ce navigateur",
+ "voices_none": "Aucune voix disponible sur cet appareil",
+ "voices_hint": "Les voix disponibles dépendent du système d'exploitation et du navigateur. Appuyez sur ↺ si la liste ne se charge pas.",
+ "url_missing": "⚠️ URL de l'endpoint manquante.",
+ "test_sending": "⏳ Envoi…",
+ "test_ok": "✅ Réponse {code} — vérifiez que le haut-parleur a parlé.",
+ "heard_question": "Avez-vous entendu la voix ?",
+ "heard_yes": "Oui, je l'ai entendu",
+ "heard_no": "Non, je n'ai rien entendu",
+ "test_ok_kiosk": "TTS fonctionne.",
+ "test_fail_steps": "Vérifiez : 1) le volume média n'est pas 0 ; 2) Google Text-to-Speech est installé et mis à jour ; 3) le pack vocal français est téléchargé dans les paramètres TTS Android."
+ },
+ "language": {
+ "title": "🌐 Langue",
+ "hint": "Sélectionnez la langue de l'interface.",
+ "label": "🌐 Langue",
+ "restart_notice": "La page sera rechargée pour appliquer la nouvelle langue."
+ },
+ "screensaver": {
+ "label": "Activer l'économiseur d'écran",
+ "card_title": "🌙 Économiseur d'écran",
+ "card_hint": "Affiche une horloge avec des informations utiles après 5 minutes d'inactivité. Désactivé par défaut.",
+ "timeout_1": "1 minute",
+ "timeout_2": "2 minutes",
+ "timeout_5": "5 minutes",
+ "timeout_10": "10 minutes",
+ "timeout_15": "15 minutes",
+ "timeout_30": "30 minutes",
+ "timeout_60": "1 heure",
+ "start_after": "⏱️ Démarrer après"
+ },
+ "scale": {
+ "title": "⚖️ Balance connectée",
+ "hint": "Connectez une balance Bluetooth via la passerelle Android pour lire automatiquement le poids.",
+ "tab": "Balance connectée",
+ "enabled": "✅ Activer la balance connectée",
+ "url_label": "🌐 URL de la passerelle WebSocket",
+ "url_placeholder": "ws://192.168.1.x:8765",
+ "url_hint": "URL affichée par l'application Android (même réseau Wi-Fi). Ex. :",
+ "test_btn": "🔗 Tester la connexion",
+ "download_btn": "📥 Télécharger la passerelle Android (APK)",
+ "download_hint": "Application Android qui connecte votre balance BLE et EverShelf.",
+ "download_sub": "Source : evershelf-scale-gateway/ dans la racine du projet",
+ "live_weight": "poids en temps réel",
+ "auto_reconnect": "🔁 Reconnexion : automatique",
+ "kiosk_title": "📡 Balance BLE intégrée dans le kiosque",
+ "kiosk_hint": "La balance est directement gérée par la passerelle BLE interne du kiosque. Pour associer un nouvel appareil, utilisez l'assistant de configuration.",
+ "kiosk_reconfigure": "🔄 Reconfigurer la balance BLE",
+ "ble_protocols": "
🔌 Protocoles BLE supportés :
Bluetooth SIG Weight Scale (0x181D)
Bluetooth SIG Body Composition (0x181B) — poids, graisse, IMC
Xiaomi Mi Body Composition Scale 2
Générique — heuristique automatique pour 100+ modèles
"
+ },
+ "kiosk": {
+ "hint": "Transformez une tablette Android en panneau EverShelf permanent avec passerelle BLE intégrée.",
+ "download_btn": "📥 Télécharger EverShelf Kiosk (APK)",
+ "download_sub": "Mode kiosque plein écran + passerelle de balance intégrée. Source : evershelf-kiosk/",
+ "native_title": "Configuration du kiosque",
+ "native_hint": "URL du serveur, balance BLE, économiseur d'écran et assistant de configuration.",
+ "native_btn": "Ouvrir la configuration du kiosque",
+ "native_tap_hint": "Appuyez sur le bouton engrenage en haut à droite",
+ "native_update_hint": "Mettez à jour l'application kiosque pour utiliser cette fonctionnalité",
+ "update_title": "Mise à jour du kiosque",
+ "check_updates_btn": "🔍 Vérifier les mises à jour",
+ "needs_update": "⚠️ Le kiosque installé ne supporte pas cette fonctionnalité. Mettez à jour l'application kiosque pour l'activer."
+ },
+ "saved": "✅ Configuration enregistrée !",
+ "saved_local": "✅ Configuration enregistrée localement",
+ "saved_local_error": "⚠️ Enregistré localement, erreur serveur : {error}",
+ "theme": {
+ "title": "🌙 Apparence",
+ "hint": "Choisissez le thème de l'interface.",
+ "label": "🌙 Thème",
+ "off": "☀️ Clair",
+ "on": "🌙 Sombre",
+ "auto": "🔄 Automatique (système)"
+ },
+ "zerowaste": {
+ "card_title": "♻️ Conseils zéro déchet",
+ "card_hint": "Pendant la cuisson, affichez des conseils pour réutiliser les déchets produits à chaque étape (épluchures, eau de cuisson, etc.). Désactivé par défaut.",
+ "label": "Afficher les conseils pendant la cuisson"
+ },
+ "backup": {
+ "tab": "Sauvegarde",
+ "local_title": "Sauvegarde locale",
+ "local_hint": "Instantané quotidien de la base de données. Configurez le nombre de jours de rétention.",
+ "enabled": "Activer la sauvegarde automatique quotidienne",
+ "retention_days": "Rétention (jours)",
+ "retention_info": "Les sauvegardes sont conservées pendant",
+ "backup_now": "Sauvegarder maintenant",
+ "backing_up": "Sauvegarde en cours…",
+ "backed_up": "Sauvegarde terminée",
+ "backup_error": "Erreur de sauvegarde",
+ "last_backup": "Dernière sauvegarde",
+ "no_backup_yet": "Aucune sauvegarde créée",
+ "list_empty": "Aucune sauvegarde disponible",
+ "restore_btn": "Restaurer",
+ "restore_confirm": "Restaurer la sauvegarde",
+ "delete_btn": "Supprimer",
+ "delete_confirm": "Supprimer la sauvegarde",
+ "gdrive_title": "Google Drive",
+ "gdrive_hint": "Sauvegardez automatiquement sur Google Drive via OAuth 2.0. Aucune bibliothèque externe requise.",
+ "gdrive_enabled": "Activer la sauvegarde Google Drive",
+ "gdrive_folder_id": "ID du dossier Drive",
+ "gdrive_folder_id_hint": "Copiez l'ID depuis l'URL du dossier Drive : …/folders/ID",
+ "gdrive_retention_days": "Rétention Drive (jours, 0=tout garder)",
+ "gdrive_test": "Tester la connexion",
+ "gdrive_ok": "Connexion réussie !",
+ "gdrive_error": "Échec de la connexion",
+ "gdrive_push_now": "Téléverser sur Drive maintenant",
+ "gdrive_pushing": "Téléversement en cours…",
+ "gdrive_pushed": "Téléversé sur Drive",
+ "gdrive_wizard_hint": "Optionnel : sauvegarde quotidienne automatique sur Google Drive via OAuth 2.0.",
+ "gdrive_skip": "Passer — configurer plus tard dans Paramètres",
+ "gdrive_client_id": "Client ID",
+ "gdrive_client_secret": "Client Secret",
+ "gdrive_redirect_uri_hint": "Ajoute http://localhost comme URI de redirection autorisé dans la Google Cloud Console. Fonctionne sur n'importe quel serveur, même sans domaine public.",
+ "gdrive_code_title": "Coller l'URL ou le code d'autorisation",
+ "gdrive_code_hint": "Après autorisation, le navigateur ouvre http://localhost et peut afficher une erreur de connexion — c'est normal. Copie l'URL dans la barre d'adresse (ex. http://localhost/?code=4%2F0A...) et colle-la ici.",
+ "gdrive_code_submit": "Confirmer",
+ "gdrive_code_empty": "Coller d'abord l'URL ou le code d'autorisation",
+ "gdrive_redirect_uri_label": "URI de redirection (ajouter dans Google Cloud Console) :",
+ "gdrive_oauth_authorize": "Autoriser avec Google",
+ "gdrive_oauth_authorized": "Autorisé",
+ "gdrive_oauth_not_authorized": "Pas encore autorisé",
+ "gdrive_oauth_window_opened": "Fenêtre ouverte — autorisez et revenez ici",
+ "gdrive_oauth_how_to": "Configurer OAuth 2.0 (étape par étape)",
+ "gdrive_oauth_steps": "
Activez l’API Google Drive : API et services → Activer les API → Google Drive API
Allez dans API et services → Identifiants → Créer des identifiants → ID client OAuth
Type d’application : Application Web ; ajoutez l’URL affichée ci-dessous comme URI de redirection autorisé
Copiez le Client ID et le Client Secret dans les champs ci-dessus et enregistrez
Cliquez sur Autoriser avec Google : connectez-vous et accordez l’accès
La fenêtre se ferme automatiquement une fois terminé et les sauvegardes sont prêtes
"
+ },
+ "shopping": {
+ "tab": "Liste de courses",
+ "title": "Liste de courses",
+ "hint": "Configurez la liste de courses intégrée ou connectez Bring!.",
+ "enable_label": "Activer la liste de courses",
+ "mode_label": "Fournisseur",
+ "mode_internal": "Intégré (sans Bring!)",
+ "mode_bring": "Bring! (application externe)",
+ "bring_section_title": "Configuration Bring!",
+ "ai_section_title": "Assistance IA",
+ "smart_suggestions_label": "Suggestions IA",
+ "forecast_label": "Prévision des produits bientôt épuisés",
+ "auto_add_label": "Ajouter automatiquement quand",
+ "auto_add_suffix": "restant en stock (0 = seulement quand épuisé)"
+ },
+ "ha": {
+ "tab": "Home Assistant",
+ "title": "Home Assistant",
+ "hint": "Connectez EverShelf à Home Assistant pour les automations, les notifications push et les capteurs REST.",
+ "enabled": "Activer l'intégration Home Assistant",
+ "connection_title": "Connexion",
+ "url_label": "URL Home Assistant",
+ "url_placeholder": "http://192.168.1.50:8123",
+ "url_hint": "URL de base de votre instance Home Assistant.",
+ "token_label": "Jeton d'accès longue durée",
+ "token_hint": "Générez depuis Profil HA → Sécurité → Jetons d'accès longue durée.",
+ "token_placeholder": "eyJhbGci...",
+ "token_saved": "Jeton enregistré (masqué pour des raisons de sécurité)",
+ "test_btn": "Tester la connexion",
+ "test_ok": "Connecté à {version}",
+ "test_fail": "Connexion échouée : {error}",
+ "test_bad_token": "HA accessible mais le jeton est invalide",
+ "testing": "Test en cours…",
+ "error_no_url": "Veuillez d'abord saisir l'URL de Home Assistant.",
+ "tts_title": "TTS sur enceinte connectée",
+ "tts_hint": "Lisez les étapes de recette sur un media player Home Assistant.",
+ "tts_entity_label": "Entity ID du lecteur multimédia",
+ "tts_entity_placeholder": "media_player.salon",
+ "tts_entity_hint": "Entity ID du lecteur multimédia HA. Disponible dans HA : Outils développeur → États.",
+ "tts_platform_label": "Plateforme TTS",
+ "tts_platform_speak": "tts.speak (recommandé)",
+ "tts_platform_notify": "notify.* (service de notification)",
+ "tts_apply_btn": "Appliquer le preset HA à l'onglet TTS",
+ "tts_apply_hint": "Pré-remplit l'onglet TTS avec l'URL et le jeton de Home Assistant.",
+ "tts_preset_applied": "Preset HA appliqué à l'onglet TTS.",
+ "webhook_title": "Automations Webhook",
+ "webhook_hint": "Envoyez des données à Home Assistant lors d'événements dans le garde-manger.",
+ "webhook_id_label": "ID Webhook",
+ "webhook_id_placeholder": "evershelf_webhook_abc123",
+ "webhook_id_hint": "ID du webhook créé dans HA. Copiez depuis : HA → Paramètres → Automations → Créer → Déclencheur Webhook.",
+ "webhook_events_label": "Notifier pour ces événements",
+ "event_expiry": "Produits expirant bientôt (quotidien)",
+ "event_shopping": "Article ajouté à la liste de courses",
+ "event_stock": "Niveau de stock mis à jour",
+ "expiry_days_label": "Préavis d'expiration (jours)",
+ "expiry_days_hint": "Envoyer l'alerte d'expiration N jours avant la date d'expiration.",
+ "webhook_help": "Dans HA : Paramètres → Automations → Créer → Déclencheur : Webhook → copier l'ID généré.",
+ "notify_title": "Notifications push",
+ "notify_hint": "Envoyez des notifications push sur votre téléphone via un service notify de Home Assistant.",
+ "notify_service_label": "Service notify",
+ "notify_service_placeholder": "notify.mobile_app_mon_telephone",
+ "notify_service_hint": "Nom du service notify HA. Laissez vide pour désactiver.",
+ "sensor_title": "Capteurs REST",
+ "sensor_hint": "Ajoutez à configuration.yaml pour créer des capteurs EverShelf dans Home Assistant.",
+ "sensor_copy_btn": "Copier le YAML",
+ "sensor_copied": "YAML copié dans le presse-papiers !",
+ "save_btn": "Enregistrer les paramètres HA",
+ "ha_hint": "Si vous utilisez Home Assistant, utilisez l'onglet Home Assistant pour configurer TTS, webhooks et capteurs."
}
+ },
+ "expiry": {
+ "today": "AUJOURD'HUI",
+ "tomorrow": "Demain",
+ "days": "{days} jours",
+ "expired_days": "il y a {days}j",
+ "expired_yesterday": "Hier",
+ "expired_today": "Aujourd'hui",
+ "badge_today": "⚠️ Expire aujourd'hui !",
+ "badge_tomorrow": "⏰ Demain",
+ "badge_tomorrow_long": "⏰ Expire demain",
+ "badge_days": "⏰ {n} jours",
+ "badge_expired_ago": "⚠️ Périmé il y a {n}j",
+ "badge_expired": "⛔ Périmé !",
+ "badge_stable": "✅ Stable",
+ "badge_expiring_short": "⏰ Exp. dans {n}j",
+ "badge_ok_still": "✅ Encore {n}j",
+ "badge_expires_red": "🔴 Exp. dans {n}j",
+ "badge_expires_yellow": "🟡 Exp. dans {n}j",
+ "badge_expired_bare": "⚠️ Périmé",
+ "badge_expires_warn": "⚠️ Exp. dans {n}j",
+ "badge_days_left": "⏳ ~{n}j restants",
+ "days_approx": "~{n} jours",
+ "weeks_approx": "~{n} semaines",
+ "months_approx": "~{n} mois",
+ "years_approx": "~{n} ans",
+ "expired_today_long": "Périmé aujourd'hui",
+ "expired_ago_long": "Périmé il y a {n} jours",
+ "expired_suffix": "— Périmé !",
+ "expired_suffix_ok": "— Périmé (encore ok)",
+ "expired_suffix_warning": "— Périmé (vérifier d'abord)",
+ "opened_ago_long": "Ouvert il y a {n} jours",
+ "opened_today_long": "Ouvert aujourd'hui",
+ "opened_suffix": "— Ouvert depuis trop longtemps !",
+ "opened_suffix_ok": "— Ouvert (encore ok)",
+ "opened_suffix_warning": "— Ouvert (vérifier d'abord)",
+ "days_compact": "{n}j",
+ "badge_check_soon": "Vérifier prochainement"
+ },
+ "status": {
+ "ok": "OK",
+ "check": "Vérifier",
+ "discard": "Jeter",
+ "tip_freezer_ok": "Au congélateur : encore sûr (~{n}j de marge)",
+ "tip_freezer_check": "Au congélateur depuis longtemps, peut avoir perdu en qualité. Consommer rapidement",
+ "tip_freezer_danger": "Au congélateur trop longtemps, risque de brûlure de congélation et de dégradation",
+ "tip_highRisk_check": "Périmé récemment, vérifiez l'odeur et l'aspect avant de consommer",
+ "tip_highRisk_danger": "Produit périssable périmé : jeter pour la sécurité",
+ "tip_medRisk_check1": "Vérifiez l'aspect et l'odeur avant de consommer",
+ "tip_medRisk_check2": "Périmé depuis un moment, vérifiez soigneusement avant utilisation",
+ "tip_medRisk_danger": "Trop longtemps depuis la péremption, mieux vaut jeter",
+ "tip_lowRisk_ok": "Produit longue conservation, encore sûr à consommer",
+ "tip_lowRisk_check": "Périmé depuis plus d'un mois, vérifiez l'intégrité de l'emballage",
+ "tip_lowRisk_danger": "Périmé depuis trop longtemps, mieux vaut ne pas risquer"
+ },
+ "toast": {
+ "product_saved": "Produit enregistré !",
+ "product_created": "Produit créé !",
+ "product_updated": "✅ Produit mis à jour !",
+ "product_removed": "Produit supprimé",
+ "updated": "Mis à jour !",
+ "quantity_confirmed": "✓ Quantité confirmée",
+ "added_to_inventory": "✅ {name} ajouté !",
+ "removed_from_list": "✅ {name} retiré de la liste !",
+ "removed_from_list_short": "Retiré de la liste",
+ "added_to_shopping": "🛒 Ajouté à la liste de courses !",
+ "removed_from_shopping": "🛒 Retiré de la liste de courses",
+ "finished_to_bring": "🛒 Produit terminé → ajouté à Bring !",
+ "thrown_away": "🗑️ {name} jeté !",
+ "thrown_away_partial": "🗑️ {qty} {unit} de {name} jeté(s)",
+ "finished_all": "📤 {name} terminé !",
+ "product_finished_confirmed": "✅ Supprimé — ajoutez-le à nouveau lors du réapprovisionnement",
+ "appliance_added": "Appareil ajouté",
+ "item_added": "{name} ajouté"
+ },
+ "antiwaste": {
+ "title": "🌱 Rapport anti-gaspi",
+ "grade_label": "Note",
+ "you": "Vous",
+ "avg_label": "Moy.",
+ "better": "🎉 Vous gaspillez {diff}% de moins que la moyenne {country} !",
+ "worse": "⚠️ Vous gaspillez plus que la moyenne {country}. Des progrès sont possibles !",
+ "on_par": "→ Vous êtes dans la moyenne {country}. Vous pouvez faire mieux !",
+ "saved_money": "~{amount}/mois économisé",
+ "saved_meals": "~{n} repas sauvés",
+ "saved_co2": "{n} kg CO₂ évités",
+ "trend_title": "Tendance (3 derniers mois)",
+ "months_ago_2": "-60 jours",
+ "months_ago_1": "-30 jours",
+ "this_month": "Maintenant",
+ "country_it": "Moy. italienne",
+ "country_de": "Moy. allemande",
+ "country_en": "Moy. américaine",
+ "source": "Sources : REDUCE, Eurostat, USDA 2021",
+ "live_on": "Données en direct",
+ "live_off": "Hors ligne",
+ "meals": "repas",
+ "annual_info": "📅 Vous ~{you} kg/an · moy. ~{avg} kg/an",
+ "badge_rate": "taux de perte",
+ "badge_saved_money": "économisé vs moy.",
+ "badge_wasted": "articles perdus",
+ "badge_better": "moins que la moy."
+ },
+ "error": {
+ "generic": "Erreur",
+ "network": "Erreur réseau",
+ "no_api_key": "Configurez la clé API dans les paramètres",
+ "loading": "Erreur de chargement du produit",
+ "not_found": "Produit introuvable",
+ "not_found_manual": "Produit introuvable. Entrez-le manuellement.",
+ "search": "Erreur de recherche. Réessayez.",
+ "search_short": "Erreur de recherche",
+ "save": "Erreur d'enregistrement",
+ "connection": "Erreur de connexion",
+ "camera": "Impossible d'accéder à la caméra",
+ "bring_add": "Erreur d'ajout à Bring !",
+ "bring_connection": "Erreur de connexion à Bring !",
+ "identification": "Erreur d'identification",
+ "ai_quota": "Quota IA épuisé. Réessayez dans quelques minutes.",
+ "barcode_empty": "Entrez un code-barres",
+ "barcode_format": "Le code-barres ne doit contenir que des chiffres (4-14 chiffres)",
+ "min_chars": "Tapez au moins 2 caractères",
+ "not_in_inventory": "Produit absent de l'inventaire",
+ "appliance_exists": "L'appareil existe déjà",
+ "already_exists": "Existe déjà",
+ "network_retry": "Erreur de connexion. Réessayez.",
+ "select_items": "Sélectionnez au moins un produit",
+ "server_offline": "Connexion au serveur perdue",
+ "server_restored": "Connexion au serveur rétablie",
+ "server_retry": "Réessayer",
+ "unknown": "Erreur inconnue",
+ "prefix": "Erreur",
+ "no_inventory_entry": "Aucune entrée d'inventaire trouvée",
+ "offline_title": "Aucune connexion",
+ "offline_subtitle": "L'app ne peut pas atteindre le serveur. Vérifiez votre connexion Wi-Fi.",
+ "offline_checking": "Vérification de la connexion…",
+ "offline_restored": "Connexion rétablie !",
+ "offline_continue": "Continuer en mode hors ligne",
+ "offline_reading_cache": "Lecture depuis le cache local",
+ "offline_ops_pending": "{n} opérations en attente",
+ "offline_synced": "{n} opérations synchronisées",
+ "offline_ai_disabled": "Indisponible hors ligne",
+ "offline_cache_ready": "Offline — {n} produits en cache"
+ },
+ "confirm_placeholder_search": null,
+ "confirm": {
+ "remove_item": "Voulez-vous vraiment supprimer ce produit de l'inventaire ?",
+ "kiosk_exit": "Quitter le mode kiosque ?",
+ "cancel": "Annuler",
+ "proceed": "Confirmer",
+ "discard_one": "Jeter 1 pièce"
+ },
+ "location": {
+ "dispensa": "Garde-manger",
+ "frigo": "Réfrigérateur",
+ "freezer": "Congélateur"
+ },
+ "edit": {
+ "title": "Modifier {name}",
+ "unknown_hint": "Entrez le nom du produit et les informations",
+ "label_name": "🏷️ Nom du produit",
+ "choose_location_title": "Quel emplacement ?",
+ "choose_location_hint": "Choisissez l'emplacement à modifier :",
+ "confirm_large_qty": "Vous définissez la quantité à {qty} {unit}. Cela semble inhabituellement élevé. Confirmer ?"
+ },
+ "screensaver": {
+ "recipe_btn": "Recettes",
+ "scan_btn": "Scanner un produit"
+ },
+ "days": {
+ "mon": "Lundi",
+ "tue": "Mardi",
+ "wed": "Mercredi",
+ "thu": "Jeudi",
+ "fri": "Vendredi",
+ "sat": "Samedi",
+ "sun": "Dimanche",
+ "mon_short": "Lun",
+ "tue_short": "Mar",
+ "wed_short": "Mer",
+ "thu_short": "Jeu",
+ "fri_short": "Ven",
+ "sat_short": "Sam",
+ "sun_short": "Dim"
+ },
+ "meal_types": {
+ "lunch": "Déjeuner",
+ "dinner": "Dîner",
+ "colazione": "Petit-déjeuner",
+ "merenda": "Goûter",
+ "dolce": "Dessert",
+ "succo": "Jus de fruits",
+ "pranzo": "Déjeuner",
+ "cena": "Dîner"
+ },
+ "scale": {
+ "status_connected": "Balance connectée",
+ "status_searching": "Passerelle connectée, attente de la balance…",
+ "status_disconnected": "Passerelle de balance inaccessible",
+ "status_error": "Erreur de connexion à la passerelle",
+ "not_connected": "Passerelle de balance non connectée",
+ "read_btn": "⚖️ Lire depuis la balance",
+ "reading_title": "Lecture de la balance",
+ "place_on_scale": "Posez le produit sur la balance…",
+ "waiting_stable": "Le poids sera capturé automatiquement une fois la lecture stable.",
+ "no_url": "Entrez l'URL de la passerelle",
+ "testing": "⏳ Test de connexion…",
+ "connected_ok": "Connexion à la passerelle réussie !",
+ "timeout": "Délai dépassé : pas de réponse de la passerelle",
+ "error_connect": "Impossible de se connecter à la passerelle",
+ "tab": "Balance connectée",
+ "low_weight": "Poids < 10 g · entrez manuellement\n(la lecture automatique nécessite au moins 10 g)",
+ "density_hint": "(densité {density} g/ml)",
+ "ml_hint": "(sera converti en ml)",
+ "weight_detected": "Poids détecté — attendez 10s de stabilité…",
+ "weight_too_low": "Poids trop faible — attente…",
+ "stable": "✓ Stable",
+ "auto_confirm": "✅ {val} {unit} — confirmation automatique dans 5s (appuyez pour annuler)",
+ "cancelled_replace": "Annulé — replacez l'ingrédient sur la balance pour reprendre"
+ },
+ "prediction": {
+ "expected_qty": "Attendu : {expected} {unit}",
+ "actual_qty": "Actuel : {actual} {unit}",
+ "check_suggestion": "Vérifiez ou pesez la quantité restante"
+ },
+ "date": {
+ "today": "📅 Aujourd'hui",
+ "yesterday": "📅 Hier"
+ },
+ "scanner": {
+ "title_barcode": "🔖 Scanner le code-barres",
+ "barcode_hint": "Cadrez le code-barres du produit",
+ "barcode_manual_placeholder": "Ou entrez manuellement...",
+ "barcode_use_btn": "✅ Utiliser ce code",
+ "ai_identifying": "🤖 Identification du produit...",
+ "ai_analyzing": "🤖 Analyse IA en cours...",
+ "product_label_hint": "Cadrez l'étiquette du produit",
+ "expiry_label_hint": "Cadrez la date de péremption imprimée sur le produit",
+ "capture_btn": "📸 Capturer",
+ "capture_photo_btn": "📸 Prendre une photo",
+ "retake_btn": "🔄 Reprendre",
+ "camera_error_hint": "Assurez-vous d'utiliser HTTPS et d'avoir accordé les permissions caméra. Vous pouvez entrer le code-barres manuellement ou utiliser l'identification IA.",
+ "no_barcode": "Pas de code-barres",
+ "save_new_btn": "🆕 Aucun de ceux-ci — enregistrer comme nouveau"
+ },
+ "lowstock": {
+ "title": "⚠️ Stock faible !",
+ "message": "{name} est presque épuisé — il ne reste que {qty}.",
+ "question": "Voulez-vous l'ajouter à la liste de courses ?",
+ "yes": "🛒 Oui, ajouter à Bring !",
+ "no": "Non, pour l'instant ça va"
+ },
+ "move": {
+ "title": "📦 Déplacer le reste ?",
+ "question": "Voulez-vous déplacer {thing} de {name} vers un autre emplacement ?",
+ "question_short": "Voulez-vous déplacer {thing} vers un autre emplacement ?",
+ "thing_opened": "l'emballage ouvert",
+ "thing_rest": "le reste",
+ "stay_btn": "Non, rester dans {location}",
+ "moved_toast": "📦 Emballage ouvert déplacé vers {location}",
+ "vacuum_restore": "🫙 Restaurer sous vide",
+ "vacuum_seal_rest": "🔒 Mettre le reste sous vide"
+ },
+ "nova": {
+ "1": "Non transformé",
+ "2": "Ingrédient culinaire",
+ "3": "Transformé",
+ "4": "Ultra-transformé"
+ },
+ "meal_plan_types": {
+ "pasta": "Pâtes",
+ "riso": "Riz",
+ "carne": "Viande",
+ "pesce": "Poisson",
+ "legumi": "Légumineuses",
+ "uova": "Œufs",
+ "formaggio": "Fromage",
+ "pizza": "Pizza",
+ "affettati": "Charcuterie",
+ "verdure": "Légumes",
+ "zuppa": "Soupe",
+ "insalata": "Salade",
+ "pane": "Pain/Sandwich",
+ "dolce": "Dessert",
+ "libero": "Libre"
+ },
+ "meal_sub": {
+ "dolce_torta": "Gâteau",
+ "dolce_crema": "Crème / Pudding",
+ "dolce_crumble": "Crumble / Tarte",
+ "dolce_biscotti": "Biscuits / Pâtisseries",
+ "dolce_frutta": "Dessert aux fruits",
+ "succo_dolce": "Sucré / Fruité",
+ "succo_energizzante": "Énergisant",
+ "succo_detox": "Détox / Vert",
+ "succo_rinfrescante": "Rafraîchissant",
+ "succo_vitaminico": "Vitaminé / Agrumes"
+ },
+ "meal_plan": {
+ "reset_success": "Plan hebdomadaire réinitialisé",
+ "not_available": "non disponible dans le garde-manger",
+ "suggested_by": "suggéré par le plan hebdomadaire"
+ },
+ "nutrition": {
+ "title": "🥗 Analyse alimentaire",
+ "score_excellent": "😄 Excellent",
+ "score_good": "🙂 Bien",
+ "score_improve": "😬 Améliorable",
+ "label_health": "🌿 Santé",
+ "label_variety": "🎨 Variété",
+ "label_fresh": "❄️ Frais",
+ "source": "Basé sur {n} produits dans votre garde-manger · EverShelf",
+ "products_count": "produits",
+ "today_title": "🥗 Votre garde-manger aujourd'hui",
+ "products_n": "{n} produits",
+ "macros_title": "Macronutriments estimés",
+ "macros_proteins": "Protéines",
+ "macros_carbs": "Glucides",
+ "macros_fat": "Lipides",
+ "macros_fiber": "Fibres",
+ "macros_source": "Estimation basée sur {n} produits en stock"
+ },
+ "facts": {
+ "greeting_morning": "Bonjour",
+ "greeting_afternoon": "Bon après-midi",
+ "greeting_evening": "Bonsoir",
+ "pantry_waiting": "{greeting} ! Votre garde-manger vous attend.",
+ "expired_one": "Vous avez 1 produit périmé dans votre garde-manger. Vérifiez-le !",
+ "expired_many": "Vous avez {n} produits périmés dans votre garde-manger. Vérifiez-les !",
+ "expired_list": "Produits périmés : {names}",
+ "expired_list_more": "et {n} autres",
+ "freezer_expired_ok": "{name} est périmé, mais étant au congélateur il peut encore être bon ! Vérifiez.",
+ "freezer_expired_old": "{name} au congélateur est périmé depuis trop longtemps. Mieux vaut le jeter.",
+ "fridge_expired_one": "Vous avez 1 produit périmé dans le réfrigérateur !",
+ "fridge_expired_many": "Vous avez {n} produits périmés dans le réfrigérateur !",
+ "expiring_today": "{name} expire aujourd'hui ! Utilisez-le immédiatement.",
+ "expiring_tomorrow": "{name} expire demain. Planifiez !",
+ "expiring_days": "{name} expire dans {days} jours.",
+ "expiring_many": "Vous avez {n} produits qui expirent bientôt.",
+ "expiring_this_week": "{n} produits expirent cette semaine. Planifiez vos repas en conséquence !",
+ "expiring_item_loc": "{name} ({loc}) expire dans {days} {dayslabel}.",
+ "expiring_this_month": "{n} produits expireront ce mois-ci.",
+ "shopping_add": "Ajouter à la liste : {names} 🛒",
+ "shopping_more": "et {n} autres",
+ "shopping_empty": "Liste de courses vide. Tout est en stock ! ✅",
+ "in_fridge": "Dans le réfrigérateur : {name}.",
+ "in_freezer": "Dans le congélateur : {name}. N'oubliez pas !",
+ "top_category": "Catégorie principale : {icon} {cat} avec {n} produits.",
+ "cat_meat": "Vous avez {n} produits carnés. 🥩",
+ "cat_dairy": "Vous avez {n} produits laitiers chez vous. 🥛",
+ "cat_veggies": "Vous avez {n} types de légumes. Super pour la santé ! 🥬",
+ "cat_fruit": "Vous avez {n} types de fruits. 🍎",
+ "cat_drinks": "Vous avez {n} boissons disponibles. 🥤",
+ "cat_frozen": "Vous avez {n} articles surgelés. ❄️",
+ "cat_pasta": "Vous avez {n} types de pâtes. 🍝 Et si on faisait une carbonara ?",
+ "cat_canned": "Vous avez {n} conserves dans le garde-manger. 🥫",
+ "cat_snacks": "Vous avez {n} snacks. Résistez à la tentation ! 🍪",
+ "cat_condiments": "Vous avez {n} condiments disponibles. 🧂",
+ "item_random": "Le saviez-vous ? Vous avez {name} dans {loc}.",
+ "item_qty": "{name} : vous en avez {qty}.",
+ "no_expiry_count": "{n} produits n'ont pas de date de péremption.",
+ "furthest_expiry": "Le produit avec la date de péremption la plus lointaine est {name} : {months} mois.",
+ "high_qty": "Vous avez un beau stock de {name} : {qty} !",
+ "low_qty_item": "{name} est presque épuisé. L'ajouter à votre liste de courses ?",
+ "low_qty_count": "{n} produits sont presque épuisés.",
+ "morning_bread": "Bonjour ! Vous avez du pain pour le petit-déjeuner. 🍞",
+ "morning_milk": "Y a-t-il du lait dans le réfrigérateur pour un cappuccino ? ☕🥛",
+ "morning_fruit": "Bonjour ! Des fruits frais sont un excellent début de journée. 🍎",
+ "noon_pasta": "C'est l'heure du déjeuner… Et si on faisait un bon bol de pâtes ? 🍝",
+ "noon_salad": "Une salade fraîche pour le déjeuner ? Vous avez {n} légumes ! 🥗",
+ "evening_meat": "Pour le dîner, vous pourriez utiliser la viande que vous avez. 🥩",
+ "evening_fish": "Et si on mangeait du poisson ce soir ? 🐟",
+ "evening_expiring": "Vous avez {n} produits qui expirent cette semaine — utilisez-les ce soir !",
+ "night_reminder": "Bonne nuit ! N'oubliez pas d'utiliser demain : {names}.",
+ "weekly_balance": "Bilan hebdomadaire : +{in} ajoutés, −{out} consommés.",
+ "weekly_added": "Vous avez ajouté {n} produits cette semaine.",
+ "weekly_consumed": "Vous avez consommé {n} produits cette semaine. Bravo !",
+ "tip_freezer": "💡 Les produits surgelés durent bien plus longtemps que la date de péremption.",
+ "tip_bread": "💡 Le pain congelé garde sa fraîcheur pendant des semaines.",
+ "tip_fifo": "💡 Pour éviter le gaspillage, utilisez en premier les produits les plus proches de la péremption (FIFO).",
+ "tip_meat": "💡 La viande au congélateur peut se conserver jusqu'à 6 mois en toute sécurité.",
+ "tip_no_refreeze": "💡 Ne jamais recongeler un produit décongelé. Cuisinez-le immédiatement !",
+ "tip_fridge": "💡 Un réfrigérateur bien rangé vous fait gagner du temps et de l'argent.",
+ "tip_canned": "💡 Les conserves ouvertes doivent aller au réfrigérateur et être consommées en quelques jours.",
+ "top_brand": "La marque la plus courante dans votre garde-manger est {brand} avec {n} produits.",
+ "combo_pasta": "Vous avez des pâtes et des condiments : prêt pour un premier plat ! 🍝",
+ "combo_sandwich": "Pain et viande : un sandwich rapide est toujours une bonne idée ! 🥪",
+ "combo_balanced": "Légumes et viande : vous avez tout pour un repas équilibré ! 🥗🥩",
+ "pantry_empty": "Le garde-manger est vide ! Il est temps de faire les courses. 🛒",
+ "pantry_empty_scan": "Aucun produit enregistré. Scannez quelque chose pour commencer !",
+ "location_distribution": "Distribution : {parts}",
+ "day": "jour",
+ "days": "jours"
+ },
+ "kiosk_session": {
+ "first_item": "Premier article : {name} !",
+ "items_two_four": "{n} articles — on démarre 🚀",
+ "items_five_nine": "{n} articles — bon rythme ! 💪",
+ "items_ten_twenty": "{n} articles — presque un record 🏆",
+ "items_twenty_plus": "{n} articles — courses épiques ! 🛒🔥",
+ "duplicates_one": "1 doublon (même article deux fois)",
+ "duplicates_many": "{n} doublons (pris plusieurs fois)",
+ "top_category": "Catégorie principale : {cat} ({count}×)",
+ "items_fallback": "{n} article{plural} ajouté{plural}"
+ },
+ "kiosk": {
+ "check_btn": "🔍 Vérifier les mises à jour",
+ "checking": "⏳ Vérification…",
+ "error_check": "Erreur lors de la vérification des mises à jour",
+ "error_start_install": "Erreur au démarrage de l'installation",
+ "version_installed": "Installé : {v}",
+ "update_available": "⬆️ Nouvelle version disponible : {latest} (installée : {current})",
+ "up_to_date": "✅ Vous êtes à jour — version {v}",
+ "too_old": "⚠️ Le kiosque installé est trop ancien pour la vérification automatique des mises à jour. Appuyez sur le bouton ci-dessous pour télécharger et installer la nouvelle version directement.",
+ "manual_install": "⚠️ Ce kiosque ne supporte pas l'installation automatique. Procédure manuelle : 1. Quittez le kiosque (bouton ✕ en haut à gauche) 2. Désinstallez l'application EverShelf Kiosk 3. Téléchargez et installez le nouvel APK depuis GitHub :",
+ "starting_download": "⏳ Démarrage du téléchargement…",
+ "install_btn": "⬇️ Installer la mise à jour",
+ "exit_title": "Quitter le kiosque",
+ "refresh_title": "Actualiser la page"
+ },
+ "update": {
+ "new_version": "Nouvelle version",
+ "btn": "Mettre à jour"
+ },
+ "gemini": {
+ "chat_title": "Discussion avec Gemini",
+ "not_configured": "🤖 Gemini non configuré — définissez GEMINI_API_KEY dans les paramètres"
+ },
+ "appliances": {
+ "empty": "Aucun appareil ajouté"
+ },
+ "about": {
+ "title": "À propos",
+ "version": "Version",
+ "report_bug": "Signaler un bug",
+ "report_bug_hint": "Quelque chose ne fonctionne pas ? Envoyez-nous un rapport directement depuis l'application.",
+ "report_bug_modal_title": "Signaler un bug",
+ "report_type_bug": "Bug",
+ "report_type_feature": "Fonctionnalité",
+ "report_type_question": "Question",
+ "report_field_title": "Titre",
+ "report_field_title_ph": "Brève description du problème",
+ "report_field_desc": "Description",
+ "report_field_desc_ph": "Décrivez le problème en détail…",
+ "report_field_steps": "Étapes pour reproduire (optionnel)",
+ "report_field_steps_ph": "1. Aller à…\n2. Appuyer sur…\n3. Voir l'erreur…",
+ "report_auto_info": "Joint automatiquement : version {version}, langue {lang}.",
+ "report_send_btn": "Envoyer le rapport",
+ "report_bug_sending": "Envoi…",
+ "report_bug_sent": "Rapport envoyé — merci !",
+ "report_bug_error": "Impossible d'envoyer le rapport. Vérifiez votre connexion.",
+ "changelog": "Journal des modifications",
+ "github": "Dépôt GitHub"
+ },
+ "export": {
+ "title": "Exporter l'inventaire",
+ "hint": "Téléchargez l'inventaire actuel en CSV ou ouvrez la version imprimable (PDF).",
+ "btn_csv": "Télécharger CSV",
+ "btn_pdf": "PDF / Imprimer",
+ "btn_title": "Exporter"
+ },
+ "startup": {
+ "connecting": "Connexion au serveur...",
+ "check_php_memory": "Mémoire PHP",
+ "check_php_timeout": "Délai PHP",
+ "check_php_upload": "Upload PHP",
+ "check_data_dir": "Dossier de données",
+ "check_rate_limits": "Dossier rate limits",
+ "check_backups": "Dossier sauvegardes",
+ "check_write_test": "Test d'écriture disque",
+ "check_disk_space": "Espace disque",
+ "check_db_connect": "Connexion base de données",
+ "check_db_tables": "Tables de la BDD",
+ "check_db_integrity": "Intégrité BDD",
+ "check_db_wal": "Mode WAL",
+ "check_db_size": "Taille de la BDD",
+ "check_db_rows": "Données inventaire",
+ "check_env": "Fichier .env",
+ "check_gemini": "Clé Gemini AI",
+ "check_bring_creds": "Identifiants Bring!",
+ "check_bring_token": "Token Bring!",
+ "check_curl_ssl": "cURL SSL",
+ "check_internet": "Connexion internet",
+ "fresh_install": "nouvelle installation",
+ "warnings_found": "avertissements détectés",
+ "all_ok": "Système OK",
+ "critical_error_short": "Erreur critique",
+ "critical_error": "Erreur critique : l'application ne peut pas démarrer. Vérifiez les logs.",
+ "error_network": "Impossible de contacter le serveur. Vérifiez votre connexion réseau.",
+ "retry": "Réessayer",
+ "syncing_local": "Synchronisation des données locales...",
+ "sync_done": "Données locales synchronisées"
+ },
+ "stats_monthly": {
+ "title": "Statistiques Mensuelles",
+ "consumed": "produits utilisés",
+ "trend_up": "+{pct}% vs {prev}",
+ "trend_down": "-{pct}% vs {prev}",
+ "trend_same": "même rythme que le mois dernier",
+ "added": "ajoutés",
+ "wasted": "gaspillés",
+ "top_used": "le plus utilisé",
+ "top_cats": "Catégories principales",
+ "source": "Historique des transactions · mois en cours"
+ }
}
\ No newline at end of file
diff --git a/translations/it.json b/translations/it.json
index d725aff..b597cd2 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -1,1457 +1,1462 @@
{
- "app": {
- "name": "EverShelf",
- "loading": "Caricamento..."
- },
- "nav": {
- "title": "EverShelf",
- "home": "Home",
- "inventory": "Dispensa",
- "recipes": "Ricette",
- "shopping": "Spesa",
- "log": "Storico",
- "settings": "Config"
- },
- "btn": {
- "back": "← Indietro",
- "save": "💾 Salva",
- "cancel": "✕ Annulla",
- "close": "Chiudi",
- "add": "✅ Aggiungi",
- "delete": "Elimina",
- "edit": "✏️ Modifica",
- "use": "Usa",
- "edit_item": "Modifica",
- "search": "🔍 Cerca",
- "go": "✅ Vai",
- "toggle_password": "👁️ Mostra/Nascondi",
- "load_more": "Carica altri...",
- "save_config": "💾 Salva Configurazione",
- "save_product": "💾 Salva Prodotto",
- "restart": "↺ Ricomincia",
- "reset_default": "↺ Ripristina default",
- "save_info": "💾 Salva informazioni",
- "retry": "🔄 Riprova",
- "yes_short": "Sì",
- "no_short": "No"
- },
- "form": {
- "select_placeholder": "-- Seleziona --"
- },
- "locations": {
- "dispensa": "Dispensa",
- "frigo": "Frigo",
- "freezer": "Freezer",
- "altro": "Altro"
- },
- "categories": {
- "latticini": "Latticini",
- "carne": "Carne",
- "pesce": "Pesce",
- "frutta": "Frutta",
- "verdura": "Verdura",
- "pasta": "Pasta & Riso",
- "pane": "Pane & Forno",
- "surgelati": "Surgelati",
- "bevande": "Bevande",
- "condimenti": "Condimenti",
- "snack": "Snack & Dolci",
- "conserve": "Conserve",
- "cereali": "Cereali & Legumi",
- "igiene": "Igiene",
- "pulizia": "Pulizia Casa",
- "altro": "Altro",
- "select": "-- Seleziona --"
- },
- "units": {
- "pz": "pz",
- "conf": "conf",
- "g": "g",
- "ml": "ml",
- "pieces": "Pezzi",
- "grams": "Grammi",
- "box": "Confezione",
- "boxes": "Confezioni",
- "millilitres": "Millilitri",
- "from": "da"
- },
- "shopping_sections": {
- "frutta_verdura": "Frutta & Verdura",
- "carne_pesce": "Carne & Pesce",
- "latticini": "Latticini & Fresco",
- "pane_dolci": "Pane & Dolci",
- "pasta": "Pasta & Cereali",
- "conserve": "Conserve & Salse",
- "surgelati": "Surgelati",
- "bevande": "Bevande",
- "pulizia_igiene": "Pulizia & Igiene",
- "altro": "Altro"
- },
- "dashboard": {
- "expired_title": "🚫 Scaduti",
- "expiring_title": "⏰ Prossime Scadenze",
- "stats_period": "📊 Ultimi 30 giorni",
- "opened_title": "📦 Prodotti Aperti",
- "review_title": "🔍 Da revisionare",
- "review_hint": "Quantità che sembrano anomale. Conferma se corrette o modifica.",
- "quick_recipe": "Ricetta veloce con prodotti in scadenza",
- "banner_review_title": "Quantità anomala",
- "banner_review_action_ok": "È corretto",
- "banner_review_action_finish": "🗑️ È finito tutto",
- "banner_review_action_edit": "Correggi",
- "banner_review_action_weigh": "Pesa",
- "banner_review_dismiss": "Ignora",
- "banner_prediction_title": "Consumo da verificare",
- "banner_prediction_hint": "La stima di consumo si adatta ai dati recenti: verifica solo se la quantità corrente è corretta.",
- "banner_prediction_action_confirm": "Confermo {qty} {unit}",
- "banner_prediction_action_weigh": "Pesa ora",
- "banner_prediction_action_edit": "Aggiorna quantità",
- "banner_expired_title": "Prodotto scaduto",
- "banner_expired_today": "Scaduto oggi",
- "banner_expired_days": "Scaduto da {days} giorni",
- "banner_expired_action_use": "Usa comunque",
- "banner_expired_action_finished": "L'ho finito!",
- "banner_expired_action_throw": "L'ho buttato",
- "banner_expired_action_edit": "Correggi data",
- "banner_expired_action_modify": "Modifica",
- "banner_expired_action_vacuum": "Metti sottovuoto",
- "banner_anomaly_action_edit": "Correggi inventario",
- "banner_anomaly_action_dismiss": "La quantità è giusta",
- "banner_no_expiry_title": "Scadenza mancante: {name}",
- "banner_no_expiry_detail": "Questo prodotto non ha una data di scadenza. Vuoi aggiungerla o confermare che non scade?",
- "banner_no_expiry_action_set": "Imposta scadenza",
- "banner_no_expiry_action_dismiss": "Non scade ✓",
- "banner_no_expiry_toast_dismissed": "Segnato come 'non scade'",
- "banner_expiring_title": "In scadenza",
- "banner_expiring_today": "Scade oggi!",
- "banner_expiring_tomorrow": "Scade domani",
- "banner_expiring_days": "Scade tra {days} giorni",
- "banner_expiring_action_use": "Usa ora",
- "banner_finished_title": "è finito?",
- "banner_finished_detail": "Ho registrato che {name} ha toccato quota zero. È davvero finito o hai ancora delle scorte?",
- "banner_finished_action_yes": "Sì, è finito",
- "banner_finished_action_no": "No, ne ho ancora",
- "banner_review_unusual_pkg_title": "Confezione insolita",
- "banner_review_unusual_pkg_detail": "Hai impostato una confezione da {qty} {unit} — la dimensione sembra molto alta. Controlla se è corretta o modifica.",
- "banner_review_low_qty_title": "Quantità molto bassa",
- "banner_review_low_qty_detail": "Hai solo {qty} in inventario — sembra poco, potrebbe essere un errore. Conferma se è corretto.",
- "banner_review_high_qty_title": "Quantità insolitamente alta",
- "banner_review_high_qty_detail": "Hai {qty} in inventario — la cifra sembra molto alta. Conferma se è corretto o correggi.",
- "banner_prediction_rate_day": "Media ~{n} {unit}/giorno",
- "banner_prediction_rate_week": "Media ~{n} {unit}/settimana",
- "banner_prediction_days_ago": "{n} giorni fa hai rifornito",
- "banner_prediction_more": "stima precedente: {expected} {unit}{time}; quantità attuale: {actual} {unit}.",
- "banner_prediction_less": "stima: {expected} {unit}{time}; quantità attuale: {actual} {unit}. Se hai cambiato ritmo d'uso, la previsione si aggiorna automaticamente.",
- "banner_finished_zero": "L'inventario segna zero, ma i movimenti registrati dicono che non dovrebbe essere finito.",
- "banner_finished_expected": "Secondo le registrazioni dovresti averne ancora {qty} {unit}.",
- "banner_finished_check": "Puoi controllare?",
- "banner_anomaly_phantom_title": "hai più scorte del previsto",
- "banner_anomaly_phantom_detail": "L'inventario segna {inv_qty} {unit}, ma in base alle registrazioni ne dovresti avere solo {expected_qty} {unit}. Hai aggiunto scorte senza registrarle?",
- "banner_anomaly_untracked_title": "scorte non registrate come entrata",
- "banner_anomaly_untracked_detail": "Hai {inv_qty} {unit} in inventario, ma le uscite registrate superano le entrate — le scorte iniziali probabilmente non sono mai state aggiunte come entrata. Puoi correggere la quantità o registrare le entrate mancanti.",
- "banner_anomaly_ghost_title": "hai meno scorte del previsto",
- "banner_anomaly_ghost_detail": "In base alle operazioni registrate dovresti avere {expected_qty} {unit} di {name}, ma l'inventario mostra solo {inv_qty} {unit}. Hai prelevato senza registrarlo?",
- "consumed": "Consumati: {n} ({pct}%)",
- "wasted": "Buttati: {n} ({pct}%)",
- "more_opened": "e altri {n} prodotti aperti...",
- "banner_expired_detail": "{when} · hai ancora {qty}.",
- "banner_opened_detail": "{when} in {location} · hai ancora {qty}.",
- "banner_explain_title": "Chiedi a Gemini una spiegazione",
- "banner_explain_btn": "Spiega",
- "banner_analyzing": "🤖 Analizzo…"
- },
- "inventory": {
- "title": "Dispensa",
- "filter_all": "Tutti",
- "search_placeholder": "🔍 Cerca prodotto...",
- "recent_title": "🕐 Ultimi usati",
- "popular_title": "⭐ Più usati",
- "empty": "Nessun prodotto qui.\nScansiona un prodotto per aggiungerlo!",
- "no_items_found": "Nessuna voce di inventario trovata",
- "qty_remainder_suffix": "rimasti",
- "vacuum_badge": "🫙 Sotto vuoto",
- "opened_badge": "📭 Aperto",
- "label_expiry": "📅 Scadenza",
- "label_storage": "🫙 Conservazione",
- "label_status": "📭 Stato",
- "opened_since": "Aperto dal {date}",
- "label_position": "📍 Posizione",
- "label_quantity": "📦 Quantità",
- "label_added": "📅 Aggiunto",
- "empty_text": "Nessun prodotto qui. Scansiona un prodotto per aggiungerlo!",
- "empty_db": "Nessun prodotto nel database. Scansiona un prodotto per iniziare!",
- "qty_trace": "< 1"
- },
- "scan": {
- "title": "Scansiona",
- "mode_shopping": "🛒 Modalità Spesa",
- "mode_shopping_end": "✅ Fine spesa",
- "spesa_btn": "🛒 Spesa",
- "zoom": "Zoom",
- "tab_barcode": "Barcode",
- "tab_name": "Nome",
- "tab_ai": "AI",
- "recents_label": "Recenti",
- "torch_hint": "Torcia",
- "torch_on": "Torcia accesa",
- "torch_off": "Torcia spenta",
- "torch_unavailable": "Torcia non disponibile su questo dispositivo",
- "flip_hint": "Cambia fotocamera",
- "flip_front": "Fotocamera anteriore",
- "flip_back": "Fotocamera posteriore",
- "num_ocr_btn": "🔢 Leggi numeri con AI",
- "num_ocr_searching": "Cerco il codice con AI...",
- "num_ocr_found": "Codice trovato: {code}",
- "num_ocr_not_found": "Nessun codice trovato nell'immagine",
- "barcode_placeholder": "Inserisci codice a barre...",
- "quick_name_divider": "oppure scrivi il nome",
- "quick_name_placeholder": "Es: Mele, Zucchine, Pane...",
- "manual_entry": "✏️ Inserimento Manuale",
- "ai_identify": "🤖 Identifica con AI",
- "hint": "Scansiona il barcode, scrivi il nome del prodotto, oppure usa l'AI per identificarlo",
- "debug_toggle": "🐛 Debug Log",
- "barcode_acquired": "🔖 Barcode acquisito: {code}",
- "scan_barcode": "🔖 Scansiona Barcode",
- "create_named": "Crea {name}",
- "new_without_barcode": "Nuovo prodotto senza barcode",
- "stock_in_pantry": "Hai gia in dispensa:",
- "status_ready": "Inquadra il codice a barre",
- "status_scanning": "Scansione in corso...",
- "status_partial": "Letto: {code} — verifico...",
- "status_invalid": "Non valido: {code} — riprovo",
- "status_confirmed": "Confermato!",
- "status_parallel": "Doppia scansione attiva..."
- },
- "action": {
- "title": "Cosa vuoi fare?",
- "add_btn": "📥 AGGIUNGI",
- "add_sub": "in dispensa/frigo",
- "use_btn": "USA",
- "use_sub": "dalla dispensa/frigo",
- "have_title": "📦 Ce l'hai già!",
- "add_more_sub": "altra quantità",
- "use_qty_sub": "quanto ne hai usato",
- "throw_btn": "🗑️ BUTTA",
- "throw_sub": "butta il prodotto",
- "edit_sub": "scadenza, luogo…",
- "create_recipe_btn": "Ricetta",
- "related_stock_title": "Hai anche in casa"
- },
- "add": {
- "title": "Aggiungi alla Dispensa",
- "location_label": "📍 Dove lo metti?",
- "quantity_label": "📦 Quantità",
- "conf_size_label": "📦 Ogni confezione contiene:",
- "conf_size_placeholder": "es. 300",
- "vacuum_label": "🫙 Sotto vuoto",
- "vacuum_hint": "La scadenza verrà estesa automaticamente",
- "submit": "✅ Aggiungi",
- "purchase_type_label": "🛒 Questo prodotto è...",
- "new_btn": "🆕 Appena comprato",
- "existing_btn": "📦 Ce l'avevo già",
- "remaining_label": "📦 Quantità rimasta",
- "remaining_hint": "Quanto è rimasto approssimativamente?",
- "remaining_full": "🟢 Pieno",
- "remaining_half": "🟠 Metà",
- "estimated_expiry": "Scadenza stimata:",
- "suffix_freezer": "(freezer)",
- "suffix_vacuum": "(sotto vuoto)",
- "hint_modify": "📝 Puoi modificare la data o scansionarla con la fotocamera",
- "scan_expiry_title": "📷 Scansiona Data Scadenza",
- "product_added": "✅ {name} aggiunto!{qty}",
- "suffix_freezer_vacuum": "(freezer + sotto vuoto)",
- "history_badge_tip": "Media da {n} inserimenti precedenti",
- "vacuum_question": "Messo sotto vuoto?",
- "vacuum_saved": "🔒 Sotto vuoto registrato"
- },
- "use": {
- "title": "Usa / Consuma",
- "location_label": "📍 Da dove?",
- "quantity_label": "Quanto hai usato?",
- "change": "cambia",
- "partial_hint": "Oppure specifica la quantità usata:",
- "partial_piece_hint": "Hai usato solo una parte?",
- "piece": "pezzo",
- "one_whole": "1 intero",
- "use_all": "🗑️ Usato TUTTO / Finito",
- "submit": "📤 Usa questa quantità",
- "available": "📦 Disponibile:",
- "opened_badge": "APERTO",
- "not_in_inventory": "⚠️ Prodotto non presente nell'inventario.",
- "expiry_warning": "⚠️ Usa prima quella{loc} che scade il {date} — {when}!",
- "expiry_warning_opened": "⚠️ Quella{loc}, aperta da {when} — usala prima!",
- "throw_title": "🗑️ Butta Prodotto",
- "throw_all": "🗑️ Butta TUTTO ({qty})",
- "throw_qty_label": "Quanto butti?",
- "throw_qty_hint": "oppure specifica la quantità:",
- "throw_partial_btn": "🗑️ Butta questa quantità",
- "when_expired": "scaduta da {n} giorni",
- "when_today": "scade oggi",
- "when_tomorrow": "scade domani",
- "when_days": "scade tra {n} giorni",
- "toast_used": "📤 Usato {qty} di {name}",
- "toast_bring": "🛒 Prodotto finito → aggiunto a Bring!",
- "toast_opened_finished": "🔓 Confezione aperta di {name} finita!",
- "disambiguation_hint": "Cosa intendi con \"finito tutto\"?",
- "disambiguation_all": "🗑️ Finito TUTTO ({qty})",
- "error_exceeds_stock": "⚠️ Non puoi usare più di quanto hai disponibile!",
- "use_all_confirm_title": "✅ Finisci tutto",
- "use_all_confirm_msg": "Conferma che hai finito tutto il prodotto:",
- "use_all_confirm_btn": "✅ Sì, finito",
- "throw_all_confirm_title": "🗑️ Butta tutto",
- "throw_all_confirm_msg": "Vuoi davvero buttare via tutto il prodotto?",
- "throw_all_confirm_btn": "🗑️ Sì, butta"
- },
- "product": {
- "title_new": "Nuovo Prodotto",
- "title_edit": "Modifica Prodotto",
- "ai_fill": "📷 Scatta foto e identifica con AI",
- "ai_fill_hint": "L'AI compilerà automaticamente i campi del prodotto",
- "name_label": "🏷️ Nome Prodotto *",
- "name_placeholder": "Es: Latte intero, Pasta penne rigate...",
- "brand_label": "🏢 Marca",
- "brand_placeholder": "Es: Barilla, Granarolo, Mutti...",
- "category_label": "📂 Categoria",
- "unit_label": "📏 Unità di misura",
- "default_qty_label": "🔢 Quantità default",
- "conf_size_label": "📦 Ogni confezione contiene:",
- "conf_size_placeholder": "es. 300",
- "notes_label": "📝 Note",
- "notes_placeholder": "Es: senza lattosio, bio, conservare in frigo dopo apertura...",
- "barcode_label": "🔖 Barcode",
- "barcode_placeholder": "Codice a barre (se disponibile)",
- "barcode_hint": "⚠️ Aggiungi il barcode così al prossimo acquisto basta scansionarlo!",
- "submit": "💾 Salva Prodotto",
- "name_required": "Inserisci il nome del prodotto",
- "conf_size_required": "Specifica il contenuto di ogni confezione",
- "expiry_estimated": "Scadenza stimata:",
- "scan_expiry": "Scansiona data scadenza",
- "expiry_hint": "📝 Puoi modificare la data o scansionarla con la fotocamera",
- "add_batch": "📦 + Lotto con scadenza diversa",
- "package_info": "📦 Confezione: {info}",
- "edit_catalog": "⚙️ Modifica scheda prodotto (nome, marca, categoria…)",
- "not_recognized": "⚠️ Prodotto non riconosciuto",
- "edit_info": "✏️ Modifica informazioni",
- "modify_details": "MODIFICA\nscadenza, luogo…",
- "already_in_pantry": "📋 Già in dispensa",
- "no_barcode": "Senza barcode",
- "unknown_product": "Prodotto non riconosciuto",
- "edit_name_brand": "Modifica nome/marca",
- "weight_label": "Peso",
- "origin_label": "Origine",
- "labels_label": "Etichette",
- "select_variant": "Seleziona la variante esatta o usa i dati AI:"
- },
- "products": {
- "title": "📦 Tutti i Prodotti",
- "search_placeholder": "🔍 Cerca prodotto...",
- "empty": "Nessun prodotto nel database.\nScansiona un prodotto per iniziare!",
- "no_category": "Nessun prodotto in questa categoria"
- },
- "recipes": {
- "title": "🍳 Ricette",
- "generate": "✨ Genera nuova ricetta",
- "archive_empty": "Nessuna ricetta salvata. Genera la tua prima ricetta!",
- "dialog_title": "🍳 Ricetta",
- "dialog_desc": "Genero una ricetta sana con gli ingredienti in dispensa, dando priorità a quelli in scadenza.",
- "meal_label": "🕐 Per quale pasto?",
- "persons_label": "👥 Quante persone?",
- "meal_type_label": "🎯 Tipo di pasto",
- "opt_fast": "⚡ Pasto Veloce",
- "opt_light": "🥗 Poca Fame",
- "opt_expiry": "⏰ Priorità Scadenze",
- "opt_healthy": "💚 Extra Salutare",
- "opt_opened": "📦 Priorità Cose Aperte",
- "opt_zero_waste": "♻️ Zero Sprechi",
- "generate_btn": "✨ Genera Ricetta",
- "loading_msg": "Sto preparando la ricetta...",
- "start_cooking": "👨🍳 Modalità Cucina",
- "regenerate": "🔄 Generane un'altra",
- "regen_choice_title": "Cosa vuoi fare con questa ricetta?",
- "regen_replace": "🔄 Genera un'altra (scarta questa)",
- "regen_save_new": "💾 Salva nell'archivio e genera una nuova",
- "close_btn": "✅ Chiudi",
- "ingredients_title": "🧾 Ingredienti",
- "tools_title": "Strumenti necessari",
- "steps_title": "👨🍳 Procedimento",
- "no_steps": "Nessun procedimento disponibile",
- "generate_error": "Errore nella generazione",
- "stream_interrupted": "Generazione interrotta (risposta incompleta dal server). Controlla i log o riprova.",
- "persons_short": "pers.",
- "use_ingredient_title": "Usa ingrediente",
- "recipe_qty_label": "Ricetta",
- "from_where_label": "Da dove?",
- "amount_label": "Quanto",
- "use_amount_btn": "Usa questa quantità",
- "use_all_btn": "Usa TUTTO / Finito",
- "packs_label": "Confezioni",
- "quantity_in_total": "Quantità in {unit} (totale: {total})",
- "packs_of_have": "Confezioni da {size} (hai {count} conf)",
- "scale_wait_stable": "Attendi 10s di stabilità per la compilazione automatica…",
- "ingredient_scaled_toast": "📦 Ingrediente scalato dalla dispensa!",
- "finished_added_bring_toast": "🛒 Prodotto finito → aggiunto a Bring!",
- "load_error": "Errore nel caricamento",
- "favorite": "Aggiungi ai preferiti",
- "unfavorite": "Rimuovi dai preferiti",
- "adjust_persons": "Persone"
- },
- "shopping": {
- "title": "🛒 Lista della Spesa",
- "bring_loading": "Connessione a Bring!...",
- "bring_not_configured": "Bring! non è configurato. Aggiungi email e password nelle impostazioni.",
- "tab_to_buy": "🛍️ Da comprare",
- "tab_forecast": "🧠 In previsione",
- "total_label": "💰 Totale stimato",
- "section_to_buy": "🛍️ Da comprare",
- "suggestions_title": "💡 Suggerimenti AI",
- "suggestions_add": "✅ Aggiungi selezionati a Bring!",
- "search_prices": "🔍 Cerca tutti i prezzi",
- "suggest_btn": "Suggerisci cosa comprare",
- "smart_title": "🧠 Previsioni intelligenti",
- "smart_empty": "Nessuna previsione disponibile. Aggiungi prodotti alla dispensa per ricevere previsioni intelligenti.",
- "smart_filter_all": "Tutti",
- "smart_filter_critical": "🔴 Urgenti",
- "smart_filter_high": "🟠 Presto",
- "smart_filter_medium": "🟡 Pianifica",
- "smart_filter_low": "🟢 Previsione",
- "smart_add": "🛒 Aggiungi selezionati a Bring!",
- "empty": "Lista della spesa vuota!\nUsa il pulsante sotto per generare suggerimenti.",
- "already_in_list": "🛒 \"{name}\" già nella lista della spesa",
- "already_in_list_short": "ℹ️ Già nella lista della spesa",
- "add_prompt": "Vuoi aggiungerlo alla lista della spesa?",
- "smart_already": "📊 La spesa intelligente prevede già {name}",
- "all_searched": "Tutti i prodotti sono già stati cercati. Usa 🔄 per ricercare singoli.",
- "search_complete": "Ricerca completata: {count} prodotti",
- "removed_sufficient": "🧹 {removed} prodotto/i con scorte sufficienti rimosso/i dalla lista",
- "suggest_buy": "🛒 Compra: {qty} {unit}",
- "suggest_buy_approx": "🛒 Almeno: {qty} {unit}",
- "suggest_buy_tip": "Quantità suggerita in base al consumo degli ultimi 14 giorni",
- "suggest_buy_approx_tip": "Stima minima basata sul consumo (compra la confezione più vicina)",
- "bring_badge": "🛒 Già su Bring!",
- "add_urgent_toast": "🔴 {n} prodotto/i urgente/i aggiunto/i automaticamente a Bring!",
- "migration_done": "✅ {migrated} aggiornati, {skipped} già ok",
- "added_to_bring": "🛒 {n} prodotti aggiunti a Bring!",
- "added_to_bring_skip": "{n} già presenti",
- "all_on_bring": "Tutti i prodotti erano già su Bring!",
- "freq_high": "📈 Uso frequente",
- "freq_regular": "📊 Uso regolare",
- "freq_occasional": "📉 Uso occasionale",
- "out_of_stock": "Esaurito",
- "scan_toast": "📷 Scansiona: {name}",
- "empty_category": "Nessun prodotto in questa categoria",
- "session_empty": "🛒 Nessun prodotto ancora",
- "urgency_critical": "Urgente",
- "urgency_high": "Presto",
- "urgency_medium": "Pianifica",
- "urgency_low": "Previsione",
- "urgency_medium_short": "Medio",
- "urgency_low_short": "Ok",
- "tag_urgent": "🔴 Urgente",
- "tag_priority": "⭐ Priorità",
- "tag_check": "✅ Verificare",
- "smart_already_predicted": "📊 La spesa intelligente prevede già {name}{urgency}.",
- "item_removed": "✅ {name} rimosso dalla lista!",
- "urgency_spec_critical": "⚡ Urgente",
- "urgency_spec_high": "🟠 Presto",
- "bring_add_n": "Aggiungi {n} a Bring!",
- "bring_add_selected": "Aggiungi selezionati a Bring!",
- "bring_adding": "Aggiunta in corso...",
- "bring_added_one": "1 prodotto aggiunto a Bring!",
- "bring_added_many": "{n} prodotti aggiunti a Bring!",
- "bring_skipped": "({n} già in lista)",
- "force_sync": "Forza sincronizzazione Bring!",
- "scan_target_label": "Stai cercando",
- "scan_target_found": "Trovato! Rimuovi dalla lista",
- "bring_add_one": "Aggiungi 1 prodotto a Bring!",
- "bring_add_many": "Aggiungi {n} prodotti a Bring!",
- "syncing": "Sincronizzazione…",
- "sync_done": "Sincronizzazione completata",
- "price_searching": "Cerco...",
- "search_action": "Ricerca",
- "open_action": "Apri",
- "not_found": "Non trovato",
- "search_price": "Cerca prezzo",
- "tap_to_scan": "Tocca per scansionare",
- "tag_title": "Tag",
- "remove_title": "Rimuovi",
- "found_count": "{found}/{total} prodotti trovati",
- "savings_offers": "· 🏷️ Risparmi €{amount} con le offerte",
- "searching_progress": "Cerco {current}/{total}...",
- "remove_error": "Errore nella rimozione",
- "btn_fetch_prices": "Cerca i prezzi",
- "price_total_label": "💰 Spesa stimata:",
- "price_loading": "Ricerca prezzi…",
- "price_not_found": "prezzo n/d",
- "suggest_loading": "Analisi in corso...",
- "suggest_error": "Errore nella generazione",
- "priority_high": "Alta",
- "priority_medium": "Media",
- "priority_low": "Bassa",
- "smart_last_update": "Aggiornato {time}",
- "names_already_updated": "Tutti i nomi sono già aggiornati",
- "pantry_hint": "Hai gia {qty} in dispensa"
- },
- "ai": {
- "title": "🤖 Identificazione AI",
- "capture": "📸 Scatta Foto",
- "retake": "🔄 Riscatta",
- "hint": "Scatta una foto del prodotto e l'AI cercherà di identificarlo",
- "identifying": "🤖 Identifico il prodotto...",
- "no_api_key": "⚠️ Chiave API Gemini non configurata.\nAggiungi GEMINI_API_KEY nel file .env sul server.",
- "fields_filled": "✅ Campi compilati dall'AI",
- "use_data": "✅ Usa dati AI",
- "use_data_no_barcode": "✅ Usa dati AI (senza barcode)"
- },
- "log": {
- "title": "📒 Storico",
- "type_added": "Aggiunto",
- "type_waste": "Buttato",
- "type_used": "Usato",
- "type_bring": "Aggiunto a Bring!",
- "undone_badge": "Annullato",
- "undo_title": "Annulla questa operazione",
- "load_error": "Errore nel caricamento log",
- "empty": "Nessuna operazione registrata.",
- "undo_action_remove": "rimozione di",
- "undo_action_restore": "ripristino di",
- "undo_confirm": "Annullare questa operazione?\n→ {action} {name}",
- "undo_success": "↩ Operazione annullata per {name}",
- "already_undone": "Operazione già annullata",
- "too_old": "Non è possibile annullare operazioni più vecchie di 24 ore",
- "undo_error": "Errore durante l'annullamento",
- "recipe_prefix": "Ricetta"
- },
- "chat": {
- "title": "Gemini Chef",
- "welcome": "Ciao! Sono il tuo assistente cucina",
- "welcome_desc": "Chiedimi di prepararti un succo, uno spuntino, un piatto veloce... Conosco la tua dispensa, i tuoi elettrodomestici e le tue preferenze!",
- "suggestion_snack": "🍿 Spuntino veloce",
- "suggestion_juice": "🥤 Succo/Frullato",
- "suggestion_light": "🥗 Qualcosa di leggero",
- "suggestion_expiry": "⏰ Usa le scadenze",
- "clear": "Nuova conversazione",
- "placeholder": "Chiedi qualcosa...",
- "cleared": "Chat cancellata",
- "suggestion_snack_text": "Cosa posso preparare per uno spuntino veloce?",
- "suggestion_juice_text": "Fammi un succo o frullato con quello che ho",
- "suggestion_light_text": "Ho fame ma voglio qualcosa di leggero",
- "suggestion_expiry_text": "Cosa sta per scadere e come posso usarlo?",
- "transfer_to_recipes": "Trasferisci a Ricette",
- "transferring": "Trasferimento in corso...",
- "transferred": "Aggiunta alle Ricette!",
- "open_recipe": "Apri la ricetta",
- "quick_recipe_prompt": "Suggeriscimi una ricetta veloce PER UNA PERSONA usando i prodotti che scadono prima! Ignora i prodotti in freezer, concentrati su frigo e dispensa."
- },
- "cooking": {
- "close": "Chiudi",
- "tts_btn": "Leggi ad alta voce",
- "restart": "↺ Ricomincia",
- "replay": "🔊 Rileggi",
- "timer": "⏱️ {time} · Timer",
- "prev": "◀ Precedente",
- "next": "Successivo ▶",
- "ingredient_used": "✔️ Scalato",
- "ingredient_use_btn": "Usa",
- "ingredient_deduct_title": "Scala dalla dispensa",
- "timer_expired_tts": "Timer {label} scaduto!",
- "timer_warning_tts": "Attenzione! {label}: mancano 10 secondi!",
- "recipe_done_tts": "Ricetta completata! Buon appetito!",
- "expires_chip": "scade {date}",
- "finish": "✅ Fine",
- "step_fallback": "Passo {n}",
- "zerowaste_label": "♻️ Scarto",
- "zerowaste_tip_title": "Consiglio anti-spreco"
- },
- "settings": {
- "title": "⚙️ Configurazione",
- "tab_api": "API Keys",
- "tab_bring": "Bring!",
- "tab_recipe": "Ricette",
- "tab_mealplan": "Piano Settimanale",
- "tab_appliances": "Elettrodomestici",
- "tab_spesa": "Spesa Online",
- "tab_camera": "Fotocamera",
- "tab_security": "Sicurezza",
- "tab_tts": "Voce (TTS)",
- "tab_language": "Lingua",
- "tab_scale": "Bilancia Smart",
- "gemini": {
- "title": "🤖 Google Gemini AI",
- "hint": "Chiave API per identificazione prodotti, scadenze e ricette.",
- "key_label": "API Key Gemini"
- },
- "bring": {
- "title": "🛒 Bring! Shopping List",
- "hint": "Credenziali per l'integrazione con la lista della spesa Bring!",
- "email_label": "📧 Email Bring!",
- "password_label": "🔒 Password Bring!"
- },
- "price": {
- "title": "💰 Stima Prezzi (AI)",
- "hint": "Mostra il costo stimato di ogni prodotto nella lista della spesa usando l'AI.",
- "enabled_label": "Attiva stima prezzi",
- "country_label": "🌍 Paese di riferimento",
- "currency_label": "💱 Valuta",
- "update_label": "🔄 Aggiorna prezzi ogni",
- "update_suffix": "mesi"
- },
- "recipe": {
- "title": "🍳 Preferenze Ricette",
- "hint": "Configura le opzioni predefinite per la generazione delle ricette.",
- "persons_label": "👥 Persone predefinite",
- "options_label": "🎯 Opzioni ricetta predefinite",
- "fast": "⚡ Pasto Veloce",
- "light": "🥗 Poca Fame",
- "expiry": "⏰ Priorità Scadenze",
- "healthy": "💚 Extra Salutare",
- "opened": "📦 Priorità Cose Aperte",
- "zerowaste": "♻️ Zero Sprechi",
- "dietary_label": "🚫 Intolleranze / Restrizioni",
- "dietary_placeholder": "Es: senza glutine, senza lattosio, vegetariano..."
- },
- "mealplan": {
- "title": "📅 Piano Pasti Settimanale",
- "hint": "Imposta la tipologia di pasto per ogni giorno. Sarà usata come guida nella generazione delle ricette.",
- "enabled": "✅ Attiva piano pasti settimanale",
- "legend": "🌤️ = Pranzo · 🌙 = Cena · Tocca un badge per cambiarlo.",
- "types_title": "📋 Tipologie disponibili",
- "reset_btn": "↺ Ripristina default"
- },
- "appliances": {
- "title": "🔌 Elettrodomestici Disponibili",
- "hint": "Indica gli elettrodomestici che hai a disposizione. Saranno considerati nella generazione delle ricette.",
- "new_placeholder": "Es: Macchina del pane, Bimby, Friggitrice ad aria...",
- "quick_title": "Aggiungi velocemente:",
- "oven": "🔥 Forno",
- "microwave": "📡 Microonde",
- "air_fryer": "🍟 Friggitrice ad aria",
- "bread_maker": "🍞 Macchina pane",
- "bimby": "🤖 Bimby/Cookeo",
- "mixer": "🌀 Planetaria",
- "steamer": "♨️ Vaporiera",
- "pressure_cooker": "🫕 Pentola pressione",
- "toaster": "🍞 Tostapane",
- "blender": "🍹 Frullatore",
- "empty": "Nessun elettrodomestico aggiunto"
- },
- "spesa": {
- "title": "🛍️ Spesa Online",
- "hint": "Configura il provider per la spesa online.",
- "provider_label": "🏪 Provider",
- "email_label": "📧 Email",
- "password_label": "🔒 Password",
- "login_btn": "🔐 Accedi",
- "ai_prompt_label": "🤖 Prompt AI selezione prodotto",
- "ai_prompt_placeholder": "Istruzioni per l'AI quando deve scegliere tra più prodotti...",
- "ai_prompt_hint": "L'AI usa questo prompt per scegliere il prodotto più appropriato tra i risultati. Lascia vuoto per il comportamento predefinito.",
- "configure_first": "Configura prima la Spesa Online nelle impostazioni",
- "missing_credentials": "Inserisci email e password",
- "login_in_progress": "Accesso in corso...",
- "login_error_prefix": "Errore:",
- "login_network_error_prefix": "Errore di rete:",
- "login_success_default": "Login effettuato!",
- "result_name_label": "Nome",
- "result_card_label": "Tessera",
- "result_pickup_label": "Punto Ritiro",
- "result_points_label": "Punti Fedeltà",
- "connected_relogin": "✅ Connesso — Riaccedi",
- "connected_as": "Connesso come {name}"
- },
- "camera": {
- "title": "📷 Fotocamera",
- "hint": "Scegli quale fotocamera utilizzare per la scansione barcode e l'identificazione AI.",
- "device_label": "📸 Fotocamera predefinita",
- "back": "📱 Posteriore (default)",
- "front": "🤳 Anteriore",
- "devices_hint": "Se hai più fotocamere, puoi selezionarne una specifica dall'elenco sopra dopo aver concesso i permessi.",
- "detect_btn": "🔄 Rileva fotocamere"
- },
- "security": {
- "title": "🔒 Certificato HTTPS",
- "hint": "Se il browser mostra l'errore \"La connessione non è privata\" (ERR_CERT_AUTHORITY_INVALID), devi installare il certificato CA nel dispositivo.",
- "download_btn": "📥 Scarica Certificato CA",
- "token_title": "🔑 Token Impostazioni",
- "token_label": "Token di accesso",
- "token_hint": "Se `SETTINGS_TOKEN` è configurato nel `.env` server, inserisci qui il token prima di salvare le impostazioni. Lascia vuoto se non configurato.",
- "token_placeholder": "(vuoto = nessuna protezione)",
- "token_required_hint": "🔒 Questo server richiede un token per salvare le impostazioni.",
- "cert_instructions": "Istruzioni per Chrome (Android): 1. Scarica il certificato qui sopra 2. Vai in Impostazioni → Sicurezza e privacy → Altre impostazioni di sicurezza → Installa da archivio dispositivo 3. Seleziona il file EverShelf_CA.crt scaricato 4. Scegli \"CA\" e conferma 5. Riavvia Chrome
Istruzioni per Chrome (PC): 1. Scarica il certificato qui sopra 2. Vai in chrome://settings/certificates (o Impostazioni → Privacy e sicurezza → Sicurezza → Gestisci certificati) 3. Tab \"Autorità\" → Importa → seleziona il file 4. Spunta \"Considera attendibile per identificare siti web\" 5. Riavvia Chrome"
- },
- "tts": {
- "title": "🔊 Voce & TTS",
- "hint": "Configura la sintesi vocale tramite qualsiasi API REST esterna. I passi della ricetta e i timer scaduti verranno inviati all'endpoint configurato.",
- "enabled": "✅ Attiva TTS",
- "engine_label": "⚙️ Motore TTS",
- "engine_browser": "🔇 Browser (offline, nessuna configurazione)",
- "engine_server": "🌐 Server esterno (Home Assistant, API REST...)",
- "voice_label": "🗣️ Voce",
- "rate_label": "⚡ Velocità",
- "pitch_label": "🎵 Tono",
- "url_label": "🌐 URL Endpoint",
- "method_label": "📡 Metodo HTTP",
- "auth_label": "🔐 Autenticazione",
- "auth_bearer": "Bearer Token",
- "auth_custom": "Header personalizzato",
- "auth_none": "Nessuna",
- "token_label": "🔑 Bearer Token",
- "custom_header_name": "📋 Nome header",
- "custom_header_value": "📋 Valore header",
- "content_type_label": "📄 Content-Type",
- "payload_key_label": "🗝️ Campo testo nel payload",
- "payload_key_hint": "Nome del campo JSON che conterrà il testo da leggere (es: message, text).",
- "extra_fields_label": "➕ Campi extra (JSON)",
- "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}",
- "extra_fields_hint": "Campi aggiuntivi da includere nel payload, in formato JSON. Lascia vuoto se non necessario.",
- "test_sound_btn": "🔔 Esegui Test Suono",
- "test_btn": "🔊 Invia Test Vocale",
- "voices_loading": "Caricamento voci…",
- "voice_not_supported": "Voce non supportata dal browser",
- "voices_none": "Nessuna voce disponibile su questo dispositivo",
- "voices_hint": "Le voci disponibili dipendono dal sistema operativo e dal browser. Su macOS/iOS è disponibile la voce Paola (italiano). Premi ↺ se la lista non si carica.",
- "url_missing": "⚠️ URL endpoint mancante.",
- "test_sending": "⏳ Invio in corso…",
- "test_ok": "✅ Risposta {code} — controlla che l'altoparlante abbia parlato.",
- "heard_question": "Hai sentito la voce?",
- "heard_yes": "Sì, ho sentito",
- "heard_no": "No, non ho sentito",
- "test_ok_kiosk": "TTS funzionante.",
- "test_fail_steps": "Controlla: 1) volume media del dispositivo non sia 0; 2) Google Text-to-Speech installato e aggiornato; 3) pacchetto vocale italiano scaricato nelle impostazioni TTS Android."
- },
- "language": {
- "title": "🌐 Lingua / Language",
- "hint": "Seleziona la lingua dell'interfaccia. Select the interface language.",
- "label": "🌐 Lingua",
- "restart_notice": "La pagina verrà ricaricata per applicare la nuova lingua."
- },
- "screensaver": {
- "label": "Attiva salvaschermo",
- "card_title": "🌙 Salvaschermo",
- "card_hint": "Mostra un orologio con fatti utili dopo 5 minuti di inattività. Di default è disattivato.",
- "timeout_1": "1 minuto",
- "timeout_2": "2 minuti",
- "timeout_5": "5 minuti",
- "timeout_10": "10 minuti",
- "timeout_15": "15 minuti",
- "timeout_30": "30 minuti",
- "timeout_60": "1 ora",
- "start_after": "⏱️ Avvia dopo"
- },
- "scale": {
- "title": "⚖️ Bilancia Smart",
- "hint": "Collega una bilancia Bluetooth tramite il gateway Android per leggere il peso automaticamente.",
- "tab": "Bilancia Smart",
- "enabled": "✅ Abilita bilancia smart",
- "url_label": "🌐 URL Gateway WebSocket",
- "url_placeholder": "ws://192.168.1.x:8765",
- "url_hint": "URL mostrato dall'app Android (stessa rete Wi-Fi). Es:",
- "test_btn": "🔗 Testa connessione",
- "download_btn": "📥 Scarica Gateway Android (APK)",
- "download_hint": "App Android che fa da ponte tra la bilancia BLE e questo sito.",
- "download_sub": "Sorgente: evershelf-scale-gateway/ nella root del progetto",
- "live_weight": "peso in tempo reale",
- "auto_reconnect": "🔁 Riconnessione: automatica",
- "kiosk_title": "📡 Bilancia BLE integrata nel Kiosk",
- "kiosk_hint": "La bilancia è gestita direttamente dal Gateway BLE interno al kiosk. Per abbinare un nuovo dispositivo usa il wizard di configurazione.",
- "kiosk_reconfigure": "🔄 Riconfigura bilancia BLE",
- "ble_protocols": "
🔌 Protocolli BLE supportati:
Bluetooth SIG Weight Scale (0x181D)
Bluetooth SIG Body Composition (0x181B) — peso, grasso, BMI
Xiaomi Mi Body Composition Scale 2
Generico — heuristica automatica su 100+ modelli
"
- },
- "kiosk": {
- "hint": "Trasforma un tablet Android in un pannello EverShelf sempre acceso, con bilancia BLE integrata.",
- "download_btn": "📥 Scarica EverShelf Kiosk (APK)",
- "download_sub": "Modalità kiosk full-screen + gateway bilancia integrato. Sorgente: evershelf-kiosk/",
- "native_title": "Configurazione Kiosk",
- "native_hint": "URL server, bilancia BLE, salvaschermo e setup wizard.",
- "native_btn": "Apri configurazione kiosk",
- "native_tap_hint": "Tocca la rotella in alto a destra",
- "native_update_hint": "Aggiorna l'app kiosk per usare questa funzione",
- "update_title": "Aggiornamento Kiosk",
- "check_updates_btn": "🔍 Cerca aggiornamenti",
- "needs_update": "⚠️ Il kiosk installato non supporta questa funzione. Aggiorna l'app kiosk per abilitarla."
- },
- "saved": "✅ Configurazione salvata!",
- "saved_local": "✅ Configurazione salvata localmente",
- "saved_local_error": "⚠️ Salvato localmente, errore server: {error}",
- "theme": {
- "title": "🌙 Tema / Aspetto",
- "hint": "Scegli il tema dell interfaccia.",
- "label": "🌙 Tema",
- "off": "☀️ Chiaro",
- "on": "🌙 Scuro",
- "auto": "🔄 Automatico (orario)"
- },
- "zerowaste": {
- "card_title": "♻️ Suggerimenti zero-waste",
- "card_hint": "Durante la cottura, mostra consigli su come riutilizzare gli scarti prodotti in ogni passo (bucce, acqua di cottura, ecc.). Disattivo per impostazione predefinita.",
- "label": "Mostra suggerimenti durante la cottura"
- },
- "backup": {
- "tab": "Backup",
- "local_title": "Backup Locale",
- "local_hint": "Snapshot giornaliero del database. Configura quanti giorni di backup conservare.",
- "enabled": "Backup automatico quotidiano",
- "retention_days": "Giorni di retention",
- "retention_info": "I backup vengono conservati per",
- "backup_now": "Backup Ora",
- "backing_up": "Backup in corso…",
- "backed_up": "Backup completato",
- "backup_error": "Errore backup",
- "last_backup": "Ultimo backup",
- "no_backup_yet": "Nessun backup ancora eseguito",
- "list_empty": "Nessun backup disponibile",
- "restore_btn": "Ripristina",
- "restore_confirm": "Ripristinare il backup",
- "delete_btn": "Elimina",
- "delete_confirm": "Eliminare il backup",
- "gdrive_title": "Google Drive",
- "gdrive_hint": "Backup automatici su Google Drive via OAuth 2.0. Nessuna libreria esterna richiesta.",
- "gdrive_enabled": "Abilita backup Google Drive",
- "gdrive_folder_id": "ID Cartella Drive",
- "gdrive_folder_id_hint": "Copia l'ID dalla URL della cartella Drive: …/folders/ID",
- "gdrive_retention_days": "Retention Drive (giorni, 0=tutto)",
- "gdrive_test": "Testa Connessione",
- "gdrive_ok": "Connessione riuscita!",
- "gdrive_error": "Connessione fallita",
- "gdrive_push_now": "Carica Ora su Drive",
- "gdrive_pushing": "Upload in corso…",
- "gdrive_pushed": "Caricato su Drive",
- "gdrive_wizard_hint": "Opzionale: backup giornaliero automatico su Google Drive via OAuth 2.0.",
- "gdrive_skip": "Salta — configura dopo in Impostazioni",
- "gdrive_client_id": "Client ID",
- "gdrive_client_secret": "Client Secret",
- "gdrive_redirect_uri_label": "Redirect URI (da aggiungere in Google Cloud Console):",
- "gdrive_redirect_uri_hint": "Aggiungi http://localhost come URI di reindirizzamento autorizzato in Google Cloud Console. Funziona su qualsiasi server, anche senza dominio pubblico.",
- "gdrive_oauth_authorize": "Autorizza con Google",
- "gdrive_oauth_authorized": "Autorizzato",
- "gdrive_oauth_not_authorized": "Non ancora autorizzato",
- "gdrive_oauth_window_opened": "Finestra aperta — autorizza e torna qui",
- "gdrive_oauth_how_to": "Come configurare OAuth 2.0 (passo dopo passo)",
- "gdrive_oauth_steps": "
Abilita la Google Drive API: API e servizi → Abilita API → Google Drive API
Vai su API e servizi → Credenziali → Crea credenziali → ID client OAuth 2.0
Tipo applicazione: Applicazione web; aggiungi http://localhost come URI di reindirizzamento autorizzato
Copia Client ID e Client Secret nei campi qui sopra e salva
Clicca Autorizza con Google, accedi e concedi l'accesso
Il browser aprirà http://localhost (possibile errore di connessione è normale): copia l'URL dalla barra degli indirizzi e incollalo nel campo che appare qui sotto
",
- "gdrive_code_title": "Incolla l'URL o il codice di autorizzazione",
- "gdrive_code_hint": "Dopo aver autorizzato, il browser aprirà http://localhost e potrebbe mostrare un errore. Copia l'URL dalla barra degli indirizzi (es. http://localhost/?code=4%2F0A...) e incollalo qui.",
- "gdrive_code_submit": "Conferma",
- "gdrive_code_empty": "Incolla prima l'URL o il codice di autorizzazione"
- },
- "info": {
- "tab": "Info",
- "ai_title": "Gemini AI — Utilizzo Token",
- "ai_hint": "Consumo mensile e costo stimato per la chiave API corrente.",
- "loading": "Caricamento…",
- "total_tokens": "Token totali",
- "est_cost": "Costo stimato",
- "input_tok": "Token input",
- "output_tok": "Token output",
- "ai_calls": "Chiamate",
- "by_action": "Dettaglio per funzione",
- "by_model": "Dettaglio per modello",
- "pricing_note": "Prezzi di riferimento Gemini: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.",
- "system_title": "Sistema",
- "db_size": "Database",
- "log_size": "Log",
- "log_level": "Livello log",
- "ai_overview": "Prospetto utilizzo AI, inventario e stato del sistema",
- "calls_unit": "call",
- "inv_title": "Inventario",
- "inv_active": "Attivi",
- "inv_products": "Prodotti totali",
- "inv_expiring": "In scadenza (7gg)",
- "inv_expired": "Scaduti",
- "inv_finished": "Finiti",
- "act_title": "Attività del mese",
- "act_tx_month": "Movimenti",
- "act_restock": "Acquisti",
- "act_use": "Consumi",
- "act_new_products": "Nuovi prodotti",
- "act_tx_year": "Movimenti anno",
- "price_cache": "Cache prezzi",
- "cache_entries": "prodotti",
- "last_backup": "Ultimo backup",
- "bring_days": "token scade tra {n} giorni",
- "bring_expired": "token scaduto",
- "year_label": "Anno {year}",
- "currency_title": "Valuta",
- "currency_hint": "La valuta usata per tutti i costi e i prezzi nell'app."
- },
- "tab_general": "Generali",
- "shopping": {
- "tab": "Lista spesa",
- "title": "Lista della spesa",
- "hint": "Configura la lista della spesa integrata o collega Bring!.",
- "enable_label": "Abilita lista della spesa",
- "mode_label": "Provider",
- "mode_internal": "Interno (senza Bring!)",
- "mode_bring": "Bring! (app esterna)",
- "bring_section_title": "Configurazione Bring!",
- "ai_section_title": "Assistenza AI",
- "smart_suggestions_label": "Suggerimenti AI",
- "forecast_label": "Previsione prodotti in esaurimento",
- "auto_add_label": "Aggiungi automaticamente quando",
- "auto_add_suffix": "rimasto in magazzino (0 = solo quando esaurito)"
- },
- "ha": {
- "tab": "Home Assistant",
- "title": "Home Assistant",
- "hint": "Collega EverShelf a Home Assistant per automazioni, notifiche push e sensori REST.",
- "enabled": "Abilita integrazione Home Assistant",
- "connection_title": "Connessione",
- "url_label": "URL Home Assistant",
- "url_placeholder": "http://192.168.1.50:8123",
- "url_hint": "URL del tuo server Home Assistant (es. http://homeassistant.local:8123).",
- "token_label": "Long-Lived Access Token",
- "token_hint": "Genera da Profilo HA → Sicurezza → Token di accesso a lungo termine.",
- "token_placeholder": "eyJhbGci...",
- "token_saved": "Token salvato (non mostrato per sicurezza)",
- "test_btn": "Testa connessione",
- "test_ok": "Connesso a {version}",
- "test_fail": "Connessione fallita: {error}",
- "test_bad_token": "HA raggiungibile ma token non valido",
- "testing": "Test in corso…",
- "error_no_url": "Inserisci prima l'URL di Home Assistant.",
- "tts_title": "TTS su Speaker Smart",
- "tts_hint": "Leggi i passi delle ricette su un media player di Home Assistant.",
- "tts_entity_label": "Entity ID media player",
- "tts_entity_placeholder": "media_player.living_room",
- "tts_entity_hint": "Entity ID del media player su cui vuoi la voce. Puoi trovarlo in HA: Strumenti per sviluppatori → Stati.",
- "tts_platform_label": "Piattaforma TTS",
- "tts_platform_speak": "tts.speak (raccomandato)",
- "tts_platform_notify": "notify.* (servizio notifiche)",
- "tts_apply_btn": "Applica preset HA al tab TTS",
- "tts_apply_hint": "Pre-compila il tab TTS con l'URL e il token di Home Assistant.",
- "tts_preset_applied": "Preset HA applicato al tab TTS.",
- "webhook_title": "Automazioni Webhook",
- "webhook_hint": "Invia dati a Home Assistant quando avvengono eventi nella dispensa. Crea un'automazione in HA con trigger Webhook e copia l'ID generato.",
- "webhook_id_label": "Webhook ID",
- "webhook_id_placeholder": "evershelf_webhook_abc123",
- "webhook_id_hint": "ID del webhook creato in HA. Copia da: HA → Impostazioni → Automazioni → Crea → Trigger Webhook.",
- "webhook_events_label": "Notifica per questi eventi",
- "event_expiry": "Prodotti in scadenza (giornaliero)",
- "event_shopping": "Aggiunta alla lista della spesa",
- "event_stock": "Aggiornamento scorte",
- "expiry_days_label": "Anticipo scadenze (giorni)",
- "expiry_days_hint": "Invia la notifica di scadenza N giorni prima della data di scadenza.",
- "webhook_help": "In HA: Impostazioni → Automazioni → Crea automazione → Trigger: Webhook → copia l'ID generato qui sopra.",
- "notify_title": "Notifiche Push",
- "notify_hint": "Invia notifiche push al tuo telefono tramite il servizio notify di Home Assistant.",
- "notify_service_label": "Servizio notify",
- "notify_service_placeholder": "notify.mobile_app_mio_telefono",
- "notify_service_hint": "Nome del servizio notify HA (es. notify.mobile_app_phone). Lascia vuoto per disabilitare.",
- "sensor_title": "Sensori REST",
- "sensor_hint": "Aggiungi a configuration.yaml per creare sensori EverShelf in Home Assistant.",
- "sensor_copy_btn": "Copia YAML",
- "sensor_copied": "YAML copiato negli appunti!",
- "save_btn": "Salva impostazioni HA",
- "ha_hint": "Se usi Home Assistant, usa il tab Home Assistant per configurare TTS, webhook e sensori."
- }
- },
- "expiry": {
- "today": "OGGI",
- "tomorrow": "Domani",
- "days": "{days} giorni",
- "expired_days": "Da {days}g",
- "expired_yesterday": "Da ieri",
- "expired_today": "Oggi",
- "badge_today": "⚠️ Scade oggi!",
- "badge_tomorrow": "⏰ Domani",
- "badge_tomorrow_long": "⏰ Scade domani",
- "badge_days": "⏰ {n} giorni",
- "badge_expired_ago": "⚠️ Scaduto da {n}g",
- "badge_expired": "⛔ Scaduto!",
- "badge_stable": "✅ Stabile",
- "badge_expiring_short": "⏰ Scade fra {n}gg",
- "badge_ok_still": "✅ Ancora {n}gg",
- "badge_expires_red": "🔴 Scade tra {n}g",
- "badge_expires_yellow": "🟡 Scade tra {n}g",
- "badge_expired_bare": "⚠️ Scaduto",
- "badge_expires_warn": "⚠️ Scade tra {n}gg",
- "badge_days_left": "⏳ ~{n}gg rimasti",
- "days_approx": "~{n} giorni",
- "weeks_approx": "~{n} settimane",
- "months_approx": "~{n} mesi",
- "years_approx": "~{n} anni",
- "expired_today_long": "Scaduto oggi",
- "expired_ago_long": "Scaduto da {n} giorni",
- "expired_suffix": "— Scaduto!",
- "expired_suffix_ok": "— Scaduto (ancora ok)",
- "expired_suffix_warning": "— Scaduto (controlla)",
- "opened_ago_long": "Aperto da {n} giorni",
- "opened_today_long": "Aperto oggi",
- "opened_suffix": "— Aperto da troppo tempo!",
- "opened_suffix_ok": "— Aperto (ancora ok)",
- "opened_suffix_warning": "— Aperto (controlla)",
- "days_compact": "{n}gg",
- "badge_check_soon": "Controlla presto"
- },
- "status": {
- "ok": "OK",
- "check": "Controlla",
- "discard": "Buttare",
- "tip_freezer_ok": "In freezer: ancora sicuro (~{n}g di margine)",
- "tip_freezer_check": "In freezer da molto, potrebbe aver perso qualità. Consumare presto",
- "tip_freezer_danger": "In freezer da troppo tempo, rischio di bruciatura da gelo e degrado",
- "tip_highRisk_check": "Scaduto da poco, controlla odore e aspetto prima di consumare",
- "tip_highRisk_danger": "Prodotto deperibile scaduto: da buttare per sicurezza",
- "tip_medRisk_check1": "Controlla aspetto e odore prima di consumare",
- "tip_medRisk_check2": "Scaduto da un po', verificare bene prima dell'uso",
- "tip_medRisk_danger": "Troppo tempo dalla scadenza, meglio buttare",
- "tip_lowRisk_ok": "Prodotto a lunga conservazione, ancora sicuro da consumare",
- "tip_lowRisk_check": "Scaduto da oltre un mese, controllare integrità confezione",
- "tip_lowRisk_danger": "Scaduto da troppo tempo, meglio non rischiare"
- },
- "toast": {
- "product_saved": "Prodotto salvato!",
- "product_created": "Prodotto creato!",
- "product_updated": "✅ Prodotto aggiornato!",
- "product_removed": "Prodotto rimosso",
- "updated": "Aggiornato!",
- "quantity_confirmed": "✓ Quantità confermata",
- "added_to_inventory": "✅ {name} aggiunto!",
- "removed_from_list": "✅ {name} rimosso dalla lista!",
- "removed_from_list_short": "Rimosso dalla lista",
- "added_to_shopping": "🛒 Aggiunto alla lista della spesa!",
- "removed_from_shopping": "🛒 Rimosso dalla lista della spesa",
- "finished_to_bring": "🛒 Prodotto finito → aggiunto a Bring!",
- "thrown_away": "🗑️ {name} buttato!",
- "thrown_away_partial": "🗑️ Buttato {qty} {unit} di {name}",
- "finished_all": "📤 {name} terminato!",
- "vacuum_sealed": "{name} salvato come sottovuoto",
- "product_finished_confirmed": "✅ Rimosso — riaggiungi quando ne ricompri",
- "appliance_added": "Elettrodomestico aggiunto",
- "item_added": "{name} aggiunto"
- },
- "antiwaste": {
- "title": "🌱 Rapporto Anti-Spreco",
- "grade_label": "Voto",
- "you": "Tu",
- "avg_label": "Media",
- "better": "🎉 Perdi il {diff}% in meno della {country}!",
- "worse": "⚠️ Perdi più della media {country}. Puoi migliorare!",
- "on_par": "→ Sei nella media {country}. Prova a fare ancora meglio!",
- "saved_money": "~{amount}/mese risparmiati",
- "saved_meals": "~{n} pasti salvati",
- "saved_co2": "{n} kg CO₂ evitata",
- "trend_title": "Andamento (ultimi 3 mesi)",
- "months_ago_2": "-60 gg",
- "months_ago_1": "-30 gg",
- "this_month": "Ora",
- "country_it": "media italiana",
- "country_de": "media tedesca",
- "country_en": "media USA",
- "source": "Fonti: REDUCE, Eurostat, USDA 2021",
- "live_on": "Dati in tempo reale",
- "live_off": "Offline",
- "meals": "pasti",
- "annual_info": "📅 Tu ~{you} kg/anno · media ~{avg} kg/anno",
- "badge_rate": "tasso perdita",
- "badge_saved_money": "risparmio vs media",
- "badge_wasted": "prod. persi",
- "badge_better": "in meno vs media"
- },
- "error": {
- "generic": "Errore",
- "network": "Errore di rete",
- "no_api_key": "Configura la chiave API nelle impostazioni",
- "loading": "Errore nel caricamento del prodotto",
- "not_found": "Prodotto non trovato",
- "not_found_manual": "Prodotto non trovato. Inseriscilo manualmente.",
- "search": "Errore nella ricerca. Riprova.",
- "search_short": "Errore nella ricerca",
- "save": "Errore nel salvataggio",
- "connection": "Errore di connessione",
- "camera": "Impossibile accedere alla fotocamera",
- "bring_add": "Errore nell'aggiunta a Bring!",
- "bring_connection": "Errore connessione Bring!",
- "identification": "Errore nell'identificazione",
- "ai_quota": "Quota AI esaurita. Riprova tra qualche minuto.",
- "barcode_empty": "Inserisci un codice a barre",
- "barcode_format": "Il codice a barre deve contenere solo numeri (4-14 cifre)",
- "min_chars": "Scrivi almeno 2 caratteri",
- "not_in_inventory": "Prodotto non nell'inventario",
- "appliance_exists": "Elettrodomestico già presente",
- "already_exists": "Già presente",
- "network_retry": "Errore di connessione. Riprova.",
- "select_items": "Seleziona almeno un prodotto",
- "server_offline": "Connessione al server persa",
- "server_restored": "Connessione al server ripristinata",
- "server_retry": "Riprova",
- "unknown": "Errore sconosciuto",
- "prefix": "Errore",
- "no_inventory_entry": "Nessuna voce di inventario trovata",
- "offline_title": "Nessuna connessione",
- "offline_subtitle": "L'app non riesce a raggiungere il server. Verifica la connessione Wi-Fi.",
- "offline_checking": "Verifica connessione…",
- "offline_restored": "Connessione ripristinata!",
- "offline_continue": "Continua in modalità offline",
- "offline_reading_cache": "Lettura dalla cache locale",
- "offline_ops_pending": "{n} operazioni in attesa",
- "offline_synced": "{n} operazioni sincronizzate",
- "offline_ai_disabled": "Non disponibile offline",
- "offline_cache_ready": "Offline — {n} prodotti in cache"
- },
- "confirm": {
- "remove_item": "Vuoi davvero rimuovere questo prodotto dall'inventario?",
- "kiosk_exit": "Uscire dalla modalità kiosk?",
- "cancel": "Annulla",
- "proceed": "Conferma",
- "discard_one": "Butta 1 pezzo"
- },
- "location": {
- "dispensa": "Dispensa",
- "frigo": "Frigo",
- "freezer": "Freezer"
- },
- "edit": {
- "title": "Modifica {name}",
- "unknown_hint": "Inserisci il nome e le informazioni del prodotto",
- "label_name": "🏷️ Nome prodotto",
- "choose_location_title": "Quale modifica?",
- "choose_location_hint": "Scegli la posizione da modificare:",
- "confirm_large_qty": "Stai impostando la quantità a {qty} {unit}. Questo sembra un valore insolitamente alto. Confermare?"
- },
- "screensaver": {
- "recipe_btn": "Ricette",
- "scan_btn": "Scansiona prodotto"
- },
- "days": {
- "mon": "Lunedì",
- "tue": "Martedì",
- "wed": "Mercoledì",
- "thu": "Giovedì",
- "fri": "Venerdì",
- "sat": "Sabato",
- "sun": "Domenica",
- "mon_short": "Lun",
- "tue_short": "Mar",
- "wed_short": "Mer",
- "thu_short": "Gio",
- "fri_short": "Ven",
- "sat_short": "Sab",
- "sun_short": "Dom"
- },
- "meal_types": {
- "lunch": "Pranzo",
- "dinner": "Cena",
- "colazione": "Colazione",
- "merenda": "Merenda",
- "dolce": "Dolce",
- "succo": "Succo di Frutta",
- "pranzo": "Pranzo",
- "cena": "Cena"
- },
- "scale": {
- "status_connected": "Bilancia connessa",
- "status_searching": "Connesso al gateway, attesa bilancia…",
- "status_disconnected": "Gateway bilancia non raggiungibile",
- "status_error": "Errore connessione gateway",
- "not_connected": "Gateway bilancia non connesso",
- "read_btn": "⚖️ Leggi dalla bilancia",
- "reading_title": "Lettura bilancia",
- "place_on_scale": "Metti il prodotto sulla bilancia…",
- "waiting_stable": "Il peso venire rilevato automaticamente quando la lettura sarà stabile.",
- "no_url": "Inserisci l'URL del gateway",
- "testing": "⏳ Test connessione…",
- "connected_ok": "Connessione gateway riuscita!",
- "timeout": "Timeout: nessuna risposta dal gateway",
- "error_connect": "Impossibile connettersi al gateway",
- "tab": "Bilancia Smart",
- "low_weight": "Peso < 10 g · inserisci manualmente\n(la lettura automatica richiede almeno 10 g)",
- "density_hint": "(densità {density} g/ml)",
- "ml_hint": "(verrà convertito in ml)",
- "weight_detected": "Peso rilevato — attendi 10s di stabilità…",
- "weight_too_low": "Peso troppo basso — attendi…",
- "stable": "✓ Stabile",
- "auto_confirm": "✅ {val} {unit} — conferma automatica tra 5s (tocca per annullare)",
- "cancelled_replace": "Annullato — rimetti l'ingrediente sulla bilancia per riprendere"
- },
- "prediction": {
- "expected_qty": "Previsto: {expected} {unit}",
- "actual_qty": "Attuale: {actual} {unit}",
- "check_suggestion": "Verifica o pesa la quantità residua"
- },
- "date": {
- "today": "📅 Oggi",
- "yesterday": "📅 Ieri"
- },
- "scanner": {
- "title_barcode": "🔖 Scansiona Barcode",
- "barcode_hint": "Inquadra il codice a barre del prodotto",
- "barcode_manual_placeholder": "O inserisci manualmente...",
- "barcode_use_btn": "✅ Usa questo codice",
- "ai_identifying": "🤖 Identifico il prodotto...",
- "ai_analyzing": "🤖 Analisi AI in corso...",
- "product_label_hint": "Inquadra l'etichetta del prodotto",
- "expiry_label_hint": "Inquadra la data di scadenza stampata sul prodotto",
- "capture_btn": "📸 Scatta",
- "capture_photo_btn": "📸 Scatta Foto",
- "retake_btn": "🔄 Riscatta",
- "camera_error_hint": "Assicurati di usare HTTPS e di aver concesso i permessi della fotocamera. Puoi inserire il barcode manualmente o usare l'identificazione AI.",
- "no_barcode": "Senza barcode",
- "save_new_btn": "🆕 Non è nessuno di questi — salva come nuovo",
- "expiry_found": "Data trovata",
- "expiry_read_fail": "Non riesco a leggere la data.",
- "expiry_raw_label": "Letto"
- },
- "lowstock": {
- "title": "⚠️ Sta per finire!",
- "message": "{name} sta per finire — rimangono solo {qty}.",
- "question": "Vuoi aggiungerlo alla lista della spesa?",
- "yes": "🛒 Sì, aggiungi a Bring!",
- "no": "No, per ora va bene"
- },
- "move": {
- "title": "📦 Spostare il resto?",
- "question": "Vuoi spostare {thing} di {name} in un'altra posizione?",
- "question_short": "Vuoi spostare {thing} in un'altra posizione?",
- "thing_opened": "la confezione aperta",
- "thing_rest": "il resto",
- "stay_btn": "No, resta in {location}",
- "moved_toast": "📦 Confezione aperta spostata in {location}",
- "vacuum_restore": "Torna sotto vuoto",
- "vacuum_seal_rest": "Metti sotto vuoto il resto"
- },
- "nova": {
- "1": "Non trasformato",
- "2": "Ingrediente culinario",
- "3": "Trasformato",
- "4": "Ultra-trasformato"
- },
- "meal_plan_types": {
- "pasta": "Pasta",
- "riso": "Riso",
- "carne": "Carne",
- "pesce": "Pesce",
- "legumi": "Legumi",
- "uova": "Uova",
- "formaggio": "Formaggio",
- "pizza": "Pizza",
- "affettati": "Affettati",
- "verdure": "Verdure",
- "zuppa": "Zuppa",
- "insalata": "Insalata",
- "pane": "Pane/Sandwich",
- "dolce": "Dolce",
- "libero": "Libero"
- },
- "meal_sub": {
- "dolce_torta": "Torta",
- "dolce_crema": "Crema / Budino",
- "dolce_crumble": "Crumble / Crostata",
- "dolce_biscotti": "Biscotti / Pasticcini",
- "dolce_frutta": "Dolce alla Frutta",
- "succo_dolce": "Dolce / Fruttato",
- "succo_energizzante": "Energizzante",
- "succo_detox": "Detox / Verde",
- "succo_rinfrescante": "Rinfrescante",
- "succo_vitaminico": "Vitaminico / Agrumi"
- },
- "meal_plan": {
- "reset_success": "Piano settimanale ripristinato",
- "suggested_by": "suggerito dal piano settimanale",
- "not_available": "non disponibile in dispensa"
- },
- "kiosk_session": {
- "first_item": "Primo prodotto: {name}!",
- "items_two_four": "{n} prodotti — stai scaldando i motori 🚀",
- "items_five_nine": "{n} prodotti — ottimo ritmo! 💪",
- "items_ten_twenty": "{n} prodotti — quasi un recordman 🏆",
- "items_twenty_plus": "{n} prodotti — spesa epica! 🛒🔥",
- "duplicates_one": "1 bis (stessa cosa due volte)",
- "duplicates_many": "{n} bis (roba presa più volte)",
- "top_category": "Categoria top: {cat} ({count}×)",
- "items_fallback": "{n} prodott{n} aggiunti"
- },
- "nutrition": {
- "title": "🥗 Analisi Alimentare",
- "score_excellent": "😄 Ottimo",
- "score_good": "🙂 Discreto",
- "score_improve": "😬 Migliorabile",
- "label_health": "🌿 Salute",
- "label_variety": "🎨 Varietà",
- "label_fresh": "❄️ Freschi",
- "source": "Basato su {n} prodotti in dispensa · EverShelf",
- "products_count": "prodotti",
- "today_title": "🥗 La tua dispensa oggi",
- "products_n": "{n} prodotti",
- "macros_title": "Macronutrienti stimati",
- "macros_proteins": "Proteine",
- "macros_carbs": "Carboidrati",
- "macros_fat": "Grassi",
- "macros_fiber": "Fibre",
- "macros_source": "Stima basata su {n} prodotti in dispensa"
- },
- "facts": {
- "greeting_morning": "Buongiorno",
- "greeting_afternoon": "Buon pomeriggio",
- "greeting_evening": "Buonasera",
- "pantry_waiting": "{greeting}! La tua Dispensa ti aspetta.",
- "expired_one": "Hai 1 prodotto scaduto in dispensa. Controlla!",
- "expired_many": "Hai {n} prodotti scaduti in dispensa. Controlla!",
- "expired_list": "Prodotti scaduti: {names}",
- "expired_list_more": "e altri {n}",
- "freezer_expired_ok": "{name} è scaduto, ma essendo in freezer potrebbe essere ancora buono! Controlla.",
- "freezer_expired_old": "{name} in freezer è scaduto da troppo tempo. Meglio buttarlo.",
- "fridge_expired_one": "Hai 1 prodotto scaduto in frigo!",
- "fridge_expired_many": "Hai {n} prodotti scaduti in frigo!",
- "expiring_today": "{name} scade oggi! Usalo subito.",
- "expiring_tomorrow": "{name} scade domani. Pensaci!",
- "expiring_days": "{name} scade tra {days} giorni.",
- "expiring_many": "Hai {n} prodotti in scadenza ravvicinata.",
- "expiring_this_week": "Questa settimana scadono {n} prodotti. Pianifica i pasti di conseguenza!",
- "expiring_item_loc": "{name} ({loc}) scade tra {days} {dayslabel}.",
- "expiring_this_month": "In questo mese scadranno {n} prodotti.",
- "shopping_add": "Metti in lista: {names} 🛒",
- "shopping_more": "e altri {n}",
- "shopping_empty": "Lista della spesa vuota. Tutto rifornito! ✅",
- "in_fridge": "In frigo c'è: {name}.",
- "in_freezer": "Nel freezer c'è: {name}. Non dimenticartelo!",
- "top_category": "La categoria più presente è {icon} {cat} con {n} prodotti.",
- "cat_meat": "Hai {n} prodotti di carne. 🥩",
- "cat_dairy": "Hai {n} latticini in casa. 🥛",
- "cat_veggies": "Hai {n} tipi di verdura. Ottimo per la salute! 🥬",
- "cat_fruit": "Hai {n} tipi di frutta. 🍎",
- "cat_drinks": "Hai {n} bevande disponibili. 🥤",
- "cat_frozen": "Hai {n} surgelati nel freezer. ❄️",
- "cat_pasta": "Hai {n} tipi di pasta. 🍝 Che ne dici di una carbonara?",
- "cat_canned": "Hai {n} conserve in dispensa. 🥫",
- "cat_snacks": "Hai {n} snack. Resisti alla tentazione! 🍪",
- "cat_condiments": "Hai {n} condimenti a disposizione. 🧂",
- "item_random": "Lo sapevi? Hai {name} in {loc}.",
- "item_qty": "{name}: ne hai {qty}.",
- "no_expiry_count": "{n} prodotti non hanno una data di scadenza impostata.",
- "furthest_expiry": "Il prodotto con scadenza più lontana è {name}: {months} mesi.",
- "high_qty": "Hai una bella scorta di {name}: {qty}!",
- "low_qty_item": "{name} sta per finire. Aggiungilo alla spesa?",
- "low_qty_count": "Ci sono {n} prodotti quasi finiti.",
- "morning_bread": "Buongiorno! Hai del pane per la colazione. 🍞",
- "morning_milk": "C'è del latte in frigo per il cappuccino? ☕🥛",
- "morning_fruit": "Buongiorno! Una bella frutta fresca per iniziare bene. 🍎",
- "noon_pasta": "Ora di pranzo… Un bel piatto di pasta? 🍝",
- "noon_salad": "Un'insalata fresca per pranzo? Hai {n} verdure! 🥗",
- "evening_meat": "Per cena potresti usare la carne che hai. 🥩",
- "evening_fish": "Che ne dici di pesce per cena? 🐟",
- "evening_expiring": "Hai {n} prodotti in scadenza questa settimana — usali stasera!",
- "night_reminder": "Buonanotte! Domani ricordati di usare: {names}.",
- "weekly_balance": "Bilancio settimana: +{in} aggiunti, −{out} consumati.",
- "weekly_added": "Questa settimana hai aggiunto {n} prodotti.",
- "weekly_consumed": "Questa settimana hai consumato {n} prodotti. Ottimo!",
- "tip_freezer": "💡 I prodotti in freezer durano molto più a lungo della data di scadenza.",
- "tip_bread": "💡 Il pane congelato mantiene la fragranza per settimane.",
- "tip_fifo": "💡 Per evitare sprechi, usa prima i prodotti con scadenza più vicina (FIFO).",
- "tip_meat": "💡 La carne in freezer può durare fino a 6 mesi senza problemi.",
- "tip_no_refreeze": "💡 Non ricongelare mai un alimento già scongelato. Cucinalo subito!",
- "tip_fridge": "💡 Un frigo ordinato ti fa risparmiare tempo e denaro.",
- "tip_canned": "💡 Le conserve aperte vanno in frigo e consumate in pochi giorni.",
- "top_brand": "Il marchio più presente nella tua dispensa è {brand} con {n} prodotti.",
- "combo_pasta": "Hai pasta e condimenti: sei pronto per un primo piatto! 🍝",
- "combo_sandwich": "Pane e carne: un panino veloce è sempre una buona idea! 🥪",
- "combo_balanced": "Verdura e carne: hai tutto per un piatto equilibrato! 🥗🥩",
- "pantry_empty": "La dispensa è vuota! Fai una bella spesa. 🛒",
- "pantry_empty_scan": "Nessun prodotto registrato. Scansiona qualcosa per iniziare!",
- "location_distribution": "Distribuzione: {parts}",
- "day": "giorno",
- "days": "giorni"
- },
- "kiosk": {
- "check_btn": "🔍 Cerca aggiornamenti",
- "checking": "⏳ Controllo…",
- "error_check": "Errore durante il controllo",
- "error_start_install": "Errore avvio installazione",
- "version_installed": "Installata: {v}",
- "update_available": "⬆️ Nuova versione disponibile: {latest} (installata: {current})",
- "up_to_date": "✅ Sei già aggiornato — versione {v}",
- "too_old": "⚠️ Il kiosk installato è troppo vecchio per il controllo automatico. Premi il pulsante qui sotto per scaricare e installare la nuova versione direttamente.",
- "manual_install": "⚠️ Questo kiosk non supporta l'installazione automatica. Procedura manuale: 1. Esci dal kiosk (tasto ✕ in alto a sinistra) 2. Disinstalla l'app EverShelf Kiosk 3. Scarica e installa la nuova APK da GitHub:",
- "starting_download": "⏳ Avvio download…",
- "install_btn": "⬇️ Installa aggiornamento",
- "exit_title": "Esci dal kiosk",
- "refresh_title": "Aggiorna pagina"
- },
- "update": {
- "new_version": "Nuova versione",
- "btn": "Aggiorna"
- },
+ "app": {
+ "name": "EverShelf",
+ "loading": "Caricamento..."
+ },
+ "nav": {
+ "title": "EverShelf",
+ "home": "Home",
+ "inventory": "Dispensa",
+ "recipes": "Ricette",
+ "shopping": "Spesa",
+ "log": "Storico",
+ "settings": "Config"
+ },
+ "btn": {
+ "back": "← Indietro",
+ "save": "💾 Salva",
+ "cancel": "✕ Annulla",
+ "close": "Chiudi",
+ "add": "✅ Aggiungi",
+ "delete": "Elimina",
+ "edit": "✏️ Modifica",
+ "use": "Usa",
+ "edit_item": "Modifica",
+ "search": "🔍 Cerca",
+ "go": "✅ Vai",
+ "toggle_password": "👁️ Mostra/Nascondi",
+ "load_more": "Carica altri...",
+ "save_config": "💾 Salva Configurazione",
+ "save_product": "💾 Salva Prodotto",
+ "restart": "↺ Ricomincia",
+ "reset_default": "↺ Ripristina default",
+ "save_info": "💾 Salva informazioni",
+ "retry": "🔄 Riprova",
+ "yes_short": "Sì",
+ "no_short": "No"
+ },
+ "form": {
+ "select_placeholder": "-- Seleziona --"
+ },
+ "locations": {
+ "dispensa": "Dispensa",
+ "frigo": "Frigo",
+ "freezer": "Freezer",
+ "altro": "Altro"
+ },
+ "categories": {
+ "latticini": "Latticini",
+ "carne": "Carne",
+ "pesce": "Pesce",
+ "frutta": "Frutta",
+ "verdura": "Verdura",
+ "pasta": "Pasta & Riso",
+ "pane": "Pane & Forno",
+ "surgelati": "Surgelati",
+ "bevande": "Bevande",
+ "condimenti": "Condimenti",
+ "snack": "Snack & Dolci",
+ "conserve": "Conserve",
+ "cereali": "Cereali & Legumi",
+ "igiene": "Igiene",
+ "pulizia": "Pulizia Casa",
+ "altro": "Altro",
+ "select": "-- Seleziona --"
+ },
+ "units": {
+ "pz": "pz",
+ "conf": "conf",
+ "g": "g",
+ "ml": "ml",
+ "pieces": "Pezzi",
+ "grams": "Grammi",
+ "box": "Confezione",
+ "boxes": "Confezioni",
+ "millilitres": "Millilitri",
+ "from": "da"
+ },
+ "shopping_sections": {
+ "frutta_verdura": "Frutta & Verdura",
+ "carne_pesce": "Carne & Pesce",
+ "latticini": "Latticini & Fresco",
+ "pane_dolci": "Pane & Dolci",
+ "pasta": "Pasta & Cereali",
+ "conserve": "Conserve & Salse",
+ "surgelati": "Surgelati",
+ "bevande": "Bevande",
+ "pulizia_igiene": "Pulizia & Igiene",
+ "altro": "Altro"
+ },
+ "dashboard": {
+ "expired_title": "🚫 Scaduti",
+ "expiring_title": "⏰ Prossime Scadenze",
+ "stats_period": "📊 Ultimi 30 giorni",
+ "opened_title": "📦 Prodotti Aperti",
+ "review_title": "🔍 Da revisionare",
+ "review_hint": "Quantità che sembrano anomale. Conferma se corrette o modifica.",
+ "quick_recipe": "Ricetta veloce con prodotti in scadenza",
+ "banner_review_title": "Quantità anomala",
+ "banner_review_action_ok": "È corretto",
+ "banner_review_action_finish": "🗑️ È finito tutto",
+ "banner_review_action_edit": "Correggi",
+ "banner_review_action_weigh": "Pesa",
+ "banner_review_dismiss": "Ignora",
+ "banner_prediction_title": "Consumo da verificare",
+ "banner_prediction_hint": "La stima di consumo si adatta ai dati recenti: verifica solo se la quantità corrente è corretta.",
+ "banner_prediction_action_confirm": "Confermo {qty} {unit}",
+ "banner_prediction_action_weigh": "Pesa ora",
+ "banner_prediction_action_edit": "Aggiorna quantità",
+ "banner_expired_title": "Prodotto scaduto",
+ "banner_expired_today": "Scaduto oggi",
+ "banner_expired_days": "Scaduto da {days} giorni",
+ "banner_expired_action_use": "Usa comunque",
+ "banner_expired_action_finished": "L'ho finito!",
+ "banner_expired_action_throw": "L'ho buttato",
+ "banner_expired_action_edit": "Correggi data",
+ "banner_expired_action_modify": "Modifica",
+ "banner_expired_action_vacuum": "Metti sottovuoto",
+ "banner_anomaly_action_edit": "Correggi inventario",
+ "banner_anomaly_action_dismiss": "La quantità è giusta",
+ "banner_no_expiry_title": "Scadenza mancante: {name}",
+ "banner_no_expiry_detail": "Questo prodotto non ha una data di scadenza. Vuoi aggiungerla o confermare che non scade?",
+ "banner_no_expiry_action_set": "Imposta scadenza",
+ "banner_no_expiry_action_dismiss": "Non scade ✓",
+ "banner_no_expiry_toast_dismissed": "Segnato come 'non scade'",
+ "banner_expiring_title": "In scadenza",
+ "banner_expiring_today": "Scade oggi!",
+ "banner_expiring_tomorrow": "Scade domani",
+ "banner_expiring_days": "Scade tra {days} giorni",
+ "banner_expiring_action_use": "Usa ora",
+ "banner_finished_title": "è finito?",
+ "banner_finished_detail": "Ho registrato che {name} ha toccato quota zero. È davvero finito o hai ancora delle scorte?",
+ "banner_finished_action_yes": "Sì, è finito",
+ "banner_finished_action_no": "No, ne ho ancora",
+ "banner_review_unusual_pkg_title": "Confezione insolita",
+ "banner_review_unusual_pkg_detail": "Hai impostato una confezione da {qty} {unit} — la dimensione sembra molto alta. Controlla se è corretta o modifica.",
+ "banner_review_low_qty_title": "Quantità molto bassa",
+ "banner_review_low_qty_detail": "Hai solo {qty} in inventario — sembra poco, potrebbe essere un errore. Conferma se è corretto.",
+ "banner_review_high_qty_title": "Quantità insolitamente alta",
+ "banner_review_high_qty_detail": "Hai {qty} in inventario — la cifra sembra molto alta. Conferma se è corretto o correggi.",
+ "banner_prediction_rate_day": "Media ~{n} {unit}/giorno",
+ "banner_prediction_rate_week": "Media ~{n} {unit}/settimana",
+ "banner_prediction_days_ago": "{n} giorni fa hai rifornito",
+ "banner_prediction_more": "stima precedente: {expected} {unit}{time}; quantità attuale: {actual} {unit}.",
+ "banner_prediction_less": "stima: {expected} {unit}{time}; quantità attuale: {actual} {unit}. Se hai cambiato ritmo d'uso, la previsione si aggiorna automaticamente.",
+ "banner_finished_zero": "L'inventario segna zero, ma i movimenti registrati dicono che non dovrebbe essere finito.",
+ "banner_finished_expected": "Secondo le registrazioni dovresti averne ancora {qty} {unit}.",
+ "banner_finished_check": "Puoi controllare?",
+ "banner_anomaly_phantom_title": "hai più scorte del previsto",
+ "banner_anomaly_phantom_detail": "L'inventario segna {inv_qty} {unit}, ma in base alle registrazioni ne dovresti avere solo {expected_qty} {unit}. Hai aggiunto scorte senza registrarle?",
+ "banner_anomaly_untracked_title": "scorte non registrate come entrata",
+ "banner_anomaly_untracked_detail": "Hai {inv_qty} {unit} in inventario, ma le uscite registrate superano le entrate — le scorte iniziali probabilmente non sono mai state aggiunte come entrata. Puoi correggere la quantità o registrare le entrate mancanti.",
+ "banner_anomaly_ghost_title": "hai meno scorte del previsto",
+ "banner_anomaly_ghost_detail": "In base alle operazioni registrate dovresti avere {expected_qty} {unit} di {name}, ma l'inventario mostra solo {inv_qty} {unit}. Hai prelevato senza registrarlo?",
+ "consumed": "Consumati: {n} ({pct}%)",
+ "wasted": "Buttati: {n} ({pct}%)",
+ "more_opened": "e altri {n} prodotti aperti...",
+ "banner_expired_detail": "{when} · hai ancora {qty}.",
+ "banner_opened_detail": "{when} in {location} · hai ancora {qty}.",
+ "banner_explain_title": "Chiedi a Gemini una spiegazione",
+ "banner_explain_btn": "Spiega",
+ "banner_analyzing": "🤖 Analizzo…"
+ },
+ "inventory": {
+ "title": "Dispensa",
+ "filter_all": "Tutti",
+ "search_placeholder": "🔍 Cerca prodotto...",
+ "recent_title": "🕐 Ultimi usati",
+ "popular_title": "⭐ Più usati",
+ "empty": "Nessun prodotto qui.\nScansiona un prodotto per aggiungerlo!",
+ "no_items_found": "Nessuna voce di inventario trovata",
+ "qty_remainder_suffix": "rimasti",
+ "vacuum_badge": "🫙 Sotto vuoto",
+ "opened_badge": "📭 Aperto",
+ "label_expiry": "📅 Scadenza",
+ "label_storage": "🫙 Conservazione",
+ "label_status": "📭 Stato",
+ "opened_since": "Aperto dal {date}",
+ "label_position": "📍 Posizione",
+ "label_quantity": "📦 Quantità",
+ "label_added": "📅 Aggiunto",
+ "empty_text": "Nessun prodotto qui. Scansiona un prodotto per aggiungerlo!",
+ "empty_db": "Nessun prodotto nel database. Scansiona un prodotto per iniziare!",
+ "qty_trace": "< 1"
+ },
+ "scan": {
+ "title": "Scansiona",
+ "mode_shopping": "🛒 Modalità Spesa",
+ "mode_shopping_end": "✅ Fine spesa",
+ "spesa_btn": "🛒 Spesa",
+ "zoom": "Zoom",
+ "tab_barcode": "Barcode",
+ "tab_name": "Nome",
+ "tab_ai": "AI",
+ "recents_label": "Recenti",
+ "torch_hint": "Torcia",
+ "torch_on": "Torcia accesa",
+ "torch_off": "Torcia spenta",
+ "torch_unavailable": "Torcia non disponibile su questo dispositivo",
+ "flip_hint": "Cambia fotocamera",
+ "flip_front": "Fotocamera anteriore",
+ "flip_back": "Fotocamera posteriore",
+ "num_ocr_btn": "🔢 Leggi numeri con AI",
+ "num_ocr_searching": "Cerco il codice con AI...",
+ "num_ocr_found": "Codice trovato: {code}",
+ "num_ocr_not_found": "Nessun codice trovato nell'immagine",
+ "barcode_placeholder": "Inserisci codice a barre...",
+ "quick_name_divider": "oppure scrivi il nome",
+ "quick_name_placeholder": "Es: Mele, Zucchine, Pane...",
+ "manual_entry": "✏️ Inserimento Manuale",
+ "ai_identify": "🤖 Identifica con AI",
+ "hint": "Scansiona il barcode, scrivi il nome del prodotto, oppure usa l'AI per identificarlo",
+ "debug_toggle": "🐛 Debug Log",
+ "barcode_acquired": "🔖 Barcode acquisito: {code}",
+ "scan_barcode": "🔖 Scansiona Barcode",
+ "create_named": "Crea {name}",
+ "new_without_barcode": "Nuovo prodotto senza barcode",
+ "stock_in_pantry": "Hai gia in dispensa:",
+ "status_ready": "Inquadra il codice a barre",
+ "status_scanning": "Scansione in corso...",
+ "status_partial": "Letto: {code} — verifico...",
+ "status_invalid": "Non valido: {code} — riprovo",
+ "status_confirmed": "Confermato!",
+ "status_parallel": "Doppia scansione attiva...",
+ "ai_fallback_searching": "Identificazione AI in corso...",
+ "ai_fallback_found": "Prodotto identificato dall'AI",
+ "ai_fallback_not_found": "AI: prodotto non riconosciuto"
+ },
+ "action": {
+ "title": "Cosa vuoi fare?",
+ "add_btn": "📥 AGGIUNGI",
+ "add_sub": "in dispensa/frigo",
+ "use_btn": "USA",
+ "use_sub": "dalla dispensa/frigo",
+ "have_title": "📦 Ce l'hai già!",
+ "add_more_sub": "altra quantità",
+ "use_qty_sub": "quanto ne hai usato",
+ "throw_btn": "🗑️ BUTTA",
+ "throw_sub": "butta il prodotto",
+ "edit_sub": "scadenza, luogo…",
+ "create_recipe_btn": "Ricetta",
+ "related_stock_title": "Hai anche in casa"
+ },
+ "add": {
+ "title": "Aggiungi alla Dispensa",
+ "location_label": "📍 Dove lo metti?",
+ "quantity_label": "📦 Quantità",
+ "conf_size_label": "📦 Ogni confezione contiene:",
+ "conf_size_placeholder": "es. 300",
+ "vacuum_label": "🫙 Sotto vuoto",
+ "vacuum_hint": "La scadenza verrà estesa automaticamente",
+ "submit": "✅ Aggiungi",
+ "purchase_type_label": "🛒 Questo prodotto è...",
+ "new_btn": "🆕 Appena comprato",
+ "existing_btn": "📦 Ce l'avevo già",
+ "remaining_label": "📦 Quantità rimasta",
+ "remaining_hint": "Quanto è rimasto approssimativamente?",
+ "remaining_full": "🟢 Pieno",
+ "remaining_half": "🟠 Metà",
+ "estimated_expiry": "Scadenza stimata:",
+ "suffix_freezer": "(freezer)",
+ "suffix_vacuum": "(sotto vuoto)",
+ "hint_modify": "📝 Puoi modificare la data o scansionarla con la fotocamera",
+ "scan_expiry_title": "📷 Scansiona Data Scadenza",
+ "product_added": "✅ {name} aggiunto!{qty}",
+ "suffix_freezer_vacuum": "(freezer + sotto vuoto)",
+ "history_badge_tip": "Media da {n} inserimenti precedenti",
+ "vacuum_question": "Messo sotto vuoto?",
+ "vacuum_saved": "🔒 Sotto vuoto registrato"
+ },
+ "use": {
+ "title": "Usa / Consuma",
+ "location_label": "📍 Da dove?",
+ "quantity_label": "Quanto hai usato?",
+ "change": "cambia",
+ "partial_hint": "Oppure specifica la quantità usata:",
+ "partial_piece_hint": "Hai usato solo una parte?",
+ "piece": "pezzo",
+ "one_whole": "1 intero",
+ "use_all": "🗑️ Usato TUTTO / Finito",
+ "submit": "📤 Usa questa quantità",
+ "available": "📦 Disponibile:",
+ "opened_badge": "APERTO",
+ "not_in_inventory": "⚠️ Prodotto non presente nell'inventario.",
+ "expiry_warning": "⚠️ Usa prima quella{loc} che scade il {date} — {when}!",
+ "expiry_warning_opened": "⚠️ Quella{loc}, aperta da {when} — usala prima!",
+ "throw_title": "🗑️ Butta Prodotto",
+ "throw_all": "🗑️ Butta TUTTO ({qty})",
+ "throw_qty_label": "Quanto butti?",
+ "throw_qty_hint": "oppure specifica la quantità:",
+ "throw_partial_btn": "🗑️ Butta questa quantità",
+ "when_expired": "scaduta da {n} giorni",
+ "when_today": "scade oggi",
+ "when_tomorrow": "scade domani",
+ "when_days": "scade tra {n} giorni",
+ "toast_used": "📤 Usato {qty} di {name}",
+ "toast_bring": "🛒 Prodotto finito → aggiunto a Bring!",
+ "toast_opened_finished": "🔓 Confezione aperta di {name} finita!",
+ "disambiguation_hint": "Cosa intendi con \"finito tutto\"?",
+ "disambiguation_all": "🗑️ Finito TUTTO ({qty})",
+ "error_exceeds_stock": "⚠️ Non puoi usare più di quanto hai disponibile!",
+ "use_all_confirm_title": "✅ Finisci tutto",
+ "use_all_confirm_msg": "Conferma che hai finito tutto il prodotto:",
+ "use_all_confirm_btn": "✅ Sì, finito",
+ "throw_all_confirm_title": "🗑️ Butta tutto",
+ "throw_all_confirm_msg": "Vuoi davvero buttare via tutto il prodotto?",
+ "throw_all_confirm_btn": "🗑️ Sì, butta"
+ },
+ "product": {
+ "title_new": "Nuovo Prodotto",
+ "title_edit": "Modifica Prodotto",
+ "ai_fill": "📷 Scatta foto e identifica con AI",
+ "ai_fill_hint": "L'AI compilerà automaticamente i campi del prodotto",
+ "name_label": "🏷️ Nome Prodotto *",
+ "name_placeholder": "Es: Latte intero, Pasta penne rigate...",
+ "brand_label": "🏢 Marca",
+ "brand_placeholder": "Es: Barilla, Granarolo, Mutti...",
+ "category_label": "📂 Categoria",
+ "unit_label": "📏 Unità di misura",
+ "default_qty_label": "🔢 Quantità default",
+ "conf_size_label": "📦 Ogni confezione contiene:",
+ "conf_size_placeholder": "es. 300",
+ "notes_label": "📝 Note",
+ "notes_placeholder": "Es: senza lattosio, bio, conservare in frigo dopo apertura...",
+ "barcode_label": "🔖 Barcode",
+ "barcode_placeholder": "Codice a barre (se disponibile)",
+ "barcode_hint": "⚠️ Aggiungi il barcode così al prossimo acquisto basta scansionarlo!",
+ "submit": "💾 Salva Prodotto",
+ "name_required": "Inserisci il nome del prodotto",
+ "conf_size_required": "Specifica il contenuto di ogni confezione",
+ "expiry_estimated": "Scadenza stimata:",
+ "scan_expiry": "Scansiona data scadenza",
+ "expiry_hint": "📝 Puoi modificare la data o scansionarla con la fotocamera",
+ "add_batch": "📦 + Lotto con scadenza diversa",
+ "package_info": "📦 Confezione: {info}",
+ "edit_catalog": "⚙️ Modifica scheda prodotto (nome, marca, categoria…)",
+ "not_recognized": "⚠️ Prodotto non riconosciuto",
+ "edit_info": "✏️ Modifica informazioni",
+ "modify_details": "MODIFICA\nscadenza, luogo…",
+ "already_in_pantry": "📋 Già in dispensa",
+ "no_barcode": "Senza barcode",
+ "unknown_product": "Prodotto non riconosciuto",
+ "edit_name_brand": "Modifica nome/marca",
+ "weight_label": "Peso",
+ "origin_label": "Origine",
+ "labels_label": "Etichette",
+ "select_variant": "Seleziona la variante esatta o usa i dati AI:"
+ },
+ "products": {
+ "title": "📦 Tutti i Prodotti",
+ "search_placeholder": "🔍 Cerca prodotto...",
+ "empty": "Nessun prodotto nel database.\nScansiona un prodotto per iniziare!",
+ "no_category": "Nessun prodotto in questa categoria"
+ },
+ "recipes": {
+ "title": "🍳 Ricette",
+ "generate": "✨ Genera nuova ricetta",
+ "archive_empty": "Nessuna ricetta salvata. Genera la tua prima ricetta!",
+ "dialog_title": "🍳 Ricetta",
+ "dialog_desc": "Genero una ricetta sana con gli ingredienti in dispensa, dando priorità a quelli in scadenza.",
+ "meal_label": "🕐 Per quale pasto?",
+ "persons_label": "👥 Quante persone?",
+ "meal_type_label": "🎯 Tipo di pasto",
+ "opt_fast": "⚡ Pasto Veloce",
+ "opt_light": "🥗 Poca Fame",
+ "opt_expiry": "⏰ Priorità Scadenze",
+ "opt_healthy": "💚 Extra Salutare",
+ "opt_opened": "📦 Priorità Cose Aperte",
+ "opt_zero_waste": "♻️ Zero Sprechi",
+ "generate_btn": "✨ Genera Ricetta",
+ "loading_msg": "Sto preparando la ricetta...",
+ "start_cooking": "👨🍳 Modalità Cucina",
+ "regenerate": "🔄 Generane un'altra",
+ "regen_choice_title": "Cosa vuoi fare con questa ricetta?",
+ "regen_replace": "🔄 Genera un'altra (scarta questa)",
+ "regen_save_new": "💾 Salva nell'archivio e genera una nuova",
+ "close_btn": "✅ Chiudi",
+ "ingredients_title": "🧾 Ingredienti",
+ "tools_title": "Strumenti necessari",
+ "steps_title": "👨🍳 Procedimento",
+ "no_steps": "Nessun procedimento disponibile",
+ "generate_error": "Errore nella generazione",
+ "stream_interrupted": "Generazione interrotta (risposta incompleta dal server). Controlla i log o riprova.",
+ "persons_short": "pers.",
+ "use_ingredient_title": "Usa ingrediente",
+ "recipe_qty_label": "Ricetta",
+ "from_where_label": "Da dove?",
+ "amount_label": "Quanto",
+ "use_amount_btn": "Usa questa quantità",
+ "use_all_btn": "Usa TUTTO / Finito",
+ "packs_label": "Confezioni",
+ "quantity_in_total": "Quantità in {unit} (totale: {total})",
+ "packs_of_have": "Confezioni da {size} (hai {count} conf)",
+ "scale_wait_stable": "Attendi 10s di stabilità per la compilazione automatica…",
+ "ingredient_scaled_toast": "📦 Ingrediente scalato dalla dispensa!",
+ "finished_added_bring_toast": "🛒 Prodotto finito → aggiunto a Bring!",
+ "load_error": "Errore nel caricamento",
+ "favorite": "Aggiungi ai preferiti",
+ "unfavorite": "Rimuovi dai preferiti",
+ "adjust_persons": "Persone"
+ },
+ "shopping": {
+ "title": "🛒 Lista della Spesa",
+ "bring_loading": "Connessione a Bring!...",
+ "bring_not_configured": "Bring! non è configurato. Aggiungi email e password nelle impostazioni.",
+ "tab_to_buy": "🛍️ Da comprare",
+ "tab_forecast": "🧠 In previsione",
+ "total_label": "💰 Totale stimato",
+ "section_to_buy": "🛍️ Da comprare",
+ "suggestions_title": "💡 Suggerimenti AI",
+ "suggestions_add": "✅ Aggiungi selezionati a Bring!",
+ "search_prices": "🔍 Cerca tutti i prezzi",
+ "suggest_btn": "Suggerisci cosa comprare",
+ "smart_title": "🧠 Previsioni intelligenti",
+ "smart_empty": "Nessuna previsione disponibile. Aggiungi prodotti alla dispensa per ricevere previsioni intelligenti.",
+ "smart_filter_all": "Tutti",
+ "smart_filter_critical": "🔴 Urgenti",
+ "smart_filter_high": "🟠 Presto",
+ "smart_filter_medium": "🟡 Pianifica",
+ "smart_filter_low": "🟢 Previsione",
+ "smart_add": "🛒 Aggiungi selezionati a Bring!",
+ "empty": "Lista della spesa vuota!\nUsa il pulsante sotto per generare suggerimenti.",
+ "already_in_list": "🛒 \"{name}\" già nella lista della spesa",
+ "already_in_list_short": "ℹ️ Già nella lista della spesa",
+ "add_prompt": "Vuoi aggiungerlo alla lista della spesa?",
+ "smart_already": "📊 La spesa intelligente prevede già {name}",
+ "all_searched": "Tutti i prodotti sono già stati cercati. Usa 🔄 per ricercare singoli.",
+ "search_complete": "Ricerca completata: {count} prodotti",
+ "removed_sufficient": "🧹 {removed} prodotto/i con scorte sufficienti rimosso/i dalla lista",
+ "suggest_buy": "🛒 Compra: {qty} {unit}",
+ "suggest_buy_approx": "🛒 Almeno: {qty} {unit}",
+ "suggest_buy_tip": "Quantità suggerita in base al consumo degli ultimi 14 giorni",
+ "suggest_buy_approx_tip": "Stima minima basata sul consumo (compra la confezione più vicina)",
+ "bring_badge": "🛒 Già su Bring!",
+ "add_urgent_toast": "🔴 {n} prodotto/i urgente/i aggiunto/i automaticamente a Bring!",
+ "migration_done": "✅ {migrated} aggiornati, {skipped} già ok",
+ "added_to_bring": "🛒 {n} prodotti aggiunti a Bring!",
+ "added_to_bring_skip": "{n} già presenti",
+ "all_on_bring": "Tutti i prodotti erano già su Bring!",
+ "freq_high": "📈 Uso frequente",
+ "freq_regular": "📊 Uso regolare",
+ "freq_occasional": "📉 Uso occasionale",
+ "out_of_stock": "Esaurito",
+ "scan_toast": "📷 Scansiona: {name}",
+ "empty_category": "Nessun prodotto in questa categoria",
+ "session_empty": "🛒 Nessun prodotto ancora",
+ "urgency_critical": "Urgente",
+ "urgency_high": "Presto",
+ "urgency_medium": "Pianifica",
+ "urgency_low": "Previsione",
+ "urgency_medium_short": "Medio",
+ "urgency_low_short": "Ok",
+ "tag_urgent": "🔴 Urgente",
+ "tag_priority": "⭐ Priorità",
+ "tag_check": "✅ Verificare",
+ "smart_already_predicted": "📊 La spesa intelligente prevede già {name}{urgency}.",
+ "item_removed": "✅ {name} rimosso dalla lista!",
+ "urgency_spec_critical": "⚡ Urgente",
+ "urgency_spec_high": "🟠 Presto",
+ "bring_add_n": "Aggiungi {n} a Bring!",
+ "bring_add_selected": "Aggiungi selezionati a Bring!",
+ "bring_adding": "Aggiunta in corso...",
+ "bring_added_one": "1 prodotto aggiunto a Bring!",
+ "bring_added_many": "{n} prodotti aggiunti a Bring!",
+ "bring_skipped": "({n} già in lista)",
+ "force_sync": "Forza sincronizzazione Bring!",
+ "scan_target_label": "Stai cercando",
+ "scan_target_found": "Trovato! Rimuovi dalla lista",
+ "bring_add_one": "Aggiungi 1 prodotto a Bring!",
+ "bring_add_many": "Aggiungi {n} prodotti a Bring!",
+ "syncing": "Sincronizzazione…",
+ "sync_done": "Sincronizzazione completata",
+ "price_searching": "Cerco...",
+ "search_action": "Ricerca",
+ "open_action": "Apri",
+ "not_found": "Non trovato",
+ "search_price": "Cerca prezzo",
+ "tap_to_scan": "Tocca per scansionare",
+ "tag_title": "Tag",
+ "remove_title": "Rimuovi",
+ "found_count": "{found}/{total} prodotti trovati",
+ "savings_offers": "· 🏷️ Risparmi €{amount} con le offerte",
+ "searching_progress": "Cerco {current}/{total}...",
+ "remove_error": "Errore nella rimozione",
+ "btn_fetch_prices": "Cerca i prezzi",
+ "price_total_label": "💰 Spesa stimata:",
+ "price_loading": "Ricerca prezzi…",
+ "price_not_found": "prezzo n/d",
+ "suggest_loading": "Analisi in corso...",
+ "suggest_error": "Errore nella generazione",
+ "priority_high": "Alta",
+ "priority_medium": "Media",
+ "priority_low": "Bassa",
+ "smart_last_update": "Aggiornato {time}",
+ "names_already_updated": "Tutti i nomi sono già aggiornati",
+ "pantry_hint": "Hai gia {qty} in dispensa"
+ },
+ "ai": {
+ "title": "🤖 Identificazione AI",
+ "capture": "📸 Scatta Foto",
+ "retake": "🔄 Riscatta",
+ "hint": "Scatta una foto del prodotto e l'AI cercherà di identificarlo",
+ "identifying": "🤖 Identifico il prodotto...",
+ "no_api_key": "⚠️ Chiave API Gemini non configurata.\nAggiungi GEMINI_API_KEY nel file .env sul server.",
+ "fields_filled": "✅ Campi compilati dall'AI",
+ "use_data": "✅ Usa dati AI",
+ "use_data_no_barcode": "✅ Usa dati AI (senza barcode)"
+ },
+ "log": {
+ "title": "📒 Storico",
+ "type_added": "Aggiunto",
+ "type_waste": "Buttato",
+ "type_used": "Usato",
+ "type_bring": "Aggiunto a Bring!",
+ "undone_badge": "Annullato",
+ "undo_title": "Annulla questa operazione",
+ "load_error": "Errore nel caricamento log",
+ "empty": "Nessuna operazione registrata.",
+ "undo_action_remove": "rimozione di",
+ "undo_action_restore": "ripristino di",
+ "undo_confirm": "Annullare questa operazione?\n→ {action} {name}",
+ "undo_success": "↩ Operazione annullata per {name}",
+ "already_undone": "Operazione già annullata",
+ "too_old": "Non è possibile annullare operazioni più vecchie di 24 ore",
+ "undo_error": "Errore durante l'annullamento",
+ "recipe_prefix": "Ricetta"
+ },
+ "chat": {
+ "title": "Gemini Chef",
+ "welcome": "Ciao! Sono il tuo assistente cucina",
+ "welcome_desc": "Chiedimi di prepararti un succo, uno spuntino, un piatto veloce... Conosco la tua dispensa, i tuoi elettrodomestici e le tue preferenze!",
+ "suggestion_snack": "🍿 Spuntino veloce",
+ "suggestion_juice": "🥤 Succo/Frullato",
+ "suggestion_light": "🥗 Qualcosa di leggero",
+ "suggestion_expiry": "⏰ Usa le scadenze",
+ "clear": "Nuova conversazione",
+ "placeholder": "Chiedi qualcosa...",
+ "cleared": "Chat cancellata",
+ "suggestion_snack_text": "Cosa posso preparare per uno spuntino veloce?",
+ "suggestion_juice_text": "Fammi un succo o frullato con quello che ho",
+ "suggestion_light_text": "Ho fame ma voglio qualcosa di leggero",
+ "suggestion_expiry_text": "Cosa sta per scadere e come posso usarlo?",
+ "transfer_to_recipes": "Trasferisci a Ricette",
+ "transferring": "Trasferimento in corso...",
+ "transferred": "Aggiunta alle Ricette!",
+ "open_recipe": "Apri la ricetta",
+ "quick_recipe_prompt": "Suggeriscimi una ricetta veloce PER UNA PERSONA usando i prodotti che scadono prima! Ignora i prodotti in freezer, concentrati su frigo e dispensa."
+ },
+ "cooking": {
+ "close": "Chiudi",
+ "tts_btn": "Leggi ad alta voce",
+ "restart": "↺ Ricomincia",
+ "replay": "🔊 Rileggi",
+ "timer": "⏱️ {time} · Timer",
+ "prev": "◀ Precedente",
+ "next": "Successivo ▶",
+ "ingredient_used": "✔️ Scalato",
+ "ingredient_use_btn": "Usa",
+ "ingredient_deduct_title": "Scala dalla dispensa",
+ "timer_expired_tts": "Timer {label} scaduto!",
+ "timer_warning_tts": "Attenzione! {label}: mancano 10 secondi!",
+ "recipe_done_tts": "Ricetta completata! Buon appetito!",
+ "expires_chip": "scade {date}",
+ "finish": "✅ Fine",
+ "step_fallback": "Passo {n}",
+ "zerowaste_label": "♻️ Scarto",
+ "zerowaste_tip_title": "Consiglio anti-spreco"
+ },
+ "settings": {
+ "title": "⚙️ Configurazione",
+ "tab_api": "API Keys",
+ "tab_bring": "Bring!",
+ "tab_recipe": "Ricette",
+ "tab_mealplan": "Piano Settimanale",
+ "tab_appliances": "Elettrodomestici",
+ "tab_spesa": "Spesa Online",
+ "tab_camera": "Fotocamera",
+ "tab_security": "Sicurezza",
+ "tab_tts": "Voce (TTS)",
+ "tab_language": "Lingua",
+ "tab_scale": "Bilancia Smart",
"gemini": {
- "chat_title": "Chat con Gemini",
- "not_configured": "🤖 Gemini non configurato — imposta GEMINI_API_KEY nelle impostazioni"
+ "title": "🤖 Google Gemini AI",
+ "hint": "Chiave API per identificazione prodotti, scadenze e ricette.",
+ "key_label": "API Key Gemini"
+ },
+ "bring": {
+ "title": "🛒 Bring! Shopping List",
+ "hint": "Credenziali per l'integrazione con la lista della spesa Bring!",
+ "email_label": "📧 Email Bring!",
+ "password_label": "🔒 Password Bring!"
+ },
+ "price": {
+ "title": "💰 Stima Prezzi (AI)",
+ "hint": "Mostra il costo stimato di ogni prodotto nella lista della spesa usando l'AI.",
+ "enabled_label": "Attiva stima prezzi",
+ "country_label": "🌍 Paese di riferimento",
+ "currency_label": "💱 Valuta",
+ "update_label": "🔄 Aggiorna prezzi ogni",
+ "update_suffix": "mesi"
+ },
+ "recipe": {
+ "title": "🍳 Preferenze Ricette",
+ "hint": "Configura le opzioni predefinite per la generazione delle ricette.",
+ "persons_label": "👥 Persone predefinite",
+ "options_label": "🎯 Opzioni ricetta predefinite",
+ "fast": "⚡ Pasto Veloce",
+ "light": "🥗 Poca Fame",
+ "expiry": "⏰ Priorità Scadenze",
+ "healthy": "💚 Extra Salutare",
+ "opened": "📦 Priorità Cose Aperte",
+ "zerowaste": "♻️ Zero Sprechi",
+ "dietary_label": "🚫 Intolleranze / Restrizioni",
+ "dietary_placeholder": "Es: senza glutine, senza lattosio, vegetariano..."
+ },
+ "mealplan": {
+ "title": "📅 Piano Pasti Settimanale",
+ "hint": "Imposta la tipologia di pasto per ogni giorno. Sarà usata come guida nella generazione delle ricette.",
+ "enabled": "✅ Attiva piano pasti settimanale",
+ "legend": "🌤️ = Pranzo · 🌙 = Cena · Tocca un badge per cambiarlo.",
+ "types_title": "📋 Tipologie disponibili",
+ "reset_btn": "↺ Ripristina default"
},
"appliances": {
- "empty": "Nessun elettrodomestico aggiunto"
+ "title": "🔌 Elettrodomestici Disponibili",
+ "hint": "Indica gli elettrodomestici che hai a disposizione. Saranno considerati nella generazione delle ricette.",
+ "new_placeholder": "Es: Macchina del pane, Bimby, Friggitrice ad aria...",
+ "quick_title": "Aggiungi velocemente:",
+ "oven": "🔥 Forno",
+ "microwave": "📡 Microonde",
+ "air_fryer": "🍟 Friggitrice ad aria",
+ "bread_maker": "🍞 Macchina pane",
+ "bimby": "🤖 Bimby/Cookeo",
+ "mixer": "🌀 Planetaria",
+ "steamer": "♨️ Vaporiera",
+ "pressure_cooker": "🫕 Pentola pressione",
+ "toaster": "🍞 Tostapane",
+ "blender": "🍹 Frullatore",
+ "empty": "Nessun elettrodomestico aggiunto"
},
- "about": {
- "title": "Informazioni",
- "version": "Versione",
- "report_bug": "Segnala un problema",
- "report_bug_hint": "Qualcosa non funziona? Inviaci una segnalazione direttamente dall'app.",
- "report_bug_modal_title": "Segnala un problema",
- "report_type_bug": "Bug",
- "report_type_feature": "Funzionalità",
- "report_type_question": "Domanda",
- "report_field_title": "Titolo",
- "report_field_title_ph": "Breve descrizione del problema",
- "report_field_desc": "Descrizione",
- "report_field_desc_ph": "Descrivi il problema in dettaglio…",
- "report_field_steps": "Passi per riprodurlo (opzionale)",
- "report_field_steps_ph": "1. Vai su…\n2. Tocca…\n3. Vedi l'errore…",
- "report_auto_info": "Saranno allegati automaticamente: versione {version}, lingua {lang}.",
- "report_send_btn": "Invia segnalazione",
- "report_bug_sending": "Invio in corso…",
- "report_bug_sent": "Segnalazione inviata — grazie!",
- "report_bug_error": "Impossibile inviare la segnalazione. Controlla la connessione.",
- "changelog": "Changelog",
- "github": "Repository GitHub"
+ "spesa": {
+ "title": "🛍️ Spesa Online",
+ "hint": "Configura il provider per la spesa online.",
+ "provider_label": "🏪 Provider",
+ "email_label": "📧 Email",
+ "password_label": "🔒 Password",
+ "login_btn": "🔐 Accedi",
+ "ai_prompt_label": "🤖 Prompt AI selezione prodotto",
+ "ai_prompt_placeholder": "Istruzioni per l'AI quando deve scegliere tra più prodotti...",
+ "ai_prompt_hint": "L'AI usa questo prompt per scegliere il prodotto più appropriato tra i risultati. Lascia vuoto per il comportamento predefinito.",
+ "configure_first": "Configura prima la Spesa Online nelle impostazioni",
+ "missing_credentials": "Inserisci email e password",
+ "login_in_progress": "Accesso in corso...",
+ "login_error_prefix": "Errore:",
+ "login_network_error_prefix": "Errore di rete:",
+ "login_success_default": "Login effettuato!",
+ "result_name_label": "Nome",
+ "result_card_label": "Tessera",
+ "result_pickup_label": "Punto Ritiro",
+ "result_points_label": "Punti Fedeltà",
+ "connected_relogin": "✅ Connesso — Riaccedi",
+ "connected_as": "Connesso come {name}"
},
- "export": {
- "title": "Esporta inventario",
- "hint": "Scarica l inventario corrente in CSV o apri la versione stampabile (PDF).",
- "btn_csv": "Scarica CSV",
- "btn_pdf": "PDF / Stampa",
- "btn_title": "Esporta"
+ "camera": {
+ "title": "📷 Fotocamera",
+ "hint": "Scegli quale fotocamera utilizzare per la scansione barcode e l'identificazione AI.",
+ "device_label": "📸 Fotocamera predefinita",
+ "back": "📱 Posteriore (default)",
+ "front": "🤳 Anteriore",
+ "devices_hint": "Se hai più fotocamere, puoi selezionarne una specifica dall'elenco sopra dopo aver concesso i permessi.",
+ "detect_btn": "🔄 Rileva fotocamere",
+ "ai_fallback_label": "Identificazione visiva AI (fallback 5s)",
+ "ai_fallback_hint": "Se il codice a barre non viene letto entro 5 secondi, un fotogramma viene inviato automaticamente all'AI per identificare il prodotto visivamente. Richiede Gemini configurato."
},
- "startup": {
- "connecting": "Connessione al server...",
- "check_php_memory": "Memoria PHP",
- "check_php_timeout": "Timeout PHP",
- "check_php_upload": "Upload PHP",
- "check_data_dir": "Cartella dati",
- "check_rate_limits": "Dir rate limits",
- "check_backups": "Dir backup",
- "check_write_test": "Test scrittura disco",
- "check_disk_space": "Spazio disco",
- "check_db_legacy": "DB legacy (dispensa.db)",
- "check_db_connect": "Connessione database",
- "check_db_tables": "Tabelle database",
- "check_db_integrity": "Integrità database",
- "check_db_wal": "WAL mode",
- "check_db_size": "Dimensione database",
- "check_db_rows": "Dati inventario",
- "check_env": "File .env",
- "check_gemini": "Chiave Gemini AI",
- "check_bring_creds": "Credenziali Bring!",
- "check_bring_token": "Token Bring!",
- "check_tts": "URL Text-to-Speech",
- "check_scale": "Gateway bilancia",
- "check_curl_ssl": "cURL SSL",
- "check_internet": "Connessione internet",
- "fresh_install": "nuovo impianto",
- "warnings_found": "avvisi rilevati",
- "all_ok": "Sistema OK",
- "critical_error_short": "Errore critico",
- "critical_error": "Errore critico: l'app non può avviarsi. Controlla i log del server.",
- "critical_error_intro": "L'app non può avviarsi a causa dei seguenti problemi:",
- "error_network": "Impossibile contattare il server.",
- "error_network_detail": "Il browser non riesce a raggiungere il server PHP.\n\nPossibili cause:\n• Il server Apache/PHP non è in esecuzione\n• Problema di rete o firewall\n• URL dell'app non corretta\n\nControlla che il server sia avviato e riprova.",
- "retry": "Riprova",
- "syncing_local": "Sincronizzazione dati locali...",
- "sync_done": "Dati locali aggiornati"
+ "security": {
+ "title": "🔒 Certificato HTTPS",
+ "hint": "Se il browser mostra l'errore \"La connessione non è privata\" (ERR_CERT_AUTHORITY_INVALID), devi installare il certificato CA nel dispositivo.",
+ "download_btn": "📥 Scarica Certificato CA",
+ "token_title": "🔑 Token Impostazioni",
+ "token_label": "Token di accesso",
+ "token_hint": "Se `SETTINGS_TOKEN` è configurato nel `.env` server, inserisci qui il token prima di salvare le impostazioni. Lascia vuoto se non configurato.",
+ "token_placeholder": "(vuoto = nessuna protezione)",
+ "token_required_hint": "🔒 Questo server richiede un token per salvare le impostazioni.",
+ "cert_instructions": "Istruzioni per Chrome (Android): 1. Scarica il certificato qui sopra 2. Vai in Impostazioni → Sicurezza e privacy → Altre impostazioni di sicurezza → Installa da archivio dispositivo 3. Seleziona il file EverShelf_CA.crt scaricato 4. Scegli \"CA\" e conferma 5. Riavvia Chrome
Istruzioni per Chrome (PC): 1. Scarica il certificato qui sopra 2. Vai in chrome://settings/certificates (o Impostazioni → Privacy e sicurezza → Sicurezza → Gestisci certificati) 3. Tab \"Autorità\" → Importa → seleziona il file 4. Spunta \"Considera attendibile per identificare siti web\" 5. Riavvia Chrome"
},
- "stats_monthly": {
- "title": "Statistiche Mensili",
- "consumed": "prodotti usati",
- "trend_up": "+{pct}% rispetto a {prev}",
- "trend_down": "-{pct}% rispetto a {prev}",
- "trend_same": "stesso ritmo del mese scorso",
- "added": "aggiunti",
- "wasted": "sprecati",
- "top_used": "più usato",
- "top_cats": "Categorie principali",
- "source": "Storico transazioni · mese corrente"
+ "tts": {
+ "title": "🔊 Voce & TTS",
+ "hint": "Configura la sintesi vocale tramite qualsiasi API REST esterna. I passi della ricetta e i timer scaduti verranno inviati all'endpoint configurato.",
+ "enabled": "✅ Attiva TTS",
+ "engine_label": "⚙️ Motore TTS",
+ "engine_browser": "🔇 Browser (offline, nessuna configurazione)",
+ "engine_server": "🌐 Server esterno (Home Assistant, API REST...)",
+ "voice_label": "🗣️ Voce",
+ "rate_label": "⚡ Velocità",
+ "pitch_label": "🎵 Tono",
+ "url_label": "🌐 URL Endpoint",
+ "method_label": "📡 Metodo HTTP",
+ "auth_label": "🔐 Autenticazione",
+ "auth_bearer": "Bearer Token",
+ "auth_custom": "Header personalizzato",
+ "auth_none": "Nessuna",
+ "token_label": "🔑 Bearer Token",
+ "custom_header_name": "📋 Nome header",
+ "custom_header_value": "📋 Valore header",
+ "content_type_label": "📄 Content-Type",
+ "payload_key_label": "🗝️ Campo testo nel payload",
+ "payload_key_hint": "Nome del campo JSON che conterrà il testo da leggere (es: message, text).",
+ "extra_fields_label": "➕ Campi extra (JSON)",
+ "extra_fields_placeholder": "{\"entity_id\": \"media_player.living_room\"}",
+ "extra_fields_hint": "Campi aggiuntivi da includere nel payload, in formato JSON. Lascia vuoto se non necessario.",
+ "test_sound_btn": "🔔 Esegui Test Suono",
+ "test_btn": "🔊 Invia Test Vocale",
+ "voices_loading": "Caricamento voci…",
+ "voice_not_supported": "Voce non supportata dal browser",
+ "voices_none": "Nessuna voce disponibile su questo dispositivo",
+ "voices_hint": "Le voci disponibili dipendono dal sistema operativo e dal browser. Su macOS/iOS è disponibile la voce Paola (italiano). Premi ↺ se la lista non si carica.",
+ "url_missing": "⚠️ URL endpoint mancante.",
+ "test_sending": "⏳ Invio in corso…",
+ "test_ok": "✅ Risposta {code} — controlla che l'altoparlante abbia parlato.",
+ "heard_question": "Hai sentito la voce?",
+ "heard_yes": "Sì, ho sentito",
+ "heard_no": "No, non ho sentito",
+ "test_ok_kiosk": "TTS funzionante.",
+ "test_fail_steps": "Controlla: 1) volume media del dispositivo non sia 0; 2) Google Text-to-Speech installato e aggiornato; 3) pacchetto vocale italiano scaricato nelle impostazioni TTS Android."
+ },
+ "language": {
+ "title": "🌐 Lingua / Language",
+ "hint": "Seleziona la lingua dell'interfaccia. Select the interface language.",
+ "label": "🌐 Lingua",
+ "restart_notice": "La pagina verrà ricaricata per applicare la nuova lingua."
+ },
+ "screensaver": {
+ "label": "Attiva salvaschermo",
+ "card_title": "🌙 Salvaschermo",
+ "card_hint": "Mostra un orologio con fatti utili dopo 5 minuti di inattività. Di default è disattivato.",
+ "timeout_1": "1 minuto",
+ "timeout_2": "2 minuti",
+ "timeout_5": "5 minuti",
+ "timeout_10": "10 minuti",
+ "timeout_15": "15 minuti",
+ "timeout_30": "30 minuti",
+ "timeout_60": "1 ora",
+ "start_after": "⏱️ Avvia dopo"
+ },
+ "scale": {
+ "title": "⚖️ Bilancia Smart",
+ "hint": "Collega una bilancia Bluetooth tramite il gateway Android per leggere il peso automaticamente.",
+ "tab": "Bilancia Smart",
+ "enabled": "✅ Abilita bilancia smart",
+ "url_label": "🌐 URL Gateway WebSocket",
+ "url_placeholder": "ws://192.168.1.x:8765",
+ "url_hint": "URL mostrato dall'app Android (stessa rete Wi-Fi). Es:",
+ "test_btn": "🔗 Testa connessione",
+ "download_btn": "📥 Scarica Gateway Android (APK)",
+ "download_hint": "App Android che fa da ponte tra la bilancia BLE e questo sito.",
+ "download_sub": "Sorgente: evershelf-scale-gateway/ nella root del progetto",
+ "live_weight": "peso in tempo reale",
+ "auto_reconnect": "🔁 Riconnessione: automatica",
+ "kiosk_title": "📡 Bilancia BLE integrata nel Kiosk",
+ "kiosk_hint": "La bilancia è gestita direttamente dal Gateway BLE interno al kiosk. Per abbinare un nuovo dispositivo usa il wizard di configurazione.",
+ "kiosk_reconfigure": "🔄 Riconfigura bilancia BLE",
+ "ble_protocols": "
🔌 Protocolli BLE supportati:
Bluetooth SIG Weight Scale (0x181D)
Bluetooth SIG Body Composition (0x181B) — peso, grasso, BMI
Xiaomi Mi Body Composition Scale 2
Generico — heuristica automatica su 100+ modelli
"
+ },
+ "kiosk": {
+ "hint": "Trasforma un tablet Android in un pannello EverShelf sempre acceso, con bilancia BLE integrata.",
+ "download_btn": "📥 Scarica EverShelf Kiosk (APK)",
+ "download_sub": "Modalità kiosk full-screen + gateway bilancia integrato. Sorgente: evershelf-kiosk/",
+ "native_title": "Configurazione Kiosk",
+ "native_hint": "URL server, bilancia BLE, salvaschermo e setup wizard.",
+ "native_btn": "Apri configurazione kiosk",
+ "native_tap_hint": "Tocca la rotella in alto a destra",
+ "native_update_hint": "Aggiorna l'app kiosk per usare questa funzione",
+ "update_title": "Aggiornamento Kiosk",
+ "check_updates_btn": "🔍 Cerca aggiornamenti",
+ "needs_update": "⚠️ Il kiosk installato non supporta questa funzione. Aggiorna l'app kiosk per abilitarla."
+ },
+ "saved": "✅ Configurazione salvata!",
+ "saved_local": "✅ Configurazione salvata localmente",
+ "saved_local_error": "⚠️ Salvato localmente, errore server: {error}",
+ "theme": {
+ "title": "🌙 Tema / Aspetto",
+ "hint": "Scegli il tema dell interfaccia.",
+ "label": "🌙 Tema",
+ "off": "☀️ Chiaro",
+ "on": "🌙 Scuro",
+ "auto": "🔄 Automatico (orario)"
+ },
+ "zerowaste": {
+ "card_title": "♻️ Suggerimenti zero-waste",
+ "card_hint": "Durante la cottura, mostra consigli su come riutilizzare gli scarti prodotti in ogni passo (bucce, acqua di cottura, ecc.). Disattivo per impostazione predefinita.",
+ "label": "Mostra suggerimenti durante la cottura"
+ },
+ "backup": {
+ "tab": "Backup",
+ "local_title": "Backup Locale",
+ "local_hint": "Snapshot giornaliero del database. Configura quanti giorni di backup conservare.",
+ "enabled": "Backup automatico quotidiano",
+ "retention_days": "Giorni di retention",
+ "retention_info": "I backup vengono conservati per",
+ "backup_now": "Backup Ora",
+ "backing_up": "Backup in corso…",
+ "backed_up": "Backup completato",
+ "backup_error": "Errore backup",
+ "last_backup": "Ultimo backup",
+ "no_backup_yet": "Nessun backup ancora eseguito",
+ "list_empty": "Nessun backup disponibile",
+ "restore_btn": "Ripristina",
+ "restore_confirm": "Ripristinare il backup",
+ "delete_btn": "Elimina",
+ "delete_confirm": "Eliminare il backup",
+ "gdrive_title": "Google Drive",
+ "gdrive_hint": "Backup automatici su Google Drive via OAuth 2.0. Nessuna libreria esterna richiesta.",
+ "gdrive_enabled": "Abilita backup Google Drive",
+ "gdrive_folder_id": "ID Cartella Drive",
+ "gdrive_folder_id_hint": "Copia l'ID dalla URL della cartella Drive: …/folders/ID",
+ "gdrive_retention_days": "Retention Drive (giorni, 0=tutto)",
+ "gdrive_test": "Testa Connessione",
+ "gdrive_ok": "Connessione riuscita!",
+ "gdrive_error": "Connessione fallita",
+ "gdrive_push_now": "Carica Ora su Drive",
+ "gdrive_pushing": "Upload in corso…",
+ "gdrive_pushed": "Caricato su Drive",
+ "gdrive_wizard_hint": "Opzionale: backup giornaliero automatico su Google Drive via OAuth 2.0.",
+ "gdrive_skip": "Salta — configura dopo in Impostazioni",
+ "gdrive_client_id": "Client ID",
+ "gdrive_client_secret": "Client Secret",
+ "gdrive_redirect_uri_label": "Redirect URI (da aggiungere in Google Cloud Console):",
+ "gdrive_redirect_uri_hint": "Aggiungi http://localhost come URI di reindirizzamento autorizzato in Google Cloud Console. Funziona su qualsiasi server, anche senza dominio pubblico.",
+ "gdrive_oauth_authorize": "Autorizza con Google",
+ "gdrive_oauth_authorized": "Autorizzato",
+ "gdrive_oauth_not_authorized": "Non ancora autorizzato",
+ "gdrive_oauth_window_opened": "Finestra aperta — autorizza e torna qui",
+ "gdrive_oauth_how_to": "Come configurare OAuth 2.0 (passo dopo passo)",
+ "gdrive_oauth_steps": "
Abilita la Google Drive API: API e servizi → Abilita API → Google Drive API
Vai su API e servizi → Credenziali → Crea credenziali → ID client OAuth 2.0
Tipo applicazione: Applicazione web; aggiungi http://localhost come URI di reindirizzamento autorizzato
Copia Client ID e Client Secret nei campi qui sopra e salva
Clicca Autorizza con Google, accedi e concedi l'accesso
Il browser aprirà http://localhost (possibile errore di connessione è normale): copia l'URL dalla barra degli indirizzi e incollalo nel campo che appare qui sotto
",
+ "gdrive_code_title": "Incolla l'URL o il codice di autorizzazione",
+ "gdrive_code_hint": "Dopo aver autorizzato, il browser aprirà http://localhost e potrebbe mostrare un errore. Copia l'URL dalla barra degli indirizzi (es. http://localhost/?code=4%2F0A...) e incollalo qui.",
+ "gdrive_code_submit": "Conferma",
+ "gdrive_code_empty": "Incolla prima l'URL o il codice di autorizzazione"
+ },
+ "info": {
+ "tab": "Info",
+ "ai_title": "Gemini AI — Utilizzo Token",
+ "ai_hint": "Consumo mensile e costo stimato per la chiave API corrente.",
+ "loading": "Caricamento…",
+ "total_tokens": "Token totali",
+ "est_cost": "Costo stimato",
+ "input_tok": "Token input",
+ "output_tok": "Token output",
+ "ai_calls": "Chiamate",
+ "by_action": "Dettaglio per funzione",
+ "by_model": "Dettaglio per modello",
+ "pricing_note": "Prezzi di riferimento Gemini: 2.5-flash $0.15/M in · $0.60/M out — 2.0-flash $0.10/M in · $0.40/M out.",
+ "system_title": "Sistema",
+ "db_size": "Database",
+ "log_size": "Log",
+ "log_level": "Livello log",
+ "ai_overview": "Prospetto utilizzo AI, inventario e stato del sistema",
+ "calls_unit": "call",
+ "inv_title": "Inventario",
+ "inv_active": "Attivi",
+ "inv_products": "Prodotti totali",
+ "inv_expiring": "In scadenza (7gg)",
+ "inv_expired": "Scaduti",
+ "inv_finished": "Finiti",
+ "act_title": "Attività del mese",
+ "act_tx_month": "Movimenti",
+ "act_restock": "Acquisti",
+ "act_use": "Consumi",
+ "act_new_products": "Nuovi prodotti",
+ "act_tx_year": "Movimenti anno",
+ "price_cache": "Cache prezzi",
+ "cache_entries": "prodotti",
+ "last_backup": "Ultimo backup",
+ "bring_days": "token scade tra {n} giorni",
+ "bring_expired": "token scaduto",
+ "year_label": "Anno {year}",
+ "currency_title": "Valuta",
+ "currency_hint": "La valuta usata per tutti i costi e i prezzi nell'app."
+ },
+ "tab_general": "Generali",
+ "shopping": {
+ "tab": "Lista spesa",
+ "title": "Lista della spesa",
+ "hint": "Configura la lista della spesa integrata o collega Bring!.",
+ "enable_label": "Abilita lista della spesa",
+ "mode_label": "Provider",
+ "mode_internal": "Interno (senza Bring!)",
+ "mode_bring": "Bring! (app esterna)",
+ "bring_section_title": "Configurazione Bring!",
+ "ai_section_title": "Assistenza AI",
+ "smart_suggestions_label": "Suggerimenti AI",
+ "forecast_label": "Previsione prodotti in esaurimento",
+ "auto_add_label": "Aggiungi automaticamente quando",
+ "auto_add_suffix": "rimasto in magazzino (0 = solo quando esaurito)"
+ },
+ "ha": {
+ "tab": "Home Assistant",
+ "title": "Home Assistant",
+ "hint": "Collega EverShelf a Home Assistant per automazioni, notifiche push e sensori REST.",
+ "enabled": "Abilita integrazione Home Assistant",
+ "connection_title": "Connessione",
+ "url_label": "URL Home Assistant",
+ "url_placeholder": "http://192.168.1.50:8123",
+ "url_hint": "URL del tuo server Home Assistant (es. http://homeassistant.local:8123).",
+ "token_label": "Long-Lived Access Token",
+ "token_hint": "Genera da Profilo HA → Sicurezza → Token di accesso a lungo termine.",
+ "token_placeholder": "eyJhbGci...",
+ "token_saved": "Token salvato (non mostrato per sicurezza)",
+ "test_btn": "Testa connessione",
+ "test_ok": "Connesso a {version}",
+ "test_fail": "Connessione fallita: {error}",
+ "test_bad_token": "HA raggiungibile ma token non valido",
+ "testing": "Test in corso…",
+ "error_no_url": "Inserisci prima l'URL di Home Assistant.",
+ "tts_title": "TTS su Speaker Smart",
+ "tts_hint": "Leggi i passi delle ricette su un media player di Home Assistant.",
+ "tts_entity_label": "Entity ID media player",
+ "tts_entity_placeholder": "media_player.living_room",
+ "tts_entity_hint": "Entity ID del media player su cui vuoi la voce. Puoi trovarlo in HA: Strumenti per sviluppatori → Stati.",
+ "tts_platform_label": "Piattaforma TTS",
+ "tts_platform_speak": "tts.speak (raccomandato)",
+ "tts_platform_notify": "notify.* (servizio notifiche)",
+ "tts_apply_btn": "Applica preset HA al tab TTS",
+ "tts_apply_hint": "Pre-compila il tab TTS con l'URL e il token di Home Assistant.",
+ "tts_preset_applied": "Preset HA applicato al tab TTS.",
+ "webhook_title": "Automazioni Webhook",
+ "webhook_hint": "Invia dati a Home Assistant quando avvengono eventi nella dispensa. Crea un'automazione in HA con trigger Webhook e copia l'ID generato.",
+ "webhook_id_label": "Webhook ID",
+ "webhook_id_placeholder": "evershelf_webhook_abc123",
+ "webhook_id_hint": "ID del webhook creato in HA. Copia da: HA → Impostazioni → Automazioni → Crea → Trigger Webhook.",
+ "webhook_events_label": "Notifica per questi eventi",
+ "event_expiry": "Prodotti in scadenza (giornaliero)",
+ "event_shopping": "Aggiunta alla lista della spesa",
+ "event_stock": "Aggiornamento scorte",
+ "expiry_days_label": "Anticipo scadenze (giorni)",
+ "expiry_days_hint": "Invia la notifica di scadenza N giorni prima della data di scadenza.",
+ "webhook_help": "In HA: Impostazioni → Automazioni → Crea automazione → Trigger: Webhook → copia l'ID generato qui sopra.",
+ "notify_title": "Notifiche Push",
+ "notify_hint": "Invia notifiche push al tuo telefono tramite il servizio notify di Home Assistant.",
+ "notify_service_label": "Servizio notify",
+ "notify_service_placeholder": "notify.mobile_app_mio_telefono",
+ "notify_service_hint": "Nome del servizio notify HA (es. notify.mobile_app_phone). Lascia vuoto per disabilitare.",
+ "sensor_title": "Sensori REST",
+ "sensor_hint": "Aggiungi a configuration.yaml per creare sensori EverShelf in Home Assistant.",
+ "sensor_copy_btn": "Copia YAML",
+ "sensor_copied": "YAML copiato negli appunti!",
+ "save_btn": "Salva impostazioni HA",
+ "ha_hint": "Se usi Home Assistant, usa il tab Home Assistant per configurare TTS, webhook e sensori."
}
+ },
+ "expiry": {
+ "today": "OGGI",
+ "tomorrow": "Domani",
+ "days": "{days} giorni",
+ "expired_days": "Da {days}g",
+ "expired_yesterday": "Da ieri",
+ "expired_today": "Oggi",
+ "badge_today": "⚠️ Scade oggi!",
+ "badge_tomorrow": "⏰ Domani",
+ "badge_tomorrow_long": "⏰ Scade domani",
+ "badge_days": "⏰ {n} giorni",
+ "badge_expired_ago": "⚠️ Scaduto da {n}g",
+ "badge_expired": "⛔ Scaduto!",
+ "badge_stable": "✅ Stabile",
+ "badge_expiring_short": "⏰ Scade fra {n}gg",
+ "badge_ok_still": "✅ Ancora {n}gg",
+ "badge_expires_red": "🔴 Scade tra {n}g",
+ "badge_expires_yellow": "🟡 Scade tra {n}g",
+ "badge_expired_bare": "⚠️ Scaduto",
+ "badge_expires_warn": "⚠️ Scade tra {n}gg",
+ "badge_days_left": "⏳ ~{n}gg rimasti",
+ "days_approx": "~{n} giorni",
+ "weeks_approx": "~{n} settimane",
+ "months_approx": "~{n} mesi",
+ "years_approx": "~{n} anni",
+ "expired_today_long": "Scaduto oggi",
+ "expired_ago_long": "Scaduto da {n} giorni",
+ "expired_suffix": "— Scaduto!",
+ "expired_suffix_ok": "— Scaduto (ancora ok)",
+ "expired_suffix_warning": "— Scaduto (controlla)",
+ "opened_ago_long": "Aperto da {n} giorni",
+ "opened_today_long": "Aperto oggi",
+ "opened_suffix": "— Aperto da troppo tempo!",
+ "opened_suffix_ok": "— Aperto (ancora ok)",
+ "opened_suffix_warning": "— Aperto (controlla)",
+ "days_compact": "{n}gg",
+ "badge_check_soon": "Controlla presto"
+ },
+ "status": {
+ "ok": "OK",
+ "check": "Controlla",
+ "discard": "Buttare",
+ "tip_freezer_ok": "In freezer: ancora sicuro (~{n}g di margine)",
+ "tip_freezer_check": "In freezer da molto, potrebbe aver perso qualità. Consumare presto",
+ "tip_freezer_danger": "In freezer da troppo tempo, rischio di bruciatura da gelo e degrado",
+ "tip_highRisk_check": "Scaduto da poco, controlla odore e aspetto prima di consumare",
+ "tip_highRisk_danger": "Prodotto deperibile scaduto: da buttare per sicurezza",
+ "tip_medRisk_check1": "Controlla aspetto e odore prima di consumare",
+ "tip_medRisk_check2": "Scaduto da un po', verificare bene prima dell'uso",
+ "tip_medRisk_danger": "Troppo tempo dalla scadenza, meglio buttare",
+ "tip_lowRisk_ok": "Prodotto a lunga conservazione, ancora sicuro da consumare",
+ "tip_lowRisk_check": "Scaduto da oltre un mese, controllare integrità confezione",
+ "tip_lowRisk_danger": "Scaduto da troppo tempo, meglio non rischiare"
+ },
+ "toast": {
+ "product_saved": "Prodotto salvato!",
+ "product_created": "Prodotto creato!",
+ "product_updated": "✅ Prodotto aggiornato!",
+ "product_removed": "Prodotto rimosso",
+ "updated": "Aggiornato!",
+ "quantity_confirmed": "✓ Quantità confermata",
+ "added_to_inventory": "✅ {name} aggiunto!",
+ "removed_from_list": "✅ {name} rimosso dalla lista!",
+ "removed_from_list_short": "Rimosso dalla lista",
+ "added_to_shopping": "🛒 Aggiunto alla lista della spesa!",
+ "removed_from_shopping": "🛒 Rimosso dalla lista della spesa",
+ "finished_to_bring": "🛒 Prodotto finito → aggiunto a Bring!",
+ "thrown_away": "🗑️ {name} buttato!",
+ "thrown_away_partial": "🗑️ Buttato {qty} {unit} di {name}",
+ "finished_all": "📤 {name} terminato!",
+ "vacuum_sealed": "{name} salvato come sottovuoto",
+ "product_finished_confirmed": "✅ Rimosso — riaggiungi quando ne ricompri",
+ "appliance_added": "Elettrodomestico aggiunto",
+ "item_added": "{name} aggiunto"
+ },
+ "antiwaste": {
+ "title": "🌱 Rapporto Anti-Spreco",
+ "grade_label": "Voto",
+ "you": "Tu",
+ "avg_label": "Media",
+ "better": "🎉 Perdi il {diff}% in meno della {country}!",
+ "worse": "⚠️ Perdi più della media {country}. Puoi migliorare!",
+ "on_par": "→ Sei nella media {country}. Prova a fare ancora meglio!",
+ "saved_money": "~{amount}/mese risparmiati",
+ "saved_meals": "~{n} pasti salvati",
+ "saved_co2": "{n} kg CO₂ evitata",
+ "trend_title": "Andamento (ultimi 3 mesi)",
+ "months_ago_2": "-60 gg",
+ "months_ago_1": "-30 gg",
+ "this_month": "Ora",
+ "country_it": "media italiana",
+ "country_de": "media tedesca",
+ "country_en": "media USA",
+ "source": "Fonti: REDUCE, Eurostat, USDA 2021",
+ "live_on": "Dati in tempo reale",
+ "live_off": "Offline",
+ "meals": "pasti",
+ "annual_info": "📅 Tu ~{you} kg/anno · media ~{avg} kg/anno",
+ "badge_rate": "tasso perdita",
+ "badge_saved_money": "risparmio vs media",
+ "badge_wasted": "prod. persi",
+ "badge_better": "in meno vs media"
+ },
+ "error": {
+ "generic": "Errore",
+ "network": "Errore di rete",
+ "no_api_key": "Configura la chiave API nelle impostazioni",
+ "loading": "Errore nel caricamento del prodotto",
+ "not_found": "Prodotto non trovato",
+ "not_found_manual": "Prodotto non trovato. Inseriscilo manualmente.",
+ "search": "Errore nella ricerca. Riprova.",
+ "search_short": "Errore nella ricerca",
+ "save": "Errore nel salvataggio",
+ "connection": "Errore di connessione",
+ "camera": "Impossibile accedere alla fotocamera",
+ "bring_add": "Errore nell'aggiunta a Bring!",
+ "bring_connection": "Errore connessione Bring!",
+ "identification": "Errore nell'identificazione",
+ "ai_quota": "Quota AI esaurita. Riprova tra qualche minuto.",
+ "barcode_empty": "Inserisci un codice a barre",
+ "barcode_format": "Il codice a barre deve contenere solo numeri (4-14 cifre)",
+ "min_chars": "Scrivi almeno 2 caratteri",
+ "not_in_inventory": "Prodotto non nell'inventario",
+ "appliance_exists": "Elettrodomestico già presente",
+ "already_exists": "Già presente",
+ "network_retry": "Errore di connessione. Riprova.",
+ "select_items": "Seleziona almeno un prodotto",
+ "server_offline": "Connessione al server persa",
+ "server_restored": "Connessione al server ripristinata",
+ "server_retry": "Riprova",
+ "unknown": "Errore sconosciuto",
+ "prefix": "Errore",
+ "no_inventory_entry": "Nessuna voce di inventario trovata",
+ "offline_title": "Nessuna connessione",
+ "offline_subtitle": "L'app non riesce a raggiungere il server. Verifica la connessione Wi-Fi.",
+ "offline_checking": "Verifica connessione…",
+ "offline_restored": "Connessione ripristinata!",
+ "offline_continue": "Continua in modalità offline",
+ "offline_reading_cache": "Lettura dalla cache locale",
+ "offline_ops_pending": "{n} operazioni in attesa",
+ "offline_synced": "{n} operazioni sincronizzate",
+ "offline_ai_disabled": "Non disponibile offline",
+ "offline_cache_ready": "Offline — {n} prodotti in cache"
+ },
+ "confirm": {
+ "remove_item": "Vuoi davvero rimuovere questo prodotto dall'inventario?",
+ "kiosk_exit": "Uscire dalla modalità kiosk?",
+ "cancel": "Annulla",
+ "proceed": "Conferma",
+ "discard_one": "Butta 1 pezzo"
+ },
+ "location": {
+ "dispensa": "Dispensa",
+ "frigo": "Frigo",
+ "freezer": "Freezer"
+ },
+ "edit": {
+ "title": "Modifica {name}",
+ "unknown_hint": "Inserisci il nome e le informazioni del prodotto",
+ "label_name": "🏷️ Nome prodotto",
+ "choose_location_title": "Quale modifica?",
+ "choose_location_hint": "Scegli la posizione da modificare:",
+ "confirm_large_qty": "Stai impostando la quantità a {qty} {unit}. Questo sembra un valore insolitamente alto. Confermare?"
+ },
+ "screensaver": {
+ "recipe_btn": "Ricette",
+ "scan_btn": "Scansiona prodotto"
+ },
+ "days": {
+ "mon": "Lunedì",
+ "tue": "Martedì",
+ "wed": "Mercoledì",
+ "thu": "Giovedì",
+ "fri": "Venerdì",
+ "sat": "Sabato",
+ "sun": "Domenica",
+ "mon_short": "Lun",
+ "tue_short": "Mar",
+ "wed_short": "Mer",
+ "thu_short": "Gio",
+ "fri_short": "Ven",
+ "sat_short": "Sab",
+ "sun_short": "Dom"
+ },
+ "meal_types": {
+ "lunch": "Pranzo",
+ "dinner": "Cena",
+ "colazione": "Colazione",
+ "merenda": "Merenda",
+ "dolce": "Dolce",
+ "succo": "Succo di Frutta",
+ "pranzo": "Pranzo",
+ "cena": "Cena"
+ },
+ "scale": {
+ "status_connected": "Bilancia connessa",
+ "status_searching": "Connesso al gateway, attesa bilancia…",
+ "status_disconnected": "Gateway bilancia non raggiungibile",
+ "status_error": "Errore connessione gateway",
+ "not_connected": "Gateway bilancia non connesso",
+ "read_btn": "⚖️ Leggi dalla bilancia",
+ "reading_title": "Lettura bilancia",
+ "place_on_scale": "Metti il prodotto sulla bilancia…",
+ "waiting_stable": "Il peso venire rilevato automaticamente quando la lettura sarà stabile.",
+ "no_url": "Inserisci l'URL del gateway",
+ "testing": "⏳ Test connessione…",
+ "connected_ok": "Connessione gateway riuscita!",
+ "timeout": "Timeout: nessuna risposta dal gateway",
+ "error_connect": "Impossibile connettersi al gateway",
+ "tab": "Bilancia Smart",
+ "low_weight": "Peso < 10 g · inserisci manualmente\n(la lettura automatica richiede almeno 10 g)",
+ "density_hint": "(densità {density} g/ml)",
+ "ml_hint": "(verrà convertito in ml)",
+ "weight_detected": "Peso rilevato — attendi 10s di stabilità…",
+ "weight_too_low": "Peso troppo basso — attendi…",
+ "stable": "✓ Stabile",
+ "auto_confirm": "✅ {val} {unit} — conferma automatica tra 5s (tocca per annullare)",
+ "cancelled_replace": "Annullato — rimetti l'ingrediente sulla bilancia per riprendere"
+ },
+ "prediction": {
+ "expected_qty": "Previsto: {expected} {unit}",
+ "actual_qty": "Attuale: {actual} {unit}",
+ "check_suggestion": "Verifica o pesa la quantità residua"
+ },
+ "date": {
+ "today": "📅 Oggi",
+ "yesterday": "📅 Ieri"
+ },
+ "scanner": {
+ "title_barcode": "🔖 Scansiona Barcode",
+ "barcode_hint": "Inquadra il codice a barre del prodotto",
+ "barcode_manual_placeholder": "O inserisci manualmente...",
+ "barcode_use_btn": "✅ Usa questo codice",
+ "ai_identifying": "🤖 Identifico il prodotto...",
+ "ai_analyzing": "🤖 Analisi AI in corso...",
+ "product_label_hint": "Inquadra l'etichetta del prodotto",
+ "expiry_label_hint": "Inquadra la data di scadenza stampata sul prodotto",
+ "capture_btn": "📸 Scatta",
+ "capture_photo_btn": "📸 Scatta Foto",
+ "retake_btn": "🔄 Riscatta",
+ "camera_error_hint": "Assicurati di usare HTTPS e di aver concesso i permessi della fotocamera. Puoi inserire il barcode manualmente o usare l'identificazione AI.",
+ "no_barcode": "Senza barcode",
+ "save_new_btn": "🆕 Non è nessuno di questi — salva come nuovo",
+ "expiry_found": "Data trovata",
+ "expiry_read_fail": "Non riesco a leggere la data.",
+ "expiry_raw_label": "Letto"
+ },
+ "lowstock": {
+ "title": "⚠️ Sta per finire!",
+ "message": "{name} sta per finire — rimangono solo {qty}.",
+ "question": "Vuoi aggiungerlo alla lista della spesa?",
+ "yes": "🛒 Sì, aggiungi a Bring!",
+ "no": "No, per ora va bene"
+ },
+ "move": {
+ "title": "📦 Spostare il resto?",
+ "question": "Vuoi spostare {thing} di {name} in un'altra posizione?",
+ "question_short": "Vuoi spostare {thing} in un'altra posizione?",
+ "thing_opened": "la confezione aperta",
+ "thing_rest": "il resto",
+ "stay_btn": "No, resta in {location}",
+ "moved_toast": "📦 Confezione aperta spostata in {location}",
+ "vacuum_restore": "Torna sotto vuoto",
+ "vacuum_seal_rest": "Metti sotto vuoto il resto"
+ },
+ "nova": {
+ "1": "Non trasformato",
+ "2": "Ingrediente culinario",
+ "3": "Trasformato",
+ "4": "Ultra-trasformato"
+ },
+ "meal_plan_types": {
+ "pasta": "Pasta",
+ "riso": "Riso",
+ "carne": "Carne",
+ "pesce": "Pesce",
+ "legumi": "Legumi",
+ "uova": "Uova",
+ "formaggio": "Formaggio",
+ "pizza": "Pizza",
+ "affettati": "Affettati",
+ "verdure": "Verdure",
+ "zuppa": "Zuppa",
+ "insalata": "Insalata",
+ "pane": "Pane/Sandwich",
+ "dolce": "Dolce",
+ "libero": "Libero"
+ },
+ "meal_sub": {
+ "dolce_torta": "Torta",
+ "dolce_crema": "Crema / Budino",
+ "dolce_crumble": "Crumble / Crostata",
+ "dolce_biscotti": "Biscotti / Pasticcini",
+ "dolce_frutta": "Dolce alla Frutta",
+ "succo_dolce": "Dolce / Fruttato",
+ "succo_energizzante": "Energizzante",
+ "succo_detox": "Detox / Verde",
+ "succo_rinfrescante": "Rinfrescante",
+ "succo_vitaminico": "Vitaminico / Agrumi"
+ },
+ "meal_plan": {
+ "reset_success": "Piano settimanale ripristinato",
+ "suggested_by": "suggerito dal piano settimanale",
+ "not_available": "non disponibile in dispensa"
+ },
+ "kiosk_session": {
+ "first_item": "Primo prodotto: {name}!",
+ "items_two_four": "{n} prodotti — stai scaldando i motori 🚀",
+ "items_five_nine": "{n} prodotti — ottimo ritmo! 💪",
+ "items_ten_twenty": "{n} prodotti — quasi un recordman 🏆",
+ "items_twenty_plus": "{n} prodotti — spesa epica! 🛒🔥",
+ "duplicates_one": "1 bis (stessa cosa due volte)",
+ "duplicates_many": "{n} bis (roba presa più volte)",
+ "top_category": "Categoria top: {cat} ({count}×)",
+ "items_fallback": "{n} prodott{n} aggiunti"
+ },
+ "nutrition": {
+ "title": "🥗 Analisi Alimentare",
+ "score_excellent": "😄 Ottimo",
+ "score_good": "🙂 Discreto",
+ "score_improve": "😬 Migliorabile",
+ "label_health": "🌿 Salute",
+ "label_variety": "🎨 Varietà",
+ "label_fresh": "❄️ Freschi",
+ "source": "Basato su {n} prodotti in dispensa · EverShelf",
+ "products_count": "prodotti",
+ "today_title": "🥗 La tua dispensa oggi",
+ "products_n": "{n} prodotti",
+ "macros_title": "Macronutrienti stimati",
+ "macros_proteins": "Proteine",
+ "macros_carbs": "Carboidrati",
+ "macros_fat": "Grassi",
+ "macros_fiber": "Fibre",
+ "macros_source": "Stima basata su {n} prodotti in dispensa"
+ },
+ "facts": {
+ "greeting_morning": "Buongiorno",
+ "greeting_afternoon": "Buon pomeriggio",
+ "greeting_evening": "Buonasera",
+ "pantry_waiting": "{greeting}! La tua Dispensa ti aspetta.",
+ "expired_one": "Hai 1 prodotto scaduto in dispensa. Controlla!",
+ "expired_many": "Hai {n} prodotti scaduti in dispensa. Controlla!",
+ "expired_list": "Prodotti scaduti: {names}",
+ "expired_list_more": "e altri {n}",
+ "freezer_expired_ok": "{name} è scaduto, ma essendo in freezer potrebbe essere ancora buono! Controlla.",
+ "freezer_expired_old": "{name} in freezer è scaduto da troppo tempo. Meglio buttarlo.",
+ "fridge_expired_one": "Hai 1 prodotto scaduto in frigo!",
+ "fridge_expired_many": "Hai {n} prodotti scaduti in frigo!",
+ "expiring_today": "{name} scade oggi! Usalo subito.",
+ "expiring_tomorrow": "{name} scade domani. Pensaci!",
+ "expiring_days": "{name} scade tra {days} giorni.",
+ "expiring_many": "Hai {n} prodotti in scadenza ravvicinata.",
+ "expiring_this_week": "Questa settimana scadono {n} prodotti. Pianifica i pasti di conseguenza!",
+ "expiring_item_loc": "{name} ({loc}) scade tra {days} {dayslabel}.",
+ "expiring_this_month": "In questo mese scadranno {n} prodotti.",
+ "shopping_add": "Metti in lista: {names} 🛒",
+ "shopping_more": "e altri {n}",
+ "shopping_empty": "Lista della spesa vuota. Tutto rifornito! ✅",
+ "in_fridge": "In frigo c'è: {name}.",
+ "in_freezer": "Nel freezer c'è: {name}. Non dimenticartelo!",
+ "top_category": "La categoria più presente è {icon} {cat} con {n} prodotti.",
+ "cat_meat": "Hai {n} prodotti di carne. 🥩",
+ "cat_dairy": "Hai {n} latticini in casa. 🥛",
+ "cat_veggies": "Hai {n} tipi di verdura. Ottimo per la salute! 🥬",
+ "cat_fruit": "Hai {n} tipi di frutta. 🍎",
+ "cat_drinks": "Hai {n} bevande disponibili. 🥤",
+ "cat_frozen": "Hai {n} surgelati nel freezer. ❄️",
+ "cat_pasta": "Hai {n} tipi di pasta. 🍝 Che ne dici di una carbonara?",
+ "cat_canned": "Hai {n} conserve in dispensa. 🥫",
+ "cat_snacks": "Hai {n} snack. Resisti alla tentazione! 🍪",
+ "cat_condiments": "Hai {n} condimenti a disposizione. 🧂",
+ "item_random": "Lo sapevi? Hai {name} in {loc}.",
+ "item_qty": "{name}: ne hai {qty}.",
+ "no_expiry_count": "{n} prodotti non hanno una data di scadenza impostata.",
+ "furthest_expiry": "Il prodotto con scadenza più lontana è {name}: {months} mesi.",
+ "high_qty": "Hai una bella scorta di {name}: {qty}!",
+ "low_qty_item": "{name} sta per finire. Aggiungilo alla spesa?",
+ "low_qty_count": "Ci sono {n} prodotti quasi finiti.",
+ "morning_bread": "Buongiorno! Hai del pane per la colazione. 🍞",
+ "morning_milk": "C'è del latte in frigo per il cappuccino? ☕🥛",
+ "morning_fruit": "Buongiorno! Una bella frutta fresca per iniziare bene. 🍎",
+ "noon_pasta": "Ora di pranzo… Un bel piatto di pasta? 🍝",
+ "noon_salad": "Un'insalata fresca per pranzo? Hai {n} verdure! 🥗",
+ "evening_meat": "Per cena potresti usare la carne che hai. 🥩",
+ "evening_fish": "Che ne dici di pesce per cena? 🐟",
+ "evening_expiring": "Hai {n} prodotti in scadenza questa settimana — usali stasera!",
+ "night_reminder": "Buonanotte! Domani ricordati di usare: {names}.",
+ "weekly_balance": "Bilancio settimana: +{in} aggiunti, −{out} consumati.",
+ "weekly_added": "Questa settimana hai aggiunto {n} prodotti.",
+ "weekly_consumed": "Questa settimana hai consumato {n} prodotti. Ottimo!",
+ "tip_freezer": "💡 I prodotti in freezer durano molto più a lungo della data di scadenza.",
+ "tip_bread": "💡 Il pane congelato mantiene la fragranza per settimane.",
+ "tip_fifo": "💡 Per evitare sprechi, usa prima i prodotti con scadenza più vicina (FIFO).",
+ "tip_meat": "💡 La carne in freezer può durare fino a 6 mesi senza problemi.",
+ "tip_no_refreeze": "💡 Non ricongelare mai un alimento già scongelato. Cucinalo subito!",
+ "tip_fridge": "💡 Un frigo ordinato ti fa risparmiare tempo e denaro.",
+ "tip_canned": "💡 Le conserve aperte vanno in frigo e consumate in pochi giorni.",
+ "top_brand": "Il marchio più presente nella tua dispensa è {brand} con {n} prodotti.",
+ "combo_pasta": "Hai pasta e condimenti: sei pronto per un primo piatto! 🍝",
+ "combo_sandwich": "Pane e carne: un panino veloce è sempre una buona idea! 🥪",
+ "combo_balanced": "Verdura e carne: hai tutto per un piatto equilibrato! 🥗🥩",
+ "pantry_empty": "La dispensa è vuota! Fai una bella spesa. 🛒",
+ "pantry_empty_scan": "Nessun prodotto registrato. Scansiona qualcosa per iniziare!",
+ "location_distribution": "Distribuzione: {parts}",
+ "day": "giorno",
+ "days": "giorni"
+ },
+ "kiosk": {
+ "check_btn": "🔍 Cerca aggiornamenti",
+ "checking": "⏳ Controllo…",
+ "error_check": "Errore durante il controllo",
+ "error_start_install": "Errore avvio installazione",
+ "version_installed": "Installata: {v}",
+ "update_available": "⬆️ Nuova versione disponibile: {latest} (installata: {current})",
+ "up_to_date": "✅ Sei già aggiornato — versione {v}",
+ "too_old": "⚠️ Il kiosk installato è troppo vecchio per il controllo automatico. Premi il pulsante qui sotto per scaricare e installare la nuova versione direttamente.",
+ "manual_install": "⚠️ Questo kiosk non supporta l'installazione automatica. Procedura manuale: 1. Esci dal kiosk (tasto ✕ in alto a sinistra) 2. Disinstalla l'app EverShelf Kiosk 3. Scarica e installa la nuova APK da GitHub:",
+ "starting_download": "⏳ Avvio download…",
+ "install_btn": "⬇️ Installa aggiornamento",
+ "exit_title": "Esci dal kiosk",
+ "refresh_title": "Aggiorna pagina"
+ },
+ "update": {
+ "new_version": "Nuova versione",
+ "btn": "Aggiorna"
+ },
+ "gemini": {
+ "chat_title": "Chat con Gemini",
+ "not_configured": "🤖 Gemini non configurato — imposta GEMINI_API_KEY nelle impostazioni"
+ },
+ "appliances": {
+ "empty": "Nessun elettrodomestico aggiunto"
+ },
+ "about": {
+ "title": "Informazioni",
+ "version": "Versione",
+ "report_bug": "Segnala un problema",
+ "report_bug_hint": "Qualcosa non funziona? Inviaci una segnalazione direttamente dall'app.",
+ "report_bug_modal_title": "Segnala un problema",
+ "report_type_bug": "Bug",
+ "report_type_feature": "Funzionalità",
+ "report_type_question": "Domanda",
+ "report_field_title": "Titolo",
+ "report_field_title_ph": "Breve descrizione del problema",
+ "report_field_desc": "Descrizione",
+ "report_field_desc_ph": "Descrivi il problema in dettaglio…",
+ "report_field_steps": "Passi per riprodurlo (opzionale)",
+ "report_field_steps_ph": "1. Vai su…\n2. Tocca…\n3. Vedi l'errore…",
+ "report_auto_info": "Saranno allegati automaticamente: versione {version}, lingua {lang}.",
+ "report_send_btn": "Invia segnalazione",
+ "report_bug_sending": "Invio in corso…",
+ "report_bug_sent": "Segnalazione inviata — grazie!",
+ "report_bug_error": "Impossibile inviare la segnalazione. Controlla la connessione.",
+ "changelog": "Changelog",
+ "github": "Repository GitHub"
+ },
+ "export": {
+ "title": "Esporta inventario",
+ "hint": "Scarica l inventario corrente in CSV o apri la versione stampabile (PDF).",
+ "btn_csv": "Scarica CSV",
+ "btn_pdf": "PDF / Stampa",
+ "btn_title": "Esporta"
+ },
+ "startup": {
+ "connecting": "Connessione al server...",
+ "check_php_memory": "Memoria PHP",
+ "check_php_timeout": "Timeout PHP",
+ "check_php_upload": "Upload PHP",
+ "check_data_dir": "Cartella dati",
+ "check_rate_limits": "Dir rate limits",
+ "check_backups": "Dir backup",
+ "check_write_test": "Test scrittura disco",
+ "check_disk_space": "Spazio disco",
+ "check_db_legacy": "DB legacy (dispensa.db)",
+ "check_db_connect": "Connessione database",
+ "check_db_tables": "Tabelle database",
+ "check_db_integrity": "Integrità database",
+ "check_db_wal": "WAL mode",
+ "check_db_size": "Dimensione database",
+ "check_db_rows": "Dati inventario",
+ "check_env": "File .env",
+ "check_gemini": "Chiave Gemini AI",
+ "check_bring_creds": "Credenziali Bring!",
+ "check_bring_token": "Token Bring!",
+ "check_tts": "URL Text-to-Speech",
+ "check_scale": "Gateway bilancia",
+ "check_curl_ssl": "cURL SSL",
+ "check_internet": "Connessione internet",
+ "fresh_install": "nuovo impianto",
+ "warnings_found": "avvisi rilevati",
+ "all_ok": "Sistema OK",
+ "critical_error_short": "Errore critico",
+ "critical_error": "Errore critico: l'app non può avviarsi. Controlla i log del server.",
+ "critical_error_intro": "L'app non può avviarsi a causa dei seguenti problemi:",
+ "error_network": "Impossibile contattare il server.",
+ "error_network_detail": "Il browser non riesce a raggiungere il server PHP.\n\nPossibili cause:\n• Il server Apache/PHP non è in esecuzione\n• Problema di rete o firewall\n• URL dell'app non corretta\n\nControlla che il server sia avviato e riprova.",
+ "retry": "Riprova",
+ "syncing_local": "Sincronizzazione dati locali...",
+ "sync_done": "Dati locali aggiornati"
+ },
+ "stats_monthly": {
+ "title": "Statistiche Mensili",
+ "consumed": "prodotti usati",
+ "trend_up": "+{pct}% rispetto a {prev}",
+ "trend_down": "-{pct}% rispetto a {prev}",
+ "trend_same": "stesso ritmo del mese scorso",
+ "added": "aggiunti",
+ "wasted": "sprecati",
+ "top_used": "più usato",
+ "top_cats": "Categorie principali",
+ "source": "Storico transazioni · mese corrente"
+ }
}
\ No newline at end of file