From 8a16307b392b2afa4e674499b8b895ddc4f3c179 Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Tue, 28 Apr 2026 06:36:30 +0000 Subject: [PATCH] i18n: translate all hardcoded Italian strings in app.js Added 49 new translation keys to all 3 language files (IT/EN/DE) and wired every hardcoded Italian label/toast/hint in app.js to use the t() translation function. Sections covered: - scale: density_hint, ml_hint, weight_detected, weight_too_low, stable, auto_confirm - dashboard: banner review titles/details, prediction rate/days/ direction texts, finished-zero/expected/check, anomaly phantom/ghost titles and details - action: have_title, add_more_sub, use_qty_sub, throw_btn/sub, edit_sub - add: purchase_type_label, new_btn, existing_btn, remaining_label/hint/full/half - use: throw_title, throw_all, throw_qty_label/hint, throw_partial_btn - shopping: bring_badge, add_urgent_toast, migration_done, added_to_bring, added_to_bring_skip, all_on_bring, removed_sufficient (was a complex plural, now uses key) - toast: product_updated, thrown_away, thrown_away_partial - confirm: kiosk_exit - WEEK_DAYS array now uses t('days.*') keys --- assets/js/app.js | 124 +++++++++++++++++++++---------------------- translations/de.json | 63 +++++++++++++++++++--- translations/en.json | 63 +++++++++++++++++++--- translations/it.json | 63 +++++++++++++++++++--- 4 files changed, 230 insertions(+), 83 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index c363095..e09f7c0 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -321,7 +321,7 @@ function _scaleAutoFillUse(msg) { if (scaleAlreadyMl) { const density = _scaleDensityForProduct(currentProduct); val = Math.round(grams * density); - if (density !== 1.00) hintExtra = ` (densità ${density} g/ml)`; + if (density !== 1.00) hintExtra = ' ' + t('scale.density_hint', { density }); } else { val = Math.round(grams); } @@ -331,7 +331,7 @@ function _scaleAutoFillUse(msg) { } else { const density = _scaleDensityForProduct(currentProduct); val = Math.round(grams / density); - if (density !== 1.00) hintExtra = ` (densità ${density} g/ml)`; + if (density !== 1.00) hintExtra = ' ' + t('scale.density_hint', { density }); } } @@ -407,7 +407,7 @@ function _scaleAutoFillRecipeUse(msg) { if (scaleAlreadyMl) { const density = _scaleDensityForProduct(currentProduct); val = Math.round(grams * density); - if (density !== 1.00) hintExtra = ` (densità ${density} g/ml)`; + if (density !== 1.00) hintExtra = ' ' + t('scale.density_hint', { density }); } else { val = Math.round(grams); } @@ -417,7 +417,7 @@ function _scaleAutoFillRecipeUse(msg) { } else { const density = _scaleDensityForProduct(currentProduct); val = Math.round(grams / density); - if (density !== 1.00) hintExtra = ` (densità ${density} g/ml)`; + if (density !== 1.00) hintExtra = ' ' + t('scale.density_hint', { density }); } } @@ -434,21 +434,21 @@ function _scaleAutoFillRecipeUse(msg) { livVal.textContent = `${msg.value} ${msg.unit || 'kg'}`; } } - if (livStatus) livStatus.textContent = msg.stable ? '✓ Stabile' : '…'; + if (livStatus) livStatus.textContent = msg.stable ? t('scale.stable') : '…'; // Update live hint in modal with the raw scale reading always const hint = document.getElementById('ruse-scale-hint'); if (hint) { hint.textContent = `⚖️ Bilancia: ${msg.value} ${msg.unit || 'kg'}${msg.stable ? ' ✓' : ' …'}`; if (unit === 'ml' && srcUnit !== 'ml') { - hint.textContent += ' (verrà convertito in ml)'; + hint.textContent += ' ' + t('scale.ml_hint'); } hint.style.display = ''; } if (val < 10) { _cancelScaleStabilityWait(); // stop bar only; keep sentinel - if (livLabel) livLabel.textContent = 'Peso troppo basso — attendi…'; + if (livLabel) livLabel.textContent = t('scale.weight_too_low'); return; } @@ -461,7 +461,7 @@ function _scaleAutoFillRecipeUse(msg) { _scaleStabilityVal = val; _scaleUserDismissed = false; _cancelScaleTimersOnly(); - if (livLabel) livLabel.textContent = 'Peso rilevato — attendi 10s di stabilità…'; + if (livLabel) livLabel.textContent = t('scale.weight_detected'); // Hide confirm bar when new value arrives const confirmWrap = document.getElementById('ruse-scale-confirm-wrap'); if (confirmWrap) confirmWrap.style.display = 'none'; @@ -472,7 +472,7 @@ function _scaleAutoFillRecipeUse(msg) { hint.textContent = `⚖️ Peso bilancia: ${val} ${unit}${hintExtra}`; hint.style.display = ''; } - if (livLabel) livLabel.textContent = `✅ ${val} ${unit} — conferma automatica tra 5s (tocca per annullare)`; + if (livLabel) livLabel.textContent = t('scale.auto_confirm', { val, unit }); if (livVal) livVal.style.color = '#22c55e'; const confirmWrap2 = document.getElementById('ruse-scale-confirm-wrap'); if (confirmWrap2) { confirmWrap2.style.display = ''; } @@ -486,11 +486,11 @@ function _scaleAutoFillRecipeUse(msg) { }); } else if (!_scaleUserDismissed && !_scaleStabilityTimer && !_scaleAutoConfirmTimer) { _cancelScaleTimersOnly(); - if (livLabel) livLabel.textContent = 'Peso rilevato — attendi 10s di stabilità…'; + if (livLabel) livLabel.textContent = t('scale.weight_detected'); _startScaleStabilityWait(() => { const inp = document.getElementById('ruse-quantity'); if (inp) inp.value = val; - if (livLabel) livLabel.textContent = `✅ ${val} ${unit} — conferma automatica tra 5s (tocca per annullare)`; + if (livLabel) livLabel.textContent = t('scale.auto_confirm', { val, unit }); if (livVal) livVal.style.color = '#22c55e'; const confirmWrap3 = document.getElementById('ruse-scale-confirm-wrap'); if (confirmWrap3) confirmWrap3.style.display = ''; @@ -1769,7 +1769,7 @@ function _injectKioskOverlay() { exitBtn.style.cssText = btnStyle; exitBtn.addEventListener('click', (e) => { e.stopPropagation(); - if (confirm('Uscire dalla modalità kiosk?')) _kioskBridge.exit(); + if (confirm(t('confirm.kiosk_exit'))) _kioskBridge.exit(); }); // Refresh button @@ -2505,14 +2505,14 @@ function renderBannerItem() { iconEl.textContent = '⚠️'; let titleText, detailText; if (suspDq && !suspQty) { - titleText = `Confezione insolita: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`; - detailText = `Hai impostato una confezione da ${item.default_quantity} ${item.package_unit} — la dimensione sembra molto alta. Controlla se è corretta o modifica.`; + titleText = `${t('dashboard.banner_review_unusual_pkg_title')}: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`; + detailText = t('dashboard.banner_review_unusual_pkg_detail', { qty: item.default_quantity, unit: item.package_unit }); } else if (parseFloat(item.quantity) < t_.min) { - titleText = `Quantità molto bassa: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`; - detailText = `Hai solo ${qtyDisplay} in inventario — sembra poco, potrebbe essere un errore di inserimento. Conferma se è corretto.`; + titleText = `${t('dashboard.banner_review_low_qty_title')}: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`; + detailText = t('dashboard.banner_review_low_qty_detail', { qty: qtyDisplay }); } else { - titleText = `Quantità insolitamente alta: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`; - detailText = `Hai ${qtyDisplay} in inventario — la cifra sembra molto alta. Conferma se è corretto o correggi.`; + titleText = `${t('dashboard.banner_review_high_qty_title')}: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`; + detailText = t('dashboard.banner_review_high_qty_detail', { qty: qtyDisplay }); } titleEl.textContent = titleText; detailEl.textContent = detailText; @@ -2530,19 +2530,19 @@ function renderBannerItem() { const daysSince = parseInt(pred.days_since_restock) || 0; banner.className = 'alert-banner banner-prediction'; iconEl.textContent = '📊'; - titleEl.textContent = `Consumo anomalo: ${pred.name}${pred.brand ? ' (' + pred.brand + ')' : ''}`; + titleEl.textContent = `${t('dashboard.banner_prediction_title')}: ${pred.name}${pred.brand ? ' (' + pred.brand + ')' : ''}`; let rateText = ''; if (dailyRate > 0) { rateText = dailyRate >= 1 - ? `Media ~${Math.round(dailyRate)} ${pred.unit}/giorno` - : `Media ~${Math.round(dailyRate * 7)} ${pred.unit}/settimana`; + ? t('dashboard.banner_prediction_rate_day', { n: Math.round(dailyRate), unit: pred.unit }) + : t('dashboard.banner_prediction_rate_week', { n: Math.round(dailyRate * 7), unit: pred.unit }); } - const timeText = daysSince > 0 ? ` — ${daysSince} giorni fa hai rifornito` : ''; + const timeText = daysSince > 0 ? ` — ${t('dashboard.banner_prediction_days_ago', { n: daysSince })}` : ''; let diffText; if (dir === 'more') { - diffText = `mi aspettavo ${pred.expected_qty} ${pred.unit}${timeText}, ne hai invece ${pred.actual_qty} ${pred.unit}. Hai aggiunto scorte senza registrarle?`; + diffText = t('dashboard.banner_prediction_more', { expected: pred.expected_qty, unit: pred.unit, time: timeText, actual: pred.actual_qty }); } else { - diffText = `mi aspettavo ${pred.expected_qty} ${pred.unit}${timeText}, ne hai solo ${pred.actual_qty} ${pred.unit}. Hai consumato di più del solito?`; + diffText = t('dashboard.banner_prediction_less', { expected: pred.expected_qty, unit: pred.unit, time: timeText, actual: pred.actual_qty }); } detailEl.innerHTML = rateText ? `${rateText}: ${diffText}` : diffText.charAt(0).toUpperCase() + diffText.slice(1); let btns = ``; @@ -2560,8 +2560,8 @@ function renderBannerItem() { ? ` …${escapeHtml(fin.barcode.slice(-3))}` : ''; titleEl.innerHTML = `${escapeHtml(fin.name)}${fin.brand ? ' (' + escapeHtml(fin.brand) + ')' : ''}${barcodeSuffix} — ${escapeHtml(t('dashboard.banner_finished_title'))}`; - const expectedText = fin.expected_qty ? ` Secondo le registrazioni dovresti averne ancora ${fin.expected_qty} ${fin.unit}.` : ''; - detailEl.innerHTML = `L'inventario segna zero, ma i movimenti registrati dicono che non dovrebbe essere finito.${expectedText} Puoi controllare?`; + const expectedText = fin.expected_qty ? ' ' + t('dashboard.banner_finished_expected', { qty: fin.expected_qty, unit: fin.unit }) : ''; + detailEl.innerHTML = t('dashboard.banner_finished_zero') + expectedText + ' ' + t('dashboard.banner_finished_check'); let btns = ``; btns += ``; actionsEl.innerHTML = btns; @@ -2572,11 +2572,11 @@ function renderBannerItem() { banner.className = 'alert-banner banner-anomaly'; iconEl.textContent = '🔍'; if (isPhantom) { - titleEl.textContent = `${an.name} — hai più scorte del previsto`; - detailEl.innerHTML = `L'inventario segna ${an.inv_qty} ${an.unit}, ma in base alle entrate e uscite registrate ne dovresti avere solo ${an.expected_qty} ${an.unit}. Hai aggiunto scorte o corretto la quantità manualmente senza registrarlo?`; + titleEl.textContent = `${an.name} — ${t('dashboard.banner_anomaly_phantom_title')}`; + detailEl.innerHTML = t('dashboard.banner_anomaly_phantom_detail', { inv_qty: an.inv_qty, unit: an.unit, expected_qty: an.expected_qty }); } else { - titleEl.textContent = `${an.name} — hai meno scorte del previsto`; - detailEl.innerHTML = `In base alle operazioni registrate dovresti avere ${an.expected_qty} ${an.unit} di ${an.name}, ma l'inventario mostra solo ${an.inv_qty} ${an.unit}. Hai prelevato senza registrarlo?`; + titleEl.textContent = `${an.name} — ${t('dashboard.banner_anomaly_ghost_title')}`; + detailEl.innerHTML = t('dashboard.banner_anomaly_ghost_detail', { expected_qty: an.expected_qty, unit: an.unit, name: an.name, inv_qty: an.inv_qty }); } let btns = ``; btns += ``; @@ -4521,7 +4521,7 @@ function showProductAction() { statusBar.innerHTML = `
- 📦 Ce l'hai già! + ${t('action.have_title')}
${totalStr} ${totalFrac ? `${totalFrac}` : ''} @@ -4534,19 +4534,19 @@ function showProductAction() { btnsContainer.innerHTML = ` `; // Secondary: catalog edit link below the buttons (one instance only) @@ -4565,7 +4565,7 @@ function showProductAction() { btnsContainer.innerHTML = ` `; // Remove catalog-edit link if left over from a previous product @@ -4715,7 +4715,7 @@ function editActionInventoryItem(inventoryId) {
- +
@@ -4723,7 +4723,7 @@ function editActionInventoryItem(inventoryId) {
- + @@ -4831,7 +4831,7 @@ function showThrowForm() { document.getElementById('modal-content').innerHTML = `
@@ -4849,9 +4849,9 @@ function showThrowForm() {
-
oppure specifica la quantità:
+
${t('use.throw_qty_hint')}
@@ -4863,7 +4863,7 @@ function showThrowForm() {
- +
@@ -4871,7 +4871,7 @@ function showThrowForm() {
`; @@ -4897,7 +4897,7 @@ async function throwAll() { }); showLoading(false); if (result.success) { - showToast(`🗑️ ${currentProduct.name} buttato!`, 'success'); + showToast(t('toast.thrown_away', { name: currentProduct.name }), 'success'); showPage('dashboard'); } else { showToast(result.error || 'Errore', 'error'); @@ -4922,7 +4922,7 @@ async function throwPartial() { }); showLoading(false); if (result.success) { - showToast(`🗑️ Buttato ${qty} ${currentProduct.unit || 'pz'} di ${currentProduct.name}`, 'success'); + showToast(t('toast.thrown_away_partial', { qty, unit: currentProduct.unit || 'pz', name: currentProduct.name }), 'success'); showPage('dashboard'); } else { showToast(result.error || 'Errore', 'error'); @@ -4971,7 +4971,7 @@ async function saveEditedProductInfo() { currentProduct.name = name; currentProduct.brand = brand; if (category) currentProduct.category = category; - showToast('✅ Prodotto aggiornato!', 'success'); + showToast(t('toast.product_updated'), 'success'); // Refresh the action page with updated data showProductAction(); } else { @@ -5074,13 +5074,13 @@ function showAddForm() { window._addBaseExpiryDays = estimatedDays; expirySection.innerHTML = ` - +
@@ -5323,12 +5323,12 @@ function selectPurchaseType(btn, type) {

Inserisci la data di scadenza o scansionala

- -

Quanto è rimasto approssimativamente?

+ +

${t('add.remaining_hint')}

- + - +
@@ -5702,7 +5702,7 @@ async function loadUseInventoryInfo() { qtyInput.value = 1; qtyInput.step = 'any'; qtyInput.min = '0.01'; - document.getElementById('use-partial-hint').textContent = 'Oppure specifica la quantità usata:'; + document.getElementById('use-partial-hint').textContent = t('use.partial_hint'); // Fraction buttons for pz unit const existingFrac = document.getElementById('pz-fraction-btns'); @@ -5970,7 +5970,7 @@ function showLowStockBringPrompt(result, afterCallback) { const alreadyOnBring = _findSimilarItem(shoppingName, shoppingItems) || _findSimilarItem(name, shoppingItems); if (alreadyOnBring) { // Already present (same or similar item). Just inform and continue. - showToast(`🛒 "${escapeHtml(alreadyOnBring.name)}" già nella lista della spesa`, 'info'); + showToast(t('shopping.already_in_list', { name: escapeHtml(alreadyOnBring.name) }), 'info'); if (afterCallback) afterCallback(); return; } @@ -6962,7 +6962,7 @@ async function autoAddCriticalItems() { try { const result = await api('bring_add', {}, 'POST', { items: itemsToAdd, listUUID: shoppingListUUID }); if (result.success && result.added > 0) { - showToast(`🔴 ${result.added} prodott${result.added === 1 ? 'o urgente aggiunto' : 'i urgenti aggiunti'} automaticamente a Bring!`, 'success'); + showToast(t('shopping.add_urgent_toast', { n: result.added }), 'success'); logOperation('bring_auto_add', { added: itemsToAdd.map(i => i.name) }); loadShoppingList(); } @@ -7066,7 +7066,7 @@ async function cleanupObsoleteBringItems() { } if (removed > 0) { - showToast(`🧹 ${removed} prodott${removed === 1 ? 'o con scorte sufficienti rimosso' : 'i con scorte sufficienti rimossi'} dalla lista`, 'info'); + showToast(t('shopping.removed_sufficient', { removed }), 'info'); logOperation('bring_cleanup', { removed: removedNames }); loadShoppingList(); } @@ -7405,7 +7405,7 @@ function renderSmartItem(item) { ${u.icon} ${u.label} ${freqBadge}${predBadge}${expiryBadge} ${item.is_opened ? '📭 Aperto' : ''} - ${item.on_bring ? '🛒 Già su Bring!' : ''} + ${item.on_bring ? `${t('shopping.bring_badge')}` : ''}
@@ -7423,7 +7423,7 @@ async function migrateBringNames(btn) { try { const data = await api('bring_migrate_names', {}, 'POST', {}); if (data.success) { - const msg = `✅ ${data.migrated} aggiornati, ${data.skipped} già ok${data.errors ? `, ${data.errors} errori` : ''}`; + const msg = t('shopping.migration_done', { migrated: data.migrated, skipped: data.skipped }) + (data.errors ? `, ${data.errors} errori` : ''); if (statusEl) statusEl.textContent = msg; if (data.migrated > 0) { showToast(`🔄 ${data.migrated} nomi generalizzati in Bring!`, 'success'); @@ -7474,8 +7474,8 @@ async function addSmartToBring() { showLoading(false); if (result.success) { const msg = result.added > 0 - ? `🛒 ${result.added} prodotti aggiunti a Bring!${result.skipped > 0 ? ` (${result.skipped} già presenti)` : ''}` - : `Tutti i prodotti erano già su Bring!`; + ? t('shopping.added_to_bring', { n: result.added }) + (result.skipped > 0 ? ` (${t('shopping.added_to_bring_skip', { n: result.skipped })})` : '') + : t('shopping.all_on_bring'); showToast(msg, result.added > 0 ? 'success' : 'info'); // Reload to refresh badges loadShoppingList(); @@ -8491,7 +8491,7 @@ const MEAL_PLAN_TYPES = [ const MEAL_PLAN_TYPE_MAP = {}; MEAL_PLAN_TYPES.forEach(t => { MEAL_PLAN_TYPE_MAP[t.id] = t; }); -const WEEK_DAYS = ['Lunedì','Martedì','Mercoledì','Giovedì','Venerdì','Sabato','Domenica']; +const WEEK_DAYS = [t('days.mon'),t('days.tue'),t('days.wed'),t('days.thu'),t('days.fri'),t('days.sat'),t('days.sun')]; const WEEK_DAYS_SHORT = ['Lun','Mar','Mer','Gio','Ven','Sab','Dom']; /** Default weekly plan as requested. */ diff --git a/translations/de.json b/translations/de.json index 7946396..14cc40b 100644 --- a/translations/de.json +++ b/translations/de.json @@ -109,7 +109,25 @@ "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_finished_action_no": "Nein, ich habe noch welches", + "banner_review_unusual_pkg_title": "Ungewöhnliche Packungsgröße", + "banner_review_unusual_pkg_detail": "Du hast eine Packung von {qty} {unit} eingestellt — die Größe scheint sehr groß. Überprüfe ob es korrekt ist.", + "banner_review_low_qty_title": "Sehr geringe Menge", + "banner_review_low_qty_detail": "Du hast nur {qty} im Bestand — das scheint sehr wenig, möglicherweise ein Eingabefehler. Bestätige wenn korrekt.", + "banner_review_high_qty_title": "Ungewöhnlich hohe Menge", + "banner_review_high_qty_detail": "Du hast {qty} im Bestand — die Zahl scheint sehr hoch. Bestätige wenn korrekt oder korrigiere.", + "banner_prediction_rate_day": "Durchschnitt ~{n} {unit}/Tag", + "banner_prediction_rate_week": "Durchschnitt ~{n} {unit}/Woche", + "banner_prediction_days_ago": "Vor {n} Tagen aufgefüllt", + "banner_prediction_more": "Ich erwartete {expected} {unit}{time}, du hast aber {actual} {unit}. Hast du Bestand ohne Buchung hinzugefügt?", + "banner_prediction_less": "Ich erwartete {expected} {unit}{time}, du hast aber nur {actual} {unit}. Hast du mehr als üblich verbraucht?", + "banner_finished_zero": "Bestand zeigt null, aber gespeicherte Buchungen deuten an, dass es nicht leer sein sollte.", + "banner_finished_expected": "Laut Aufzeichnungen solltest du noch {qty} {unit} haben.", + "banner_finished_check": "Kannst du nachschauen?", + "banner_anomaly_phantom_title": "mehr Bestand als erwartet", + "banner_anomaly_phantom_detail": "Bestand zeigt {inv_qty} {unit}, aber laut Buchungen solltest du nur {expected_qty} {unit} haben. Hast du Bestand ohne Buchung hinzugefügt?", + "banner_anomaly_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?" }, "inventory": { "title": "Vorrat", @@ -140,7 +158,13 @@ "add_btn": "📥 HINZUFÜGEN", "add_sub": "in Vorrat/Kühlschrank", "use_btn": "📤 VERWENDEN / VERBRAUCHEN", - "use_sub": "aus Vorrat/Kühlschrank" + "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…" }, "add": { "title": "Zum Vorrat hinzufügen", @@ -150,7 +174,14 @@ "conf_size_placeholder": "z.B. 300", "vacuum_label": "🫙 Vakuumiert", "vacuum_hint": "Ablaufdatum wird automatisch verlängert", - "submit": "✅ Hinzufügen" + "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" }, "use": { "title": "Verwenden / Verbrauchen", @@ -161,7 +192,12 @@ "submit": "📤 Diese Menge verwenden", "available": "📦 Verfügbar:", "not_in_inventory": "⚠️ Produkt nicht im Bestand.", - "expiry_warning": "⚠️ Verwende zuerst die{loc}, die am {date} abläuft — {when}!" + "expiry_warning": "⚠️ Verwende zuerst die{loc}, die am {date} abläuft — {when}!", + "throw_title": "🗑️ Produkt entsorgen", + "throw_all": "🗑️ ALLES entsorgen ({qty})", + "throw_qty_label": "Wie viel wegwerfen?", + "throw_qty_hint": "oder Menge angeben:", + "throw_partial_btn": "🗑️ Diese Menge entsorgen" }, "product": { "title_new": "Neues Produkt", @@ -231,7 +267,13 @@ "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", - "removed_sufficient": "🧹 {removed} Produkt(e) mit ausreichendem Bestand von der Liste entfernt" + "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!" }, "ai": { "title": "🤖 KI-Identifikation", @@ -455,7 +497,8 @@ "already_exists": "Bereits vorhanden" }, "confirm": { - "remove_item": "Möchtest du dieses Produkt wirklich aus dem Bestand entfernen?" + "remove_item": "Möchtest du dieses Produkt wirklich aus dem Bestand entfernen?", + "kiosk_exit": "Kioskmodus verlassen?" }, "edit": { "title": "{name} bearbeiten" @@ -493,7 +536,13 @@ "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)" + "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)" }, "prediction": { "expected_qty": "Erwartet: {expected} {unit}", diff --git a/translations/en.json b/translations/en.json index d0db7ba..f213041 100644 --- a/translations/en.json +++ b/translations/en.json @@ -109,7 +109,25 @@ "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_finished_action_no": "No, I still have some", + "banner_review_unusual_pkg_title": "Unusual package size", + "banner_review_unusual_pkg_detail": "You set a package of {qty} {unit} — the size seems very large. Check if correct or edit.", + "banner_review_low_qty_title": "Very low quantity", + "banner_review_low_qty_detail": "You only have {qty} in stock — seems very little, could be a typo. Confirm if correct.", + "banner_review_high_qty_title": "Unusually high quantity", + "banner_review_high_qty_detail": "You have {qty} in stock — the figure seems very high. Confirm if correct or edit.", + "banner_prediction_rate_day": "Average ~{n} {unit}/day", + "banner_prediction_rate_week": "Average ~{n} {unit}/week", + "banner_prediction_days_ago": "{n} days ago you restocked", + "banner_prediction_more": "I expected {expected} {unit}{time}, but you have {actual} {unit}. Did you add stock without recording it?", + "banner_prediction_less": "I expected {expected} {unit}{time}, but you only have {actual} {unit}. Did you use more than usual?", + "banner_finished_zero": "Inventory shows zero, but recorded movements suggest it shouldn't be empty.", + "banner_finished_expected": "According to records you should still have {qty} {unit}.", + "banner_finished_check": "Can you check?", + "banner_anomaly_phantom_title": "you have more stock than expected", + "banner_anomaly_phantom_detail": "Inventory shows {inv_qty} {unit}, but based on records you should only have {expected_qty} {unit}. Did you add stock without recording it?", + "banner_anomaly_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?" }, "inventory": { "title": "Pantry", @@ -140,7 +158,13 @@ "add_btn": "📥 ADD", "add_sub": "to pantry/fridge", "use_btn": "📤 USE / CONSUME", - "use_sub": "from pantry/fridge" + "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…" }, "add": { "title": "Add to Pantry", @@ -150,7 +174,14 @@ "conf_size_placeholder": "e.g. 300", "vacuum_label": "🫙 Vacuum sealed", "vacuum_hint": "Expiry date will be extended automatically", - "submit": "✅ Add" + "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" }, "use": { "title": "Use / Consume", @@ -161,7 +192,12 @@ "submit": "📤 Use this quantity", "available": "📦 Available:", "not_in_inventory": "⚠️ Product not in inventory.", - "expiry_warning": "⚠️ Use first the one{loc} that expires on {date} — {when}!" + "expiry_warning": "⚠️ Use first the one{loc} that expires on {date} — {when}!", + "throw_title": "🗑️ Discard Product", + "throw_all": "🗑️ Discard ALL ({qty})", + "throw_qty_label": "How much to discard?", + "throw_qty_hint": "or enter a quantity:", + "throw_partial_btn": "🗑️ Discard this quantity" }, "product": { "title_new": "New Product", @@ -231,7 +267,13 @@ "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" + "removed_sufficient": "🧹 {removed} product(s) with sufficient stock removed from the list", + "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!" }, "ai": { "title": "🤖 AI Identification", @@ -455,7 +497,8 @@ "already_exists": "Already exists" }, "confirm": { - "remove_item": "Do you really want to remove this product from inventory?" + "remove_item": "Do you really want to remove this product from inventory?", + "kiosk_exit": "Exit kiosk mode?" }, "edit": { "title": "Edit {name}" @@ -493,7 +536,13 @@ "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)" + "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)" }, "prediction": { "expected_qty": "Expected: {expected} {unit}", diff --git a/translations/it.json b/translations/it.json index 942776b..57a2d78 100644 --- a/translations/it.json +++ b/translations/it.json @@ -109,7 +109,25 @@ "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_finished_action_no": "No, ne ho ancora", + "banner_review_unusual_pkg_title": "Confezione insolita", + "banner_review_unusual_pkg_detail": "Hai impostato una confezione da {qty} {unit} — la dimensione sembra molto alta. Controlla se è corretta o modifica.", + "banner_review_low_qty_title": "Quantità molto bassa", + "banner_review_low_qty_detail": "Hai solo {qty} in inventario — sembra poco, potrebbe essere un errore. Conferma se è corretto.", + "banner_review_high_qty_title": "Quantità insolitamente alta", + "banner_review_high_qty_detail": "Hai {qty} in inventario — la cifra sembra molto alta. Conferma se è corretto o correggi.", + "banner_prediction_rate_day": "Media ~{n} {unit}/giorno", + "banner_prediction_rate_week": "Media ~{n} {unit}/settimana", + "banner_prediction_days_ago": "{n} giorni fa hai rifornito", + "banner_prediction_more": "mi aspettavo {expected} {unit}{time}, ne hai invece {actual} {unit}. Hai aggiunto scorte senza registrarle?", + "banner_prediction_less": "mi aspettavo {expected} {unit}{time}, ne hai solo {actual} {unit}. Hai consumato di più del solito?", + "banner_finished_zero": "L'inventario segna zero, ma i movimenti registrati dicono che non dovrebbe essere finito.", + "banner_finished_expected": "Secondo le registrazioni dovresti averne ancora {qty} {unit}.", + "banner_finished_check": "Puoi controllare?", + "banner_anomaly_phantom_title": "hai più scorte del previsto", + "banner_anomaly_phantom_detail": "L'inventario segna {inv_qty} {unit}, ma in base alle registrazioni ne dovresti avere solo {expected_qty} {unit}. Hai aggiunto scorte senza registrarle?", + "banner_anomaly_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?" }, "inventory": { "title": "Dispensa", @@ -140,7 +158,13 @@ "add_btn": "📥 AGGIUNGI", "add_sub": "in dispensa/frigo", "use_btn": "📤 USA / CONSUMA", - "use_sub": "dalla dispensa/frigo" + "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…" }, "add": { "title": "Aggiungi alla Dispensa", @@ -150,7 +174,14 @@ "conf_size_placeholder": "es. 300", "vacuum_label": "🫙 Sotto vuoto", "vacuum_hint": "La scadenza verrà estesa automaticamente", - "submit": "✅ Aggiungi" + "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à" }, "use": { "title": "Usa / Consuma", @@ -161,7 +192,12 @@ "submit": "📤 Usa questa quantità", "available": "📦 Disponibile:", "not_in_inventory": "⚠️ Prodotto non presente nell'inventario.", - "expiry_warning": "⚠️ Usa prima quella{loc} che scade il {date} — {when}!" + "expiry_warning": "⚠️ Usa prima quella{loc} che scade il {date} — {when}!", + "throw_title": "🗑️ Butta Prodotto", + "throw_all": "🗑️ Butta TUTTO ({qty})", + "throw_qty_label": "Quanto butti?", + "throw_qty_hint": "oppure specifica la quantità:", + "throw_partial_btn": "🗑️ Butta questa quantità" }, "product": { "title_new": "Nuovo Prodotto", @@ -231,7 +267,13 @@ "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" + "removed_sufficient": "🧹 {removed} prodotto/i con scorte sufficienti rimosso/i dalla lista", + "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!" }, "ai": { "title": "🤖 Identificazione AI", @@ -455,7 +497,8 @@ "already_exists": "Già presente" }, "confirm": { - "remove_item": "Vuoi davvero rimuovere questo prodotto dall'inventario?" + "remove_item": "Vuoi davvero rimuovere questo prodotto dall'inventario?", + "kiosk_exit": "Uscire dalla modalità kiosk?" }, "edit": { "title": "Modifica {name}" @@ -493,7 +536,13 @@ "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)" + "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)" }, "prediction": { "expected_qty": "Previsto: {expected} {unit}",