Files
Cibello-app/apps/mobile/src/locales/es/common.json
T
Sven (AAMOS AI) cb493010d0 3c: partial consumption + undo for cooking sessions
- Append-only undo via correction transactions tied to cookingSessionId.
- POST /v1/cooking-sessions/:id/undo with 24h UTC window (409 + i18n after expiry).
- Complete undo scope: meals removed, meal boxes discarded, recipe_cooks
deleted, cookCount decremented; activation milestones left intact.
- Deterministic assumption-profile rollback via lastSessionAnswers.sessionId;
profile deleted when observationCount reaches 0.
- New status 'undone' as TEXT validated against COOKING_SESSION_STATUSES.
- Analytics event cooking_session_undone + domain events MEAL_BOX_DISCARDED,
MEAL_REMOVED, COOKING_SESSION_UNDONE.
- Partial consumption uses actualPortionsEaten/plannedPortions factor.
- i18n coverage for cooked.undoWindowExpired across all 12 locales + server.
- Invariant tests: computeBalance(transactions) === item.quantity after
complete, undo, and undo + new complete; transaction count increases on undo.
- Migration 0014_expand_event_types.sql adds new enum values.
- Updated FAS3-COOKING-SESSIONS-AUDIT.md with chosen semantics.
2026-08-07 05:47:54 +07:00

