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
This commit is contained in:
+62
-62
@@ -321,7 +321,7 @@ function _scaleAutoFillUse(msg) {
|
|||||||
if (scaleAlreadyMl) {
|
if (scaleAlreadyMl) {
|
||||||
const density = _scaleDensityForProduct(currentProduct);
|
const density = _scaleDensityForProduct(currentProduct);
|
||||||
val = Math.round(grams * density);
|
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 {
|
} else {
|
||||||
val = Math.round(grams);
|
val = Math.round(grams);
|
||||||
}
|
}
|
||||||
@@ -331,7 +331,7 @@ function _scaleAutoFillUse(msg) {
|
|||||||
} else {
|
} else {
|
||||||
const density = _scaleDensityForProduct(currentProduct);
|
const density = _scaleDensityForProduct(currentProduct);
|
||||||
val = Math.round(grams / density);
|
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) {
|
if (scaleAlreadyMl) {
|
||||||
const density = _scaleDensityForProduct(currentProduct);
|
const density = _scaleDensityForProduct(currentProduct);
|
||||||
val = Math.round(grams * density);
|
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 {
|
} else {
|
||||||
val = Math.round(grams);
|
val = Math.round(grams);
|
||||||
}
|
}
|
||||||
@@ -417,7 +417,7 @@ function _scaleAutoFillRecipeUse(msg) {
|
|||||||
} else {
|
} else {
|
||||||
const density = _scaleDensityForProduct(currentProduct);
|
const density = _scaleDensityForProduct(currentProduct);
|
||||||
val = Math.round(grams / density);
|
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'}`;
|
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
|
// Update live hint in modal with the raw scale reading always
|
||||||
const hint = document.getElementById('ruse-scale-hint');
|
const hint = document.getElementById('ruse-scale-hint');
|
||||||
if (hint) {
|
if (hint) {
|
||||||
hint.textContent = `⚖️ Bilancia: ${msg.value} ${msg.unit || 'kg'}${msg.stable ? ' ✓' : ' …'}`;
|
hint.textContent = `⚖️ Bilancia: ${msg.value} ${msg.unit || 'kg'}${msg.stable ? ' ✓' : ' …'}`;
|
||||||
if (unit === 'ml' && srcUnit !== 'ml') {
|
if (unit === 'ml' && srcUnit !== 'ml') {
|
||||||
hint.textContent += ' (verrà convertito in ml)';
|
hint.textContent += ' ' + t('scale.ml_hint');
|
||||||
}
|
}
|
||||||
hint.style.display = '';
|
hint.style.display = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val < 10) {
|
if (val < 10) {
|
||||||
_cancelScaleStabilityWait(); // stop bar only; keep sentinel
|
_cancelScaleStabilityWait(); // stop bar only; keep sentinel
|
||||||
if (livLabel) livLabel.textContent = 'Peso troppo basso — attendi…';
|
if (livLabel) livLabel.textContent = t('scale.weight_too_low');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +461,7 @@ function _scaleAutoFillRecipeUse(msg) {
|
|||||||
_scaleStabilityVal = val;
|
_scaleStabilityVal = val;
|
||||||
_scaleUserDismissed = false;
|
_scaleUserDismissed = false;
|
||||||
_cancelScaleTimersOnly();
|
_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
|
// Hide confirm bar when new value arrives
|
||||||
const confirmWrap = document.getElementById('ruse-scale-confirm-wrap');
|
const confirmWrap = document.getElementById('ruse-scale-confirm-wrap');
|
||||||
if (confirmWrap) confirmWrap.style.display = 'none';
|
if (confirmWrap) confirmWrap.style.display = 'none';
|
||||||
@@ -472,7 +472,7 @@ function _scaleAutoFillRecipeUse(msg) {
|
|||||||
hint.textContent = `⚖️ Peso bilancia: ${val} ${unit}${hintExtra}`;
|
hint.textContent = `⚖️ Peso bilancia: ${val} ${unit}${hintExtra}`;
|
||||||
hint.style.display = '';
|
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';
|
if (livVal) livVal.style.color = '#22c55e';
|
||||||
const confirmWrap2 = document.getElementById('ruse-scale-confirm-wrap');
|
const confirmWrap2 = document.getElementById('ruse-scale-confirm-wrap');
|
||||||
if (confirmWrap2) { confirmWrap2.style.display = ''; }
|
if (confirmWrap2) { confirmWrap2.style.display = ''; }
|
||||||
@@ -486,11 +486,11 @@ function _scaleAutoFillRecipeUse(msg) {
|
|||||||
});
|
});
|
||||||
} else if (!_scaleUserDismissed && !_scaleStabilityTimer && !_scaleAutoConfirmTimer) {
|
} else if (!_scaleUserDismissed && !_scaleStabilityTimer && !_scaleAutoConfirmTimer) {
|
||||||
_cancelScaleTimersOnly();
|
_cancelScaleTimersOnly();
|
||||||
if (livLabel) livLabel.textContent = 'Peso rilevato — attendi 10s di stabilità…';
|
if (livLabel) livLabel.textContent = t('scale.weight_detected');
|
||||||
_startScaleStabilityWait(() => {
|
_startScaleStabilityWait(() => {
|
||||||
const inp = document.getElementById('ruse-quantity');
|
const inp = document.getElementById('ruse-quantity');
|
||||||
if (inp) inp.value = val;
|
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';
|
if (livVal) livVal.style.color = '#22c55e';
|
||||||
const confirmWrap3 = document.getElementById('ruse-scale-confirm-wrap');
|
const confirmWrap3 = document.getElementById('ruse-scale-confirm-wrap');
|
||||||
if (confirmWrap3) confirmWrap3.style.display = '';
|
if (confirmWrap3) confirmWrap3.style.display = '';
|
||||||
@@ -1769,7 +1769,7 @@ function _injectKioskOverlay() {
|
|||||||
exitBtn.style.cssText = btnStyle;
|
exitBtn.style.cssText = btnStyle;
|
||||||
exitBtn.addEventListener('click', (e) => {
|
exitBtn.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (confirm('Uscire dalla modalità kiosk?')) _kioskBridge.exit();
|
if (confirm(t('confirm.kiosk_exit'))) _kioskBridge.exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Refresh button
|
// Refresh button
|
||||||
@@ -2505,14 +2505,14 @@ function renderBannerItem() {
|
|||||||
iconEl.textContent = '⚠️';
|
iconEl.textContent = '⚠️';
|
||||||
let titleText, detailText;
|
let titleText, detailText;
|
||||||
if (suspDq && !suspQty) {
|
if (suspDq && !suspQty) {
|
||||||
titleText = `Confezione insolita: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`;
|
titleText = `${t('dashboard.banner_review_unusual_pkg_title')}: ${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.`;
|
detailText = t('dashboard.banner_review_unusual_pkg_detail', { qty: item.default_quantity, unit: item.package_unit });
|
||||||
} else if (parseFloat(item.quantity) < t_.min) {
|
} else if (parseFloat(item.quantity) < t_.min) {
|
||||||
titleText = `Quantità molto bassa: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`;
|
titleText = `${t('dashboard.banner_review_low_qty_title')}: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`;
|
||||||
detailText = `Hai solo ${qtyDisplay} in inventario — sembra poco, potrebbe essere un errore di inserimento. Conferma se è corretto.`;
|
detailText = t('dashboard.banner_review_low_qty_detail', { qty: qtyDisplay });
|
||||||
} else {
|
} else {
|
||||||
titleText = `Quantità insolitamente alta: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`;
|
titleText = `${t('dashboard.banner_review_high_qty_title')}: ${item.name}${item.brand ? ' (' + item.brand + ')' : ''}`;
|
||||||
detailText = `Hai ${qtyDisplay} in inventario — la cifra sembra molto alta. Conferma se è corretto o correggi.`;
|
detailText = t('dashboard.banner_review_high_qty_detail', { qty: qtyDisplay });
|
||||||
}
|
}
|
||||||
titleEl.textContent = titleText;
|
titleEl.textContent = titleText;
|
||||||
detailEl.textContent = detailText;
|
detailEl.textContent = detailText;
|
||||||
@@ -2530,19 +2530,19 @@ function renderBannerItem() {
|
|||||||
const daysSince = parseInt(pred.days_since_restock) || 0;
|
const daysSince = parseInt(pred.days_since_restock) || 0;
|
||||||
banner.className = 'alert-banner banner-prediction';
|
banner.className = 'alert-banner banner-prediction';
|
||||||
iconEl.textContent = '📊';
|
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 = '';
|
let rateText = '';
|
||||||
if (dailyRate > 0) {
|
if (dailyRate > 0) {
|
||||||
rateText = dailyRate >= 1
|
rateText = dailyRate >= 1
|
||||||
? `Media ~${Math.round(dailyRate)} ${pred.unit}/giorno`
|
? t('dashboard.banner_prediction_rate_day', { n: Math.round(dailyRate), unit: pred.unit })
|
||||||
: `Media ~${Math.round(dailyRate * 7)} ${pred.unit}/settimana`;
|
: 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;
|
let diffText;
|
||||||
if (dir === 'more') {
|
if (dir === 'more') {
|
||||||
diffText = `mi aspettavo <strong>${pred.expected_qty} ${pred.unit}</strong>${timeText}, ne hai invece <strong>${pred.actual_qty} ${pred.unit}</strong>. 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 {
|
} else {
|
||||||
diffText = `mi aspettavo <strong>${pred.expected_qty} ${pred.unit}</strong>${timeText}, ne hai solo <strong>${pred.actual_qty} ${pred.unit}</strong>. 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);
|
detailEl.innerHTML = rateText ? `${rateText}: ${diffText}` : diffText.charAt(0).toUpperCase() + diffText.slice(1);
|
||||||
let btns = `<button class="btn-banner btn-banner-confirm" onclick="confirmBannerPrediction()">${t('dashboard.banner_prediction_action_confirm', { qty: pred.actual_qty, unit: pred.unit })}</button>`;
|
let btns = `<button class="btn-banner btn-banner-confirm" onclick="confirmBannerPrediction()">${t('dashboard.banner_prediction_action_confirm', { qty: pred.actual_qty, unit: pred.unit })}</button>`;
|
||||||
@@ -2560,8 +2560,8 @@ function renderBannerItem() {
|
|||||||
? ` <span style="font-family:monospace;font-size:0.7em;opacity:0.6">…${escapeHtml(fin.barcode.slice(-3))}</span>`
|
? ` <span style="font-family:monospace;font-size:0.7em;opacity:0.6">…${escapeHtml(fin.barcode.slice(-3))}</span>`
|
||||||
: '';
|
: '';
|
||||||
titleEl.innerHTML = `${escapeHtml(fin.name)}${fin.brand ? ' (' + escapeHtml(fin.brand) + ')' : ''}${barcodeSuffix} — ${escapeHtml(t('dashboard.banner_finished_title'))}`;
|
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 <strong>${fin.expected_qty} ${fin.unit}</strong>.` : '';
|
const expectedText = fin.expected_qty ? ' ' + t('dashboard.banner_finished_expected', { qty: fin.expected_qty, unit: fin.unit }) : '';
|
||||||
detailEl.innerHTML = `L'inventario segna zero, ma i movimenti registrati dicono che non dovrebbe essere finito.${expectedText} Puoi controllare?`;
|
detailEl.innerHTML = t('dashboard.banner_finished_zero') + expectedText + ' ' + t('dashboard.banner_finished_check');
|
||||||
let btns = `<button class="btn-banner btn-banner-ok" onclick="confirmBannerFinished()">${t('dashboard.banner_finished_action_yes')}</button>`;
|
let btns = `<button class="btn-banner btn-banner-ok" onclick="confirmBannerFinished()">${t('dashboard.banner_finished_action_yes')}</button>`;
|
||||||
btns += `<button class="btn-banner btn-banner-edit" onclick="notFinishedBannerAction()">${t('dashboard.banner_finished_action_no')}</button>`;
|
btns += `<button class="btn-banner btn-banner-edit" onclick="notFinishedBannerAction()">${t('dashboard.banner_finished_action_no')}</button>`;
|
||||||
actionsEl.innerHTML = btns;
|
actionsEl.innerHTML = btns;
|
||||||
@@ -2572,11 +2572,11 @@ function renderBannerItem() {
|
|||||||
banner.className = 'alert-banner banner-anomaly';
|
banner.className = 'alert-banner banner-anomaly';
|
||||||
iconEl.textContent = '🔍';
|
iconEl.textContent = '🔍';
|
||||||
if (isPhantom) {
|
if (isPhantom) {
|
||||||
titleEl.textContent = `${an.name} — hai più scorte del previsto`;
|
titleEl.textContent = `${an.name} — ${t('dashboard.banner_anomaly_phantom_title')}`;
|
||||||
detailEl.innerHTML = `L'inventario segna <strong>${an.inv_qty} ${an.unit}</strong>, ma in base alle entrate e uscite registrate ne dovresti avere solo <strong>${an.expected_qty} ${an.unit}</strong>. Hai aggiunto scorte o corretto la quantità manualmente senza registrarlo?`;
|
detailEl.innerHTML = t('dashboard.banner_anomaly_phantom_detail', { inv_qty: an.inv_qty, unit: an.unit, expected_qty: an.expected_qty });
|
||||||
} else {
|
} else {
|
||||||
titleEl.textContent = `${an.name} — hai meno scorte del previsto`;
|
titleEl.textContent = `${an.name} — ${t('dashboard.banner_anomaly_ghost_title')}`;
|
||||||
detailEl.innerHTML = `In base alle operazioni registrate dovresti avere <strong>${an.expected_qty} ${an.unit}</strong> di ${an.name}, ma l'inventario mostra solo <strong>${an.inv_qty} ${an.unit}</strong>. Hai prelevato senza registrarlo?`;
|
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 = `<button class="btn-banner btn-banner-edit" onclick="editBannerAnomaly()">${t('dashboard.banner_anomaly_action_edit')}</button>`;
|
let btns = `<button class="btn-banner btn-banner-edit" onclick="editBannerAnomaly()">${t('dashboard.banner_anomaly_action_edit')}</button>`;
|
||||||
btns += `<button class="btn-banner btn-banner-ok" onclick="dismissBannerAnomaly()">${t('dashboard.banner_anomaly_action_dismiss')}</button>`;
|
btns += `<button class="btn-banner btn-banner-ok" onclick="dismissBannerAnomaly()">${t('dashboard.banner_anomaly_action_dismiss')}</button>`;
|
||||||
@@ -4521,7 +4521,7 @@ function showProductAction() {
|
|||||||
|
|
||||||
statusBar.innerHTML = `
|
statusBar.innerHTML = `
|
||||||
<div class="inv-status-header">
|
<div class="inv-status-header">
|
||||||
<span class="inv-status-title">📦 Ce l'hai già!</span>
|
<span class="inv-status-title">${t('action.have_title')}</span>
|
||||||
<div class="inv-status-total-col">
|
<div class="inv-status-total-col">
|
||||||
<span class="inv-status-total">${totalStr}</span>
|
<span class="inv-status-total">${totalStr}</span>
|
||||||
${totalFrac ? `<span class="inv-status-total-frac">${totalFrac}</span>` : ''}
|
${totalFrac ? `<span class="inv-status-total-frac">${totalFrac}</span>` : ''}
|
||||||
@@ -4534,19 +4534,19 @@ function showProductAction() {
|
|||||||
btnsContainer.innerHTML = `
|
btnsContainer.innerHTML = `
|
||||||
<button class="btn btn-huge btn-success" onclick="showAddForm()">
|
<button class="btn btn-huge btn-success" onclick="showAddForm()">
|
||||||
<span class="btn-icon">📥</span>
|
<span class="btn-icon">📥</span>
|
||||||
<span class="btn-text">AGGIUNGI<br><small>altra quantità</small></span>
|
<span class="btn-text">${t('action.add_btn')}<br><small>${t('action.add_more_sub')}</small></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-huge btn-danger" onclick="showUseForm()">
|
<button class="btn btn-huge btn-danger" onclick="showUseForm()">
|
||||||
<span class="btn-icon">📤</span>
|
<span class="btn-icon">📤</span>
|
||||||
<span class="btn-text">USA<br><small>quanto ne hai usato</small></span>
|
<span class="btn-text">${t('action.use_btn')}<br><small>${t('action.use_qty_sub')}</small></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-huge btn-throw" onclick="showThrowForm()">
|
<button class="btn btn-huge btn-throw" onclick="showThrowForm()">
|
||||||
<span class="btn-icon">🗑️</span>
|
<span class="btn-icon">🗑️</span>
|
||||||
<span class="btn-text">BUTTA<br><small>butta il prodotto</small></span>
|
<span class="btn-text">${t('action.throw_btn')}<br><small>${t('action.throw_sub')}</small></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-huge btn-edit" onclick="openInventoryEdit()">
|
<button class="btn btn-huge btn-edit" onclick="openInventoryEdit()">
|
||||||
<span class="btn-icon">✏️</span>
|
<span class="btn-icon">✏️</span>
|
||||||
<span class="btn-text">MODIFICA<br><small>scadenza, luogo…</small></span>
|
<span class="btn-text">${t('product.modify_details')}<br><small>${t('action.edit_sub')}</small></span>
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
// Secondary: catalog edit link below the buttons (one instance only)
|
// Secondary: catalog edit link below the buttons (one instance only)
|
||||||
@@ -4565,7 +4565,7 @@ function showProductAction() {
|
|||||||
btnsContainer.innerHTML = `
|
btnsContainer.innerHTML = `
|
||||||
<button class="btn btn-huge btn-success" onclick="showAddForm()" style="flex:1">
|
<button class="btn btn-huge btn-success" onclick="showAddForm()" style="flex:1">
|
||||||
<span class="btn-icon">📥</span>
|
<span class="btn-icon">📥</span>
|
||||||
<span class="btn-text">AGGIUNGI<br><small>in dispensa/frigo</small></span>
|
<span class="btn-text">${t('action.add_btn')}<br><small>${t('action.add_sub')}</small></span>
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
// Remove catalog-edit link if left over from a previous product
|
// Remove catalog-edit link if left over from a previous product
|
||||||
@@ -4715,7 +4715,7 @@ function editActionInventoryItem(inventoryId) {
|
|||||||
</div>
|
</div>
|
||||||
<form class="form" onsubmit="submitActionEditInventory(event, ${inventoryId}, ${item.product_id})">
|
<form class="form" onsubmit="submitActionEditInventory(event, ${inventoryId}, ${item.product_id})">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>📦 Quantità</label>
|
<label>${t('add.quantity_label')}</label>
|
||||||
<div class="qty-control">
|
<div class="qty-control">
|
||||||
<button type="button" class="qty-btn" onclick="adjustQty('action-edit-qty', -1)">−</button>
|
<button type="button" class="qty-btn" onclick="adjustQty('action-edit-qty', -1)">−</button>
|
||||||
<input type="number" id="action-edit-qty" value="${item.quantity}" min="0" step="any" class="qty-input">
|
<input type="number" id="action-edit-qty" value="${item.quantity}" min="0" step="any" class="qty-input">
|
||||||
@@ -4723,7 +4723,7 @@ function editActionInventoryItem(inventoryId) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>📏 Unità di misura</label>
|
<label>${t('product.unit_label')}</label>
|
||||||
<select id="action-edit-unit" class="form-input" onchange="onActionEditUnitChange()">
|
<select id="action-edit-unit" class="form-input" onchange="onActionEditUnitChange()">
|
||||||
${['pz','g','ml','conf'].map(u => `<option value="${u}" ${(item.unit||'pz') === u ? 'selected' : ''}>${u === 'pz' ? 'pz (pezzi)' : u === 'g' ? 'g (grammi)' : u === 'ml' ? 'ml (millilitri)' : u === 'conf' ? 'conf (confezioni)' : u}</option>`).join('')}
|
${['pz','g','ml','conf'].map(u => `<option value="${u}" ${(item.unit||'pz') === u ? 'selected' : ''}>${u === 'pz' ? 'pz (pezzi)' : u === 'g' ? 'g (grammi)' : u === 'ml' ? 'ml (millilitri)' : u === 'conf' ? 'conf (confezioni)' : u}</option>`).join('')}
|
||||||
</select>
|
</select>
|
||||||
@@ -4831,7 +4831,7 @@ function showThrowForm() {
|
|||||||
|
|
||||||
document.getElementById('modal-content').innerHTML = `
|
document.getElementById('modal-content').innerHTML = `
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3>🗑️ Butta Prodotto</h3>
|
<h3>${t('use.throw_title')}</h3>
|
||||||
<button class="modal-close" onclick="closeModal()">✕</button>
|
<button class="modal-close" onclick="closeModal()">✕</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="product-preview-small" style="margin-bottom:12px">
|
<div class="product-preview-small" style="margin-bottom:12px">
|
||||||
@@ -4849,9 +4849,9 @@ function showThrowForm() {
|
|||||||
</div>
|
</div>
|
||||||
<div style="display:flex;flex-direction:column;gap:10px">
|
<div style="display:flex;flex-direction:column;gap:10px">
|
||||||
<button class="btn btn-large btn-danger full-width" onclick="throwAll()">
|
<button class="btn btn-large btn-danger full-width" onclick="throwAll()">
|
||||||
🗑️ Butta TUTTO (${qtyDisplay})
|
${t('use.throw_all', { qty: qtyDisplay })}
|
||||||
</button>
|
</button>
|
||||||
<div style="text-align:center;color:var(--text-muted);font-size:0.85rem">oppure specifica la quantità:</div>
|
<div style="text-align:center;color:var(--text-muted);font-size:0.85rem">${t('use.throw_qty_hint')}</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>📍 Da dove?</label>
|
<label>📍 Da dove?</label>
|
||||||
<div class="location-selector" id="throw-location-selector">
|
<div class="location-selector" id="throw-location-selector">
|
||||||
@@ -4863,7 +4863,7 @@ function showThrowForm() {
|
|||||||
<input type="hidden" id="throw-location" value="${items[0].location}">
|
<input type="hidden" id="throw-location" value="${items[0].location}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Quanto butti?</label>
|
<label>${t('use.throw_qty_label')}</label>
|
||||||
<div class="qty-control">
|
<div class="qty-control">
|
||||||
<button type="button" class="qty-btn" onclick="adjustQty('throw-quantity', -1)">−</button>
|
<button type="button" class="qty-btn" onclick="adjustQty('throw-quantity', -1)">−</button>
|
||||||
<input type="number" id="throw-quantity" value="1" min="0.1" step="any" class="qty-input">
|
<input type="number" id="throw-quantity" value="1" min="0.1" step="any" class="qty-input">
|
||||||
@@ -4871,7 +4871,7 @@ function showThrowForm() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-large btn-warning full-width" onclick="throwPartial()">
|
<button class="btn btn-large btn-warning full-width" onclick="throwPartial()">
|
||||||
🗑️ Butta questa quantità
|
${t('use.throw_partial_btn')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -4897,7 +4897,7 @@ async function throwAll() {
|
|||||||
});
|
});
|
||||||
showLoading(false);
|
showLoading(false);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
showToast(`🗑️ ${currentProduct.name} buttato!`, 'success');
|
showToast(t('toast.thrown_away', { name: currentProduct.name }), 'success');
|
||||||
showPage('dashboard');
|
showPage('dashboard');
|
||||||
} else {
|
} else {
|
||||||
showToast(result.error || 'Errore', 'error');
|
showToast(result.error || 'Errore', 'error');
|
||||||
@@ -4922,7 +4922,7 @@ async function throwPartial() {
|
|||||||
});
|
});
|
||||||
showLoading(false);
|
showLoading(false);
|
||||||
if (result.success) {
|
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');
|
showPage('dashboard');
|
||||||
} else {
|
} else {
|
||||||
showToast(result.error || 'Errore', 'error');
|
showToast(result.error || 'Errore', 'error');
|
||||||
@@ -4971,7 +4971,7 @@ async function saveEditedProductInfo() {
|
|||||||
currentProduct.name = name;
|
currentProduct.name = name;
|
||||||
currentProduct.brand = brand;
|
currentProduct.brand = brand;
|
||||||
if (category) currentProduct.category = category;
|
if (category) currentProduct.category = category;
|
||||||
showToast('✅ Prodotto aggiornato!', 'success');
|
showToast(t('toast.product_updated'), 'success');
|
||||||
// Refresh the action page with updated data
|
// Refresh the action page with updated data
|
||||||
showProductAction();
|
showProductAction();
|
||||||
} else {
|
} else {
|
||||||
@@ -5074,13 +5074,13 @@ function showAddForm() {
|
|||||||
window._addBaseExpiryDays = estimatedDays;
|
window._addBaseExpiryDays = estimatedDays;
|
||||||
|
|
||||||
expirySection.innerHTML = `
|
expirySection.innerHTML = `
|
||||||
<label>🛒 Questo prodotto è...</label>
|
<label>${t('add.purchase_type_label')}</label>
|
||||||
<div class="purchase-type-selector">
|
<div class="purchase-type-selector">
|
||||||
<button type="button" class="purchase-type-btn active" onclick="selectPurchaseType(this, 'new')">
|
<button type="button" class="purchase-type-btn active" onclick="selectPurchaseType(this, 'new')">
|
||||||
🆕 Appena comprato
|
${t('add.new_btn')}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="purchase-type-btn" onclick="selectPurchaseType(this, 'existing')">
|
<button type="button" class="purchase-type-btn" onclick="selectPurchaseType(this, 'existing')">
|
||||||
📦 Ce l'avevo già
|
${t('add.existing_btn')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="expiry-detail" class="expiry-detail">
|
<div id="expiry-detail" class="expiry-detail">
|
||||||
@@ -5323,12 +5323,12 @@ function selectPurchaseType(btn, type) {
|
|||||||
<p class="form-hint">Inserisci la data di scadenza o scansionala</p>
|
<p class="form-hint">Inserisci la data di scadenza o scansionala</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>📦 Quantità rimasta</label>
|
<label>${t('add.remaining_label')}</label>
|
||||||
<p class="form-hint" style="margin-bottom:6px">Quanto è rimasto approssimativamente?</p>
|
<p class="form-hint" style="margin-bottom:6px">${t('add.remaining_hint')}</p>
|
||||||
<div class="remaining-options">
|
<div class="remaining-options">
|
||||||
<button type="button" class="remaining-btn" onclick="setRemainingPct(1)">🟢 Pieno</button>
|
<button type="button" class="remaining-btn" onclick="setRemainingPct(1)">${t('add.remaining_full')}</button>
|
||||||
<button type="button" class="remaining-btn" onclick="setRemainingPct(0.75)">🟡 ¾</button>
|
<button type="button" class="remaining-btn" onclick="setRemainingPct(0.75)">🟡 ¾</button>
|
||||||
<button type="button" class="remaining-btn" onclick="setRemainingPct(0.5)">🟠 Metà</button>
|
<button type="button" class="remaining-btn" onclick="setRemainingPct(0.5)">${t('add.remaining_half')}</button>
|
||||||
<button type="button" class="remaining-btn" onclick="setRemainingPct(0.25)">🔴 ¼</button>
|
<button type="button" class="remaining-btn" onclick="setRemainingPct(0.25)">🔴 ¼</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -5702,7 +5702,7 @@ async function loadUseInventoryInfo() {
|
|||||||
qtyInput.value = 1;
|
qtyInput.value = 1;
|
||||||
qtyInput.step = 'any';
|
qtyInput.step = 'any';
|
||||||
qtyInput.min = '0.01';
|
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
|
// Fraction buttons for pz unit
|
||||||
const existingFrac = document.getElementById('pz-fraction-btns');
|
const existingFrac = document.getElementById('pz-fraction-btns');
|
||||||
@@ -5970,7 +5970,7 @@ function showLowStockBringPrompt(result, afterCallback) {
|
|||||||
const alreadyOnBring = _findSimilarItem(shoppingName, shoppingItems) || _findSimilarItem(name, shoppingItems);
|
const alreadyOnBring = _findSimilarItem(shoppingName, shoppingItems) || _findSimilarItem(name, shoppingItems);
|
||||||
if (alreadyOnBring) {
|
if (alreadyOnBring) {
|
||||||
// Already present (same or similar item). Just inform and continue.
|
// 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();
|
if (afterCallback) afterCallback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -6962,7 +6962,7 @@ async function autoAddCriticalItems() {
|
|||||||
try {
|
try {
|
||||||
const result = await api('bring_add', {}, 'POST', { items: itemsToAdd, listUUID: shoppingListUUID });
|
const result = await api('bring_add', {}, 'POST', { items: itemsToAdd, listUUID: shoppingListUUID });
|
||||||
if (result.success && result.added > 0) {
|
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) });
|
logOperation('bring_auto_add', { added: itemsToAdd.map(i => i.name) });
|
||||||
loadShoppingList();
|
loadShoppingList();
|
||||||
}
|
}
|
||||||
@@ -7066,7 +7066,7 @@ async function cleanupObsoleteBringItems() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (removed > 0) {
|
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 });
|
logOperation('bring_cleanup', { removed: removedNames });
|
||||||
loadShoppingList();
|
loadShoppingList();
|
||||||
}
|
}
|
||||||
@@ -7405,7 +7405,7 @@ function renderSmartItem(item) {
|
|||||||
<span class="smart-urgency-badge" style="color:${u.color}">${u.icon} ${u.label}</span>
|
<span class="smart-urgency-badge" style="color:${u.color}">${u.icon} ${u.label}</span>
|
||||||
${freqBadge}${predBadge}${expiryBadge}
|
${freqBadge}${predBadge}${expiryBadge}
|
||||||
${item.is_opened ? '<span class="smart-freq-badge freq-low">📭 Aperto</span>' : ''}
|
${item.is_opened ? '<span class="smart-freq-badge freq-low">📭 Aperto</span>' : ''}
|
||||||
${item.on_bring ? '<span class="smart-bring-badge">🛒 Già su Bring!</span>' : ''}
|
${item.on_bring ? `<span class="smart-bring-badge">${t('shopping.bring_badge')}</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="smart-item-stock">
|
<div class="smart-item-stock">
|
||||||
@@ -7423,7 +7423,7 @@ async function migrateBringNames(btn) {
|
|||||||
try {
|
try {
|
||||||
const data = await api('bring_migrate_names', {}, 'POST', {});
|
const data = await api('bring_migrate_names', {}, 'POST', {});
|
||||||
if (data.success) {
|
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 (statusEl) statusEl.textContent = msg;
|
||||||
if (data.migrated > 0) {
|
if (data.migrated > 0) {
|
||||||
showToast(`🔄 ${data.migrated} nomi generalizzati in Bring!`, 'success');
|
showToast(`🔄 ${data.migrated} nomi generalizzati in Bring!`, 'success');
|
||||||
@@ -7474,8 +7474,8 @@ async function addSmartToBring() {
|
|||||||
showLoading(false);
|
showLoading(false);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const msg = result.added > 0
|
const msg = result.added > 0
|
||||||
? `🛒 ${result.added} prodotti aggiunti a Bring!${result.skipped > 0 ? ` (${result.skipped} già presenti)` : ''}`
|
? t('shopping.added_to_bring', { n: result.added }) + (result.skipped > 0 ? ` (${t('shopping.added_to_bring_skip', { n: result.skipped })})` : '')
|
||||||
: `Tutti i prodotti erano già su Bring!`;
|
: t('shopping.all_on_bring');
|
||||||
showToast(msg, result.added > 0 ? 'success' : 'info');
|
showToast(msg, result.added > 0 ? 'success' : 'info');
|
||||||
// Reload to refresh badges
|
// Reload to refresh badges
|
||||||
loadShoppingList();
|
loadShoppingList();
|
||||||
@@ -8491,7 +8491,7 @@ const MEAL_PLAN_TYPES = [
|
|||||||
const MEAL_PLAN_TYPE_MAP = {};
|
const MEAL_PLAN_TYPE_MAP = {};
|
||||||
MEAL_PLAN_TYPES.forEach(t => { MEAL_PLAN_TYPE_MAP[t.id] = t; });
|
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'];
|
const WEEK_DAYS_SHORT = ['Lun','Mar','Mer','Gio','Ven','Sab','Dom'];
|
||||||
|
|
||||||
/** Default weekly plan as requested. */
|
/** Default weekly plan as requested. */
|
||||||
|
|||||||
+56
-7
@@ -109,7 +109,25 @@
|
|||||||
"banner_finished_title": "aufgebraucht?",
|
"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_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_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": {
|
"inventory": {
|
||||||
"title": "Vorrat",
|
"title": "Vorrat",
|
||||||
@@ -140,7 +158,13 @@
|
|||||||
"add_btn": "📥 HINZUFÜGEN",
|
"add_btn": "📥 HINZUFÜGEN",
|
||||||
"add_sub": "in Vorrat/Kühlschrank",
|
"add_sub": "in Vorrat/Kühlschrank",
|
||||||
"use_btn": "📤 VERWENDEN / VERBRAUCHEN",
|
"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": {
|
"add": {
|
||||||
"title": "Zum Vorrat hinzufügen",
|
"title": "Zum Vorrat hinzufügen",
|
||||||
@@ -150,7 +174,14 @@
|
|||||||
"conf_size_placeholder": "z.B. 300",
|
"conf_size_placeholder": "z.B. 300",
|
||||||
"vacuum_label": "🫙 Vakuumiert",
|
"vacuum_label": "🫙 Vakuumiert",
|
||||||
"vacuum_hint": "Ablaufdatum wird automatisch verlängert",
|
"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": {
|
"use": {
|
||||||
"title": "Verwenden / Verbrauchen",
|
"title": "Verwenden / Verbrauchen",
|
||||||
@@ -161,7 +192,12 @@
|
|||||||
"submit": "📤 Diese Menge verwenden",
|
"submit": "📤 Diese Menge verwenden",
|
||||||
"available": "📦 Verfügbar:",
|
"available": "📦 Verfügbar:",
|
||||||
"not_in_inventory": "⚠️ Produkt nicht im Bestand.",
|
"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": {
|
"product": {
|
||||||
"title_new": "Neues Produkt",
|
"title_new": "Neues Produkt",
|
||||||
@@ -231,7 +267,13 @@
|
|||||||
"smart_already": "📊 Intelligenter Einkauf sagt bereits {name} voraus",
|
"smart_already": "📊 Intelligenter Einkauf sagt bereits {name} voraus",
|
||||||
"all_searched": "Alle Produkte wurden bereits gesucht. Nutze 🔄 für einzelne Suchen.",
|
"all_searched": "Alle Produkte wurden bereits gesucht. Nutze 🔄 für einzelne Suchen.",
|
||||||
"search_complete": "Suche abgeschlossen: {count} Produkte",
|
"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": {
|
"ai": {
|
||||||
"title": "🤖 KI-Identifikation",
|
"title": "🤖 KI-Identifikation",
|
||||||
@@ -455,7 +497,8 @@
|
|||||||
"already_exists": "Bereits vorhanden"
|
"already_exists": "Bereits vorhanden"
|
||||||
},
|
},
|
||||||
"confirm": {
|
"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": {
|
"edit": {
|
||||||
"title": "{name} bearbeiten"
|
"title": "{name} bearbeiten"
|
||||||
@@ -493,7 +536,13 @@
|
|||||||
"timeout": "Timeout: keine Antwort vom Gateway",
|
"timeout": "Timeout: keine Antwort vom Gateway",
|
||||||
"error_connect": "Verbindung zum Gateway nicht möglich",
|
"error_connect": "Verbindung zum Gateway nicht möglich",
|
||||||
"tab": "Smart-Waage",
|
"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": {
|
"prediction": {
|
||||||
"expected_qty": "Erwartet: {expected} {unit}",
|
"expected_qty": "Erwartet: {expected} {unit}",
|
||||||
|
|||||||
+56
-7
@@ -109,7 +109,25 @@
|
|||||||
"banner_finished_title": "finished?",
|
"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_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_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": {
|
"inventory": {
|
||||||
"title": "Pantry",
|
"title": "Pantry",
|
||||||
@@ -140,7 +158,13 @@
|
|||||||
"add_btn": "📥 ADD",
|
"add_btn": "📥 ADD",
|
||||||
"add_sub": "to pantry/fridge",
|
"add_sub": "to pantry/fridge",
|
||||||
"use_btn": "📤 USE / CONSUME",
|
"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": {
|
"add": {
|
||||||
"title": "Add to Pantry",
|
"title": "Add to Pantry",
|
||||||
@@ -150,7 +174,14 @@
|
|||||||
"conf_size_placeholder": "e.g. 300",
|
"conf_size_placeholder": "e.g. 300",
|
||||||
"vacuum_label": "🫙 Vacuum sealed",
|
"vacuum_label": "🫙 Vacuum sealed",
|
||||||
"vacuum_hint": "Expiry date will be extended automatically",
|
"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": {
|
"use": {
|
||||||
"title": "Use / Consume",
|
"title": "Use / Consume",
|
||||||
@@ -161,7 +192,12 @@
|
|||||||
"submit": "📤 Use this quantity",
|
"submit": "📤 Use this quantity",
|
||||||
"available": "📦 Available:",
|
"available": "📦 Available:",
|
||||||
"not_in_inventory": "⚠️ Product not in inventory.",
|
"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": {
|
"product": {
|
||||||
"title_new": "New Product",
|
"title_new": "New Product",
|
||||||
@@ -231,7 +267,13 @@
|
|||||||
"smart_already": "📊 Smart shopping already predicts {name}",
|
"smart_already": "📊 Smart shopping already predicts {name}",
|
||||||
"all_searched": "All products have already been searched. Use 🔄 to search individual ones.",
|
"all_searched": "All products have already been searched. Use 🔄 to search individual ones.",
|
||||||
"search_complete": "Search complete: {count} products",
|
"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": {
|
"ai": {
|
||||||
"title": "🤖 AI Identification",
|
"title": "🤖 AI Identification",
|
||||||
@@ -455,7 +497,8 @@
|
|||||||
"already_exists": "Already exists"
|
"already_exists": "Already exists"
|
||||||
},
|
},
|
||||||
"confirm": {
|
"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": {
|
"edit": {
|
||||||
"title": "Edit {name}"
|
"title": "Edit {name}"
|
||||||
@@ -493,7 +536,13 @@
|
|||||||
"timeout": "Timeout: no response from gateway",
|
"timeout": "Timeout: no response from gateway",
|
||||||
"error_connect": "Cannot connect to gateway",
|
"error_connect": "Cannot connect to gateway",
|
||||||
"tab": "Smart Scale",
|
"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": {
|
"prediction": {
|
||||||
"expected_qty": "Expected: {expected} {unit}",
|
"expected_qty": "Expected: {expected} {unit}",
|
||||||
|
|||||||
+56
-7
@@ -109,7 +109,25 @@
|
|||||||
"banner_finished_title": "è finito?",
|
"banner_finished_title": "è finito?",
|
||||||
"banner_finished_detail": "Ho registrato che {name} ha toccato quota zero. È davvero finito o hai ancora delle scorte?",
|
"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_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": {
|
"inventory": {
|
||||||
"title": "Dispensa",
|
"title": "Dispensa",
|
||||||
@@ -140,7 +158,13 @@
|
|||||||
"add_btn": "📥 AGGIUNGI",
|
"add_btn": "📥 AGGIUNGI",
|
||||||
"add_sub": "in dispensa/frigo",
|
"add_sub": "in dispensa/frigo",
|
||||||
"use_btn": "📤 USA / CONSUMA",
|
"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": {
|
"add": {
|
||||||
"title": "Aggiungi alla Dispensa",
|
"title": "Aggiungi alla Dispensa",
|
||||||
@@ -150,7 +174,14 @@
|
|||||||
"conf_size_placeholder": "es. 300",
|
"conf_size_placeholder": "es. 300",
|
||||||
"vacuum_label": "🫙 Sotto vuoto",
|
"vacuum_label": "🫙 Sotto vuoto",
|
||||||
"vacuum_hint": "La scadenza verrà estesa automaticamente",
|
"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": {
|
"use": {
|
||||||
"title": "Usa / Consuma",
|
"title": "Usa / Consuma",
|
||||||
@@ -161,7 +192,12 @@
|
|||||||
"submit": "📤 Usa questa quantità",
|
"submit": "📤 Usa questa quantità",
|
||||||
"available": "📦 Disponibile:",
|
"available": "📦 Disponibile:",
|
||||||
"not_in_inventory": "⚠️ Prodotto non presente nell'inventario.",
|
"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": {
|
"product": {
|
||||||
"title_new": "Nuovo Prodotto",
|
"title_new": "Nuovo Prodotto",
|
||||||
@@ -231,7 +267,13 @@
|
|||||||
"smart_already": "📊 La spesa intelligente prevede già {name}",
|
"smart_already": "📊 La spesa intelligente prevede già {name}",
|
||||||
"all_searched": "Tutti i prodotti sono già stati cercati. Usa 🔄 per ricercare singoli.",
|
"all_searched": "Tutti i prodotti sono già stati cercati. Usa 🔄 per ricercare singoli.",
|
||||||
"search_complete": "Ricerca completata: {count} prodotti",
|
"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": {
|
"ai": {
|
||||||
"title": "🤖 Identificazione AI",
|
"title": "🤖 Identificazione AI",
|
||||||
@@ -455,7 +497,8 @@
|
|||||||
"already_exists": "Già presente"
|
"already_exists": "Già presente"
|
||||||
},
|
},
|
||||||
"confirm": {
|
"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": {
|
"edit": {
|
||||||
"title": "Modifica {name}"
|
"title": "Modifica {name}"
|
||||||
@@ -493,7 +536,13 @@
|
|||||||
"timeout": "Timeout: nessuna risposta dal gateway",
|
"timeout": "Timeout: nessuna risposta dal gateway",
|
||||||
"error_connect": "Impossibile connettersi al gateway",
|
"error_connect": "Impossibile connettersi al gateway",
|
||||||
"tab": "Bilancia Smart",
|
"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": {
|
"prediction": {
|
||||||
"expected_qty": "Previsto: {expected} {unit}",
|
"expected_qty": "Previsto: {expected} {unit}",
|
||||||
|
|||||||
Reference in New Issue
Block a user