389 lines
21 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"auth.backToLogin": "Volver a iniciar sesión",
"auth.displayName": "¿Cómo te llamamos?",
"auth.email": "Correo electrónico",
"auth.forgotBody": "Introduce tu correo y te enviaremos un enlace si la cuenta existe.",
"auth.forgotLink": "¿Olvidaste tu contraseña?",
"auth.forgotSentBody": "Si la dirección tiene una cuenta, hemos enviado un enlace válido durante 30 minutos. Ábrelo en este dispositivo.",
"auth.forgotSentTitle": "Revisa tu bandeja de entrada",
"auth.forgotSubmit": "Enviar enlace",
"auth.forgotTitle": "Restablecer contraseña",
"auth.hasAccount": "¿Ya tienes cuenta? Inicia sesión",
"auth.login": "Iniciar sesión",
"auth.newPassword": "Nueva contraseña (mínimo 10 caracteres)",
"auth.noAccount": "¿Nuevo aquí? Crea una cuenta",
"auth.password": "Contraseña",
"auth.register": "Crear cuenta",
"auth.resetBody": "El enlace es válido durante 30 minutos y solo se puede usar una vez. Todos los dispositivos se desconectarán.",
"auth.resetDoneBody": "Tu contraseña se ha cambiado. Inicia sesión con la nueva.",
"auth.resetDoneTitle": "¡Listo!",
"auth.resetSubmit": "Cambiar contraseña",
"auth.resetTitle": "Elige una nueva contraseña",
"auth.resetTokenPlaceholder": "Pega el código del correo",
"auth.trialNote": "7 días de acceso completo sin tarjeta.",
"auth.verifyDoneBody": "¡Gracias! Tu cuenta está verificada.",
"auth.verifyDoneTitle": "Correo confirmado",
"auth.verifyFailedBody": "El enlace no es válido o ha caducado. Solicita uno nuevo desde tu perfil.",
"auth.verifyFailedTitle": "El enlace no funcionó",
"barcode.addPrompt": "¿Añadir al inventario?",
"barcode.aim": "Apunta la cámara al código de barras",
"barcode.allowCamera": "Permitir cámara",
"barcode.cameraBody": "{brand} necesita la cámara para escanear códigos de barras.",
"barcode.cameraTitle": "Se necesita la cámara",
"barcode.kcalPer100": "{kcal} kcal/100 g",
"barcode.needHousehold": "Primero necesitas un hogar.",
"barcode.noLocation": "No se encontró ningún lugar de almacenamiento.",
"barcode.noNutrition": "Sin datos nutricionales",
"barcode.photoPackage": "Fotografiar el envase",
"barcode.unknownBody": "Este producto aún no está en la base de datos. Fotografía el frontal y la etiqueta nutricional y lo añadiremos.",
"barcode.unknownTitle": "Producto desconocido",
"common.add": "Añadir",
"common.back": "Atrás",
"common.cancel": "Cancelar",
"common.done": "Listo",
"common.error": "Algo salió mal. Inténtalo de nuevo.",
"common.estimate": "Estimación",
"common.loading": "Cargando …",
"common.next": "Siguiente",
"common.off": "Desactivado",
"common.offline": "Sin conexión mostrando los últimos datos",
"common.on": "Activado",
"common.oops": "Vaya",
"common.remove": "Quitar",
"common.retry": "Reintentar",
"common.save": "Guardar",
"common.skip": "Omitir",
"common.undo": "Deshacer",
"cooked.deductPantry": "Descontar ingredientes del inventario",
"cooked.deductPantryNote": "Los ingredientes se descuentan del inventario: primero los que caducan antes.",
"cooked.mealBoxes": "Raciones para táperes",
"cooked.portionsCooked": "Raciones cocinadas",
"cooked.rate": "Valorar",
"cooked.title": "¿Qué tal ha ido?",
"cooked.actualPortionsEaten": "¿Cuántas raciones comisteis?",
"cooked.actualPortionsEatenHint": "El resto se convierte en sobras o tuppers.",
"cooked.leftoverEstimatePortions": "¿Cuántas raciones de sobras?",
"cooked.leftoverEstimateHint": "Se guarda como tupper si quieres.",
"cooked.whoAte": "¿Quiénes comieron?",
"cooking.finish": "Hecho registrar la comida",
"cooking.keepAwake": "La pantalla permanece encendida mientras cocinas",
"cooking.scale": "Raciones",
"cooking.step": "Paso {current} de {total}",
"cooking.timer": "Iniciar temporizador",
"cooking.timerDone": "¡Listo!",
"cooking.timerRunning": "Quedan {time}",
"home.budget": "Presupuesto",
"home.emptyInventory": "El inventario está vacío. Escanea la nevera o añade productos manualmente.",
"home.expired": "Caducado",
"home.expiresIn_one": "Queda 1 día",
"home.expiresIn_other": "Quedan {days} días",
"home.expiresToday": "Caduca hoy",
"home.household": "Hogar",
"home.inventory": "Inventario",
"home.mealBoxes": "Táperes",
"home.moreItems_one": "+ 1 más …",
"home.moreItems_other": "+ {count} más …",
"home.pastBestBefore": "Pasada la fecha preferente: huele y prueba primero",
"home.shopping": "Lista de la compra",
"home.thisWeek": "Esta semana",
"home.title": "En casa",
"home.useSoon": "Usar pronto",
"home.useSoonHint": "La fecha de consumo preferente es de calidad: huele, mira y prueba antes de tirar nada. La fecha de caducidad, en cambio, debe respetarse.",
"home.waste": "Desperdicio",
"home.wasteWeek": "Desperdicio esta semana",
"household.empty": "Aún no tienes un hogar. Crea uno en la configuración inicial o únete con un código.",
"household.invite": "Invita con el código: {code}",
"household.locations": "Lugares de almacenamiento",
"household.members": "Miembros",
"household.portionFactor": "×{factor} ración",
"household.private": "Privado por persona: objetivos, alergias, datos de salud e historial de comidas.",
"household.role.adult": "Adulto",
"household.role.child": "Niño",
"household.role.member": "Miembro",
"household.role.owner": "Propietario",
"household.shareCode": "Comparte el código con tu familia para compartir inventario, lista y plan.",
"household.shared": "Compartido en el hogar: inventario, lista de la compra, plan semanal, táperes y presupuesto.",
"logmeal.manualTitle": "Manual",
"logmeal.mealTypeTitle": "Tipo de comida",
"logmeal.photoTip": "Consejo: fotografía el plato en Escanear y la app lo estimará por ti; tú siempre confirmas.",
"logmeal.proteinPlaceholder": "proteína (g)",
"logmeal.quickSubtitle": "Registra los mismos valores nutricionales que la última vez.",
"logmeal.quickTitle": "Registro rápido",
"logmeal.validationBody": "Para el registro manual se necesitan nombre y calorías.",
"logmeal.validationTitle": "Faltan datos",
"logmeal.whatPlaceholder": "¿Qué has comido?",
"mealbox.eat": "Comer ahora",
"mealbox.eatBy": "Consumir antes del {date}",
"mealbox.empty": "No hay táperes ahora mismo. Al cocinar puedes guardar raciones aquí.",
"mealbox.enjoyBody": "La ración se ha registrado en Mi día.",
"mealbox.enjoyTitle": "¡Buen provecho!",
"mealbox.guidanceNote": "El plazo recomendado es orientativo: confía en el olfato y el gusto.",
"mealbox.portionsLeft_one": "Queda 1 ración",
"mealbox.portionsLeft_other": "Quedan {count} raciones",
"mealbox.title": "Táperes",
"memory.delete": "Eliminar",
"memory.deleteAll": "Eliminar toda la memoria",
"memory.deleteAllTitle": "¿Borrar toda la memoria?",
"memory.empty": "{brand} aún no ha aprendido nada sobre ti.",
"memory.irreversible": "Esto no se puede deshacer.",
"memory.origin.ai_inferred": "Suposición de la IA",
"memory.origin.observed": "Patrón observado",
"memory.origin.user_stated": "Nos lo contaste",
"memory.pause": "Pausar",
"memory.paused": "En pausa",
"memory.resume": "Reanudar",
"memory.subtitle": "Transparencia total. Corrige lo que esté mal, pausa o elimina: tu memoria es tuya.",
"memory.title": "Lo que {brand} sabe de mí",
"memory.verified": "Verificado",
"memory.verify": "Correcto",
"myday.calories": "Calorías",
"myday.carbs": "Hidratos",
"myday.estimateNote": "Los valores con ~ son estimaciones que puedes ajustar.",
"myday.estimateRange": "{min}{max} kcal, probablemente {kcal}",
"myday.fat": "Grasas",
"myday.fiber": "Fibra",
"myday.kcalProtein": "{kcal} kcal · {protein} g de proteína",
"myday.logMeal": "Registrar comida",
"myday.mealType.breakfast": "Desayuno",
"myday.mealType.dessert": "Postre",
"myday.mealType.dinner": "Cena",
"myday.mealType.lunch": "Almuerzo",
"myday.mealType.snack": "Tentempié",
"myday.noMeals": "No hay comidas registradas hoy.",
"myday.over": "{kcal} kcal por encima del objetivo",
"myday.overRecommended": "Por encima de lo recomendado",
"myday.protein": "Proteínas",
"myday.remaining": "{kcal} kcal restantes",
"myday.salt": "Sal",
"myday.targetsNote": "Los objetivos son orientación, no consejo médico.",
"myday.title": "Mi día",
"myday.todaysMeals": "Comidas de hoy",
"onboarding.allergen.crustaceans": "Crustáceos",
"onboarding.allergen.eggs": "Huevo",
"onboarding.allergen.fish": "Pescado",
"onboarding.allergen.gluten": "Gluten",
"onboarding.allergen.milk": "Leche/lactosa",
"onboarding.allergen.peanuts": "Cacahuetes",
"onboarding.allergen.sesame": "Sésamo",
"onboarding.allergen.soy": "Soja",
"onboarding.allergen.tree_nuts": "Frutos de cáscara",
"onboarding.allergies": "Alergias e intolerancias",
"onboarding.allergyNote": "El filtro de alergias es siempre estricto: los platos con tus alérgenos nunca se muestran.",
"onboarding.birthYearPlaceholder": "Año de nacimiento",
"onboarding.bodyTitle": "Para objetivos calóricos personales (opcional)",
"onboarding.diet": "¿Cómo comes?",
"onboarding.diet.flexitarian": "Flexitariano",
"onboarding.diet.omnivore": "Omnívoro",
"onboarding.diet.pescatarian": "Pescetariano",
"onboarding.diet.vegan": "Vegano",
"onboarding.diet.vegetarian": "Vegetariano",
"onboarding.goal": "¿Cuál es tu objetivo principal?",
"onboarding.goal.build_muscle": "Ganar músculo",
"onboarding.goal.cook_more": "Cocinar más en casa",
"onboarding.goal.less_waste": "Reducir el desperdicio",
"onboarding.goal.lose_weight": "Perder peso",
"onboarding.goal.lower_cost": "Gastar menos en comida",
"onboarding.goal.maintain_weight": "Mantener el peso",
"onboarding.goal.more_protein": "Comer más proteína",
"onboarding.heightPlaceholder": "Altura (cm)",
"onboarding.household": "Tu hogar",
"onboarding.householdCreate": "Crear hogar",
"onboarding.householdDefaultName": "Casa",
"onboarding.householdJoin": "Unirse con un código",
"onboarding.householdName": "Nombre del hogar",
"onboarding.inviteCode": "Código de invitación",
"onboarding.mode": "¿Cuánta precisión quieres?",
"onboarding.modeExact": "Modo exacto",
"onboarding.modeExactDesc": "Pesa la comida, introduce gramos, control total de las cifras.",
"onboarding.modeSimple": "Modo sencillo",
"onboarding.modeSimpleDesc": "Fotografía tu comida, acepta estimaciones, sin complicaciones.",
"onboarding.modesCombine": "Puedes combinar los modos: sencillo a diario, exacto cuando quieras.",
"onboarding.notMedical": "{brand} ofrece orientación, no consejo médico.",
"onboarding.stepBSubtitle": "Personaliza tu perfil para mejores sugerencias.",
"onboarding.stepBTitle": "Continuar configuración",
"onboarding.stepCSubtitle": "Opcional se usa para objetivos calóricos personales.",
"onboarding.stepCTitle": "Tu perfil de salud",
"onboarding.subtitle": "Todo es opcional y se puede cambiar cuando quieras. Cuanto más completes, mejores serán las sugerencias.",
"onboarding.title": "Cuéntanos un poco sobre ti",
"onboarding.weightPlaceholder": "Peso (kg)",
"paywall.choose": "Elegir",
"paywall.fairUse": "Cuota de uso razonable de escaneos de IA incluida.",
"paywall.family": "Family · hasta 6 personas",
"paywall.freeNote": "Gratis: 10 escaneos de IA/mes, inventario manual, recetas guardadas y registro sencillo.",
"paywall.household": "Household · hasta 3 personas",
"paywall.large": "Large Household · hasta 12 personas",
"paywall.perMonth": "{price}/mes",
"paywall.popular": "El más popular",
"paywall.restore": "Restaurar compras",
"paywall.soonBody": "Las compras se activarán a través del App Store/Google Play cuando se active la integración con las tiendas (fase 7). El flujo de backend ya está listo.",
"paywall.soonTitle": "¡Pronto!",
"paywall.subtitle": "El sistema operativo de comida de todo el hogar: inventario ilimitado, plan semanal y compartir.",
"paywall.title": "{brand} Premium",
"paywall.trialActive_one": "Queda 1 día de prueba",
"paywall.trialActive_other": "Quedan {days} días de prueba",
"profile.aiScansUsed": "Escaneos de IA: {used} / {total} este mes",
"profile.consent.anonymized_improvement": "Mejora anonimizada de la IA",
"profile.consent.health_integration": "Datos de salud (Apple Health / Health Connect)",
"profile.consent.image_training": "Mis fotos pueden usarse para entrenamiento",
"profile.consent.location_weather": "Ubicación para sugerencias según el tiempo",
"profile.consent.personalization": "Funciones personales (memoria y perfil de gustos)",
"profile.consent.product_analytics": "Análisis de producto",
"profile.consent.push_notifications": "Notificaciones push",
"profile.consents": "Consentimientos y datos",
"profile.consentsNote": "Los consentimientos son independientes: las funciones personales nunca exigen consentimiento de entrenamiento.",
"profile.deleteAccount": "Eliminar cuenta",
"profile.deleteBody": "Todos tus datos se eliminarán de forma permanente conforme al RGPD.",
"profile.deleteTitle": "¿Borrar la cuenta?",
"profile.emailUnverified": "Correo electrónico sin confirmar",
"profile.export": "Exportar mis datos",
"profile.goals": "Objetivos y dieta",
"profile.language": "Idioma",
"profile.language.da": "Dansk",
"profile.language.de": "Deutsch",
"profile.language.en": "English",
"profile.language.es": "Español",
"profile.language.fi": "Suomi",
"profile.language.fr": "Français",
"profile.language.it": "Italiano",
"profile.language.nb": "Norsk",
"profile.language.nl": "Nederlands",
"profile.language.pl": "Polski",
"profile.language.pt": "Português",
"profile.language.sv": "Svenska",
"profile.logout": "Cerrar sesión",
"profile.memory": "Lo que {brand} sabe de mí",
"profile.memorySubtitle": "Ve, corrige, pausa o borra lo que la plataforma ha aprendido.",
"profile.modeLabel": "Modo:",
"profile.resendVerification": "Reenviar correo de confirmación",
"profile.subscription": "Suscripción",
"profile.title": "Perfil",
"profile.verificationSent": "¡Enviado! Revisa tu bandeja de entrada.",
"recipe.avgRating": "Media de {avg} con {count} valoraciones",
"recipe.cook": "Cocinar ahora",
"recipe.cost": "aprox. {amount}/ración",
"recipe.iCookedThis": "Lo he cocinado",
"recipe.ingredients": "Ingredientes",
"recipe.notSafe": "No encaja con tu configuración dietética",
"recipe.perPortion": "por ración",
"recipe.portions_one": "1 ración",
"recipe.portions_other": "{count} raciones",
"recipe.save": "Guardar",
"recipe.saved": "Guardada",
"recipe.steps": "Preparación",
"recipe.substitutions": "Sustituir",
"recipe.time": "{min} min",
"scan.analyzing": "Analizando …",
"scan.barcode": "Código de barras",
"scan.coach.body": "Take a photo of the whole shelf or fridge. Keep the phone steady and make sure the light is good.",
"scan.coach.button": "Entendido",
"scan.coach.tip1": "Make sure all items are visible",
"scan.coach.tip2": "Avoid flash and shadows",
"scan.coach.tip3": "Confirm uncertain items in the next step",
"scan.coach.title": "Primer escaneo",
"scan.expiry": "Fecha de consumo preferente",
"scan.failed": "El análisis falló. Inténtalo de nuevo o añade manualmente.",
"scan.freezer": "Congelador",
"scan.fridge": "Nevera",
"scan.ingredients": "Ingredientes",
"scan.nutrition": "Etiqueta nutricional",
"scan.pantry": "Despensa",
"scan.plate": "Plato",
"scan.quotaLeft_one": "1 escaneo de IA restante este mes",
"scan.quotaLeft_other": "{count} escaneos de IA restantes este mes",
"scan.receipt": "Ticket",
"scan.review.addItem": "+ Añadir producto",
"scan.review.approveAll": "Guardar en el inventario",
"scan.review.bestBeforeNote": "Fecha de calidad: el alimento puede estar bien más allá. Huele y prueba antes de tirar.",
"scan.review.dateKindBestBefore": "Consumo preferente",
"scan.review.dateKindUseBy": "Fecha de caducidad",
"scan.review.datePlaceholder": "AAAA-MM-DD (opcional)",
"scan.review.itemPlaceholder": "Producto",
"scan.review.quantityPlaceholder": "Cantidad",
"scan.review.rejected": "Eliminado",
"scan.review.subtitle": "La IA a veces duda: tú decides. Edita, elimina o añade antes de guardar.",
"scan.review.title": "Revisa el resultado",
"scan.review.uncertain": "Dudoso compruébalo",
"scan.review.unitPlaceholder": "Unidad (g, l, uds …)",
"scan.review.unknownItem": "Producto desconocido",
"scan.review.useByNote": "Fecha de seguridad: no lo comas después de esta fecha.",
"scan.subtitle": "Llena tu inventario o registra una comida con la cámara.",
"scan.takePhoto": "Hacer foto",
"scan.tips.light": "Evita la oscuridad y las sombras",
"scan.tips.move": "Aparta los productos que se tapan entre sí",
"scan.tips.overview": "Empieza con una foto general",
"scan.tips.shelf": "Fotografía estante por estante",
"scan.tips.title": "Cómo obtener el mejor análisis",
"scan.title": "Escanear",
"shopping.addPlaceholder": "Añadir producto …",
"shopping.complete": "Terminar la compra",
"shopping.completeNote": "Los productos marcados se añaden al inventario.",
"shopping.completedBody_one": "Se añadió 1 producto al inventario.",
"shopping.completedBody_other": "Se añadieron {count} productos al inventario.",
"shopping.completedTitle": "¡Listo!",
"shopping.empty": "La lista está vacía.",
"shopping.estimated": "aprox. {amount}",
"shopping.estimatedTotal": "Total estimado: aprox. {amount}",
"shopping.section.brod": "Pan",
"shopping.section.chark": "Charcutería",
"shopping.section.dryck": "Bebidas",
"shopping.section.fisk": "Pescado",
"shopping.section.frukt_gront": "Fruta y verdura",
"shopping.section.frys": "Congelados",
"shopping.section.hygien_ovrigt": "Otros",
"shopping.section.konserver": "Conservas",
"shopping.section.kott_fagel": "Carne y aves",
"shopping.section.kryddor_bak": "Especias y repostería",
"shopping.section.mejeri": "Lácteos",
"shopping.section.skafferi": "Despensa",
"shopping.section.snacks": "Aperitivos",
"shopping.title": "Lista de la compra",
"tabs.home": "En casa",
"tabs.myDay": "Mi día",
"tabs.scan": "Escanear",
"tabs.whatToEat": "¿Qué comemos?",
"wte.coverage": "{pct}% en casa",
"wte.cravingPlaceholder": "Me apetece … (p. ej. cremoso, asiático, menos de 500 kcal)",
"wte.empty": "Aún no hay sugerencias: añade comida al inventario o relaja los filtros.",
"wte.mealBoxFirst": "Comida preparada en casa",
"wte.missing": "Falta: {items}",
"wte.refresh": "Nuevas sugerencias",
"wte.subtitle": "Según lo que tenéis en casa, lo que conviene usar pronto y lo que os gusta.",
"wte.title": "¿Qué comemos?",
"wte.whyTitle": "¿Por qué esta sugerencia?",
"onboarding.primaryGoal": "Principal",
"home.trustStatus.up_to_date": "Actualizado",
"home.trustStatus.needs_check": "Algunos productos necesitan revisión",
"home.trustStatus.uncertain": "Inventario incierto haz una revisión rápida",
"reconciliation.title": "Revisión rápida del inventario",
"reconciliation.subtitle": "Confirma lo que te queda para obtener mejores sugerencias.",
"reconciliation.exists": "Todavía lo tengo",
"reconciliation.depleted": "Se ha acabado",
"reconciliation.uncertain": "No estoy seguro",
"reconciliation.adjustQuantity": "Ajustar cantidad",
"reconciliation.reason.planned_recipe": "Se necesita pronto para una receta",
"reconciliation.reason.expiring_soon": "Caduca pronto",
"reconciliation.reason.high_value": "Alto valor",
"reconciliation.reason.low_confidence": "Cantidad estimada",
"reconciliation.reason.stale_trust": "No se ha comprobado en mucho tiempo",
"reconciliation.reason.likely_depleted": "Probablemente se acabe pronto",
"reconciliation.done": "Listo",
"reconciliation.next": "Siguiente",
"reconciliation.empty": "No hay nada que revisar ahora. ¡Bien hecho!",
"reconciliation.tasteHint": "¿Pasada la fecha de consumo preferente? Huele y prueba primero: nunca tires comida sin necesidad.",
"scan.diff.title": "Revisar diferencias",
"scan.diff.subtitle": "Confirmar cambios del último escaneo.",
"scan.diff.newItem": "Nuevo producto",
"scan.diff.quantityChanged": "Cantidad cambiada",
"scan.diff.moved": "Movido",
"scan.diff.vanished": "No aparece en la foto",
"scan.diff.unchanged": "Sin cambios",
"scan.diff.accept": "Confirmar",
"scan.diff.reject": "Ignorar",
"scan.diff.acceptAll": "Confirmar todo",
"scan.diff.noChanges": "No se encontraron diferencias.",
"scan.diff.undo": "Deshacer",
"scan.diff.undoHint": "Cada cambio se puede deshacer desde la vista de detalle del producto.",
"cooked.portionsSumExceedsPlanned": "Las raciones comidas y las sobras no pueden superar el número total de raciones.",
"cooked.undoWindowExpired": "Ya no se puede deshacer esta sesión de cocina. Ha expirado la ventana de 24 horas."
}