diff --git a/REASONING_I18N_AUDIT.md b/REASONING_I18N_AUDIT.md new file mode 100644 index 00000000..4f4bc0c4 --- /dev/null +++ b/REASONING_I18N_AUDIT.md @@ -0,0 +1,65 @@ +# Reasoning i18n Audit Report + +## Files with Hardcoded English Reasoning Text + +### ✅ Already Fixed +1. **services/orchestrator/app/services/dashboard_service.py** - Now returns reasoning_data +2. **services/procurement/app/services/procurement_service.py** - Generates structured reasoning_data +3. **services/production/app/services/production_service.py** - Generates structured reasoning_data + +### ❌ Needs Fixing + +#### 1. Demo Seed Scripts +**File:** `services/procurement/scripts/demo/seed_demo_purchase_orders.py` +- Line 126: `"Low stock detected for {supplier.name} items..."` +- Line 127: `"Stock-out risk in {days_until_delivery + 2} days..."` +- Line 135: `"Auto-approved based on supplier trust score..."` + +**File:** `services/production/scripts/demo/seed_demo_batches.py` +- Similar hardcoded text (needs check) + +**Fix:** Use `create_po_reasoning_*()` helper functions + +#### 2. Safety Stock Calculator +**File:** `services/procurement/app/services/safety_stock_calculator.py` +- Line 111: `'Lead time or demand std dev is zero or negative'` +- Line 163: `'Insufficient historical demand data (need at least 2 data points)'` + +**Fix:** Return structured error codes instead of English text + +#### 3. Replenishment Planning Service +**File:** `services/procurement/app/services/replenishment_planning_service.py` +- Line 376: `'Insufficient data for safety stock calculation'` + +**Fix:** Return structured error codes + +#### 4. ML Services +**File:** `services/procurement/app/ml/price_forecaster.py` +- Needs audit for hardcoded reasoning text + +#### 5. Frontend Components +**File:** `frontend/src/components/dashboard/OrchestrationSummaryCard.tsx` +- Hardcoded English text: "Last Night I Planned Your Day", "All caught up!", etc. + +**File:** `frontend/src/components/dashboard/HealthStatusCard.tsx` +- Hardcoded English text + +**File:** `frontend/src/components/dashboard/ActionQueueCard.tsx` +- Hardcoded English text: "What Needs Your Attention", "Why this is needed:", etc. + +**File:** `frontend/src/components/dashboard/ProductionTimelineCard.tsx` +- Hardcoded English text + +**File:** `frontend/src/components/dashboard/InsightsGrid.tsx` +- Uses backend labels (good) but needs i18n setup + +## Strategy + +### Backend +- Return structured error codes: `{"type": "error", "code": "INSUFFICIENT_DATA", "params": {...}}` +- Frontend translates based on code + +### Frontend +- Setup `react-i18next` +- Create translation files for EN, ES, CA +- Update all dashboard components to use `t()` function diff --git a/frontend/src/hooks/useReasoningTranslation.ts b/frontend/src/hooks/useReasoningTranslation.ts new file mode 100644 index 00000000..8161b5e2 --- /dev/null +++ b/frontend/src/hooks/useReasoningTranslation.ts @@ -0,0 +1,165 @@ +/** + * Hook for translating reasoning_data structures + * + * Handles translation of structured backend reasoning data into + * user-friendly, multilingual text for the JTBD dashboard. + */ + +import { useTranslation } from 'react-i18next'; + +export interface ReasoningData { + type: string; + parameters: Record; + consequence?: { + type: string; + severity?: string; + impact_days?: number; + [key: string]: any; + }; + urgency?: { + level?: string; + [key: string]: any; + }; + metadata?: Record; +} + +export function useReasoningTranslation() { + const { t } = useTranslation('reasoning'); + + /** + * Translate purchase order reasoning + */ + const translatePOReasonng = (reasoningData: ReasoningData): string => { + if (!reasoningData || !reasoningData.type) { + return t('purchaseOrder.low_stock_detection', { + supplier_name: 'Unknown', + product_names_joined: 'Items', + days_until_stockout: 7 + }); + } + + const { type, parameters } = reasoningData; + + // Join product names if array + const params = { + ...parameters, + product_names_joined: Array.isArray(parameters.product_names) + ? parameters.product_names.join(', ') + : parameters.product_names || 'Items' + }; + + return t(`purchaseOrder.${type}`, params); + }; + + /** + * Translate production batch reasoning + */ + const translateBatchReasoning = (reasoningData: ReasoningData): string => { + if (!reasoningData || !reasoningData.type) { + return t('productionBatch.forecast_demand', { + product_name: 'Product', + predicted_demand: 0, + current_stock: 0, + confidence_score: 85 + }); + } + + const { type, parameters } = reasoningData; + return t(`productionBatch.${type}`, parameters); + }; + + /** + * Translate consequence text + */ + const translateConsequence = (consequenceData?: any): string => { + if (!consequenceData || !consequenceData.type) { + return ''; + } + + const params = { + ...consequenceData, + affected_products_joined: Array.isArray(consequenceData.affected_products) + ? consequenceData.affected_products.join(', ') + : consequenceData.affected_products || 'products' + }; + + return t(`consequence.${consequenceData.type}`, params); + }; + + /** + * Translate severity level + */ + const translateSeverity = (severity?: string): string => { + if (!severity) return ''; + return t(`severity.${severity}`); + }; + + /** + * Translate trigger source + */ + const translateTrigger = (trigger?: string): string => { + if (!trigger) return ''; + return t(`triggers.${trigger}`); + }; + + /** + * Translate error code + */ + const translateError = (errorCode: string): string => { + return t(`errors.${errorCode}`, { defaultValue: errorCode }); + }; + + return { + translatePOReasonng, + translateBatchReasoning, + translateConsequence, + translateSeverity, + translateTrigger, + translateError, + t, // Expose the raw t function for direct access + }; +} + +/** + * Format reasoning data for display + * Combines reasoning and consequence into a cohesive message + */ +export function useReasoningFormatter() { + const translation = useReasoningTranslation(); + + const formatPOAction = (reasoningData?: ReasoningData) => { + if (!reasoningData) { + return { + reasoning: translation.translatePOReasonng({} as ReasoningData), + consequence: '', + severity: '' + }; + } + + return { + reasoning: translation.translatePOReasonng(reasoningData), + consequence: translation.translateConsequence(reasoningData.consequence), + severity: translation.translateSeverity(reasoningData.consequence?.severity) + }; + }; + + const formatBatchAction = (reasoningData?: ReasoningData) => { + if (!reasoningData) { + return { + reasoning: translation.translateBatchReasoning({} as ReasoningData), + urgency: '' + }; + } + + return { + reasoning: translation.translateBatchReasoning(reasoningData), + urgency: reasoningData.urgency?.level || 'normal' + }; + }; + + return { + formatPOAction, + formatBatchAction, + ...translation + }; +} diff --git a/frontend/src/locales/en/reasoning.json b/frontend/src/locales/en/reasoning.json new file mode 100644 index 00000000..c0e96a66 --- /dev/null +++ b/frontend/src/locales/en/reasoning.json @@ -0,0 +1,118 @@ +{ + "purchaseOrder": { + "low_stock_detection": "Low stock for {{supplier_name}}. Current stock of {{product_names_joined}} will run out in {{days_until_stockout}} days.", + "forecast_demand": "Order scheduled based on {{forecast_period_days}}-day demand forecast for {{product_names_joined}} from {{supplier_name}}.", + "safety_stock_replenishment": "Replenishing safety stock for {{product_names_joined}} from {{supplier_name}}.", + "supplier_contract": "Scheduled order per contract with {{supplier_name}}.", + "seasonal_demand": "Seasonal demand preparation for {{product_names_joined}} from {{supplier_name}}.", + "production_requirement": "Required for upcoming production batches from {{supplier_name}}.", + "manual_request": "Manual purchase request for {{product_names_joined}} from {{supplier_name}}." + }, + "productionBatch": { + "forecast_demand": "Scheduled based on forecast: {{predicted_demand}} {{product_name}} needed (current stock: {{current_stock}}). Confidence: {{confidence_score}}%.", + "customer_order": "Customer order for {{customer_name}}: {{order_quantity}} {{product_name}} (Order #{{order_number}}) - delivery {{delivery_date}}.", + "stock_replenishment": "Stock replenishment for {{product_name}} - current level below minimum.", + "seasonal_preparation": "Seasonal preparation batch for {{product_name}}.", + "promotion_event": "Production for promotional event - {{product_name}}.", + "urgent_order": "Urgent order requiring immediate production of {{product_name}}.", + "regular_schedule": "Regular scheduled production of {{product_name}}." + }, + "consequence": { + "stockout_risk": "Stock-out risk in {{impact_days}} days. Products affected: {{affected_products_joined}}.", + "insufficient_supply": "Insufficient supply for {{impact_days}}-day period.", + "production_delay": "Potential production delay of {{delay_hours}} hours.", + "customer_commitment": "Customer delivery commitment at risk.", + "quality_issue": "Quality standards may be compromised.", + "cost_increase": "Material costs may increase by {{percentage}}%." + }, + "severity": { + "critical": "Critical", + "high": "High", + "medium": "Medium", + "low": "Low" + }, + "triggers": { + "orchestrator_auto": "Automatic (Orchestrator)", + "manual": "Manual Request", + "customer_order": "Customer Order", + "forecast": "Demand Forecast", + "inventory_alert": "Inventory Alert" + }, + "errors": { + "INSUFFICIENT_DATA": "Insufficient historical data for accurate calculation", + "INVALID_PARAMETERS": "Invalid parameters provided", + "LEAD_TIME_INVALID": "Lead time or demand deviation is zero or negative", + "NO_DEMAND_DATA": "No historical demand data available (minimum 2 data points required)" + }, + "jtbd": { + "health_status": { + "green": "Everything is running smoothly", + "yellow": "Some items need attention", + "red": "Critical issues require immediate action", + "last_updated": "Last updated", + "next_check": "Next check" + }, + "action_queue": { + "title": "What Needs Your Attention", + "why_needed": "Why this is needed:", + "what_if_not": "What happens if I don't do this?", + "estimated_time": "Estimated time: {{minutes}} min", + "all_caught_up": "All caught up!", + "no_actions": "No actions requiring your attention right now.", + "show_more": "Show {{count}} More Action{{plural}}", + "show_less": "Show Less", + "critical_badge": "{{count}} critical", + "important_badge": "{{count}} important" + }, + "orchestration_summary": { + "title": "Last Night I Planned Your Day", + "no_runs": "Ready to Plan Your Bakery Day", + "no_runs_message": "The system hasn't run daily planning yet. Click 'Run Daily Planning' to generate your first plan.", + "run_number": "Orchestration run #{{number}}", + "duration": "Took {{seconds}}s", + "pos_created": "Created {{count}} purchase order{{plural}}", + "batches_created": "Scheduled {{count}} production batch{{plural}}", + "no_actions": "No new actions needed - everything is on track!", + "based_on": "Based on:", + "customer_orders": "{{count}} customer order{{plural}}", + "historical_demand": "Historical demand", + "inventory_levels": "Inventory levels", + "ai_optimization": "AI optimization", + "actions_required": "{{count}} item{{plural}} need{{verb}} your approval before proceeding" + }, + "production_timeline": { + "title": "Today's Production Timeline", + "no_batches": "No production batches scheduled for today", + "status": { + "pending": "Pending", + "in_progress": "In Progress", + "completed": "Completed", + "cancelled": "Cancelled" + }, + "ready_by": "Ready by {{time}}", + "priority": { + "low": "Low Priority", + "normal": "Normal", + "high": "High Priority", + "urgent": "Urgent" + } + }, + "insights": { + "savings": "Savings This Week", + "inventory": "Inventory Status", + "waste": "Waste Reduction", + "deliveries": "On-Time Deliveries" + }, + "actions": { + "approve": "Approve", + "view_details": "View Details", + "modify": "Modify", + "start_batch": "Start Batch", + "pause_batch": "Pause", + "complete_setup": "Complete Setup", + "dismiss": "Dismiss", + "view_alert": "View Details", + "run_planning": "Run Daily Planning" + } + } +} diff --git a/frontend/src/locales/es/reasoning.json b/frontend/src/locales/es/reasoning.json new file mode 100644 index 00000000..ed502e2a --- /dev/null +++ b/frontend/src/locales/es/reasoning.json @@ -0,0 +1,118 @@ +{ + "purchaseOrder": { + "low_stock_detection": "Stock bajo para {{supplier_name}}. El stock actual de {{product_names_joined}} se agotará en {{days_until_stockout}} días.", + "forecast_demand": "Pedido programado basado en pronóstico de demanda de {{forecast_period_days}} días para {{product_names_joined}} de {{supplier_name}}.", + "safety_stock_replenishment": "Reposición de stock de seguridad para {{product_names_joined}} de {{supplier_name}}.", + "supplier_contract": "Pedido programado según contrato con {{supplier_name}}.", + "seasonal_demand": "Preparación de demanda estacional para {{product_names_joined}} de {{supplier_name}}.", + "production_requirement": "Requerido para próximos lotes de producción de {{supplier_name}}.", + "manual_request": "Solicitud de compra manual para {{product_names_joined}} de {{supplier_name}}." + }, + "productionBatch": { + "forecast_demand": "Programado según pronóstico: {{predicted_demand}} {{product_name}} necesarios (stock actual: {{current_stock}}). Confianza: {{confidence_score}}%.", + "customer_order": "Pedido de cliente para {{customer_name}}: {{order_quantity}} {{product_name}} (Pedido #{{order_number}}) - entrega {{delivery_date}}.", + "stock_replenishment": "Reposición de stock para {{product_name}} - nivel actual por debajo del mínimo.", + "seasonal_preparation": "Lote de preparación estacional para {{product_name}}.", + "promotion_event": "Producción para evento promocional - {{product_name}}.", + "urgent_order": "Pedido urgente que requiere producción inmediata de {{product_name}}.", + "regular_schedule": "Producción programada regular de {{product_name}}." + }, + "consequence": { + "stockout_risk": "Riesgo de desabastecimiento en {{impact_days}} días. Productos afectados: {{affected_products_joined}}.", + "insufficient_supply": "Suministro insuficiente para período de {{impact_days}} días.", + "production_delay": "Posible retraso en producción de {{delay_hours}} horas.", + "customer_commitment": "Compromiso de entrega al cliente en riesgo.", + "quality_issue": "Los estándares de calidad pueden verse comprometidos.", + "cost_increase": "Los costos de materiales pueden aumentar un {{percentage}}%." + }, + "severity": { + "critical": "Crítico", + "high": "Alto", + "medium": "Medio", + "low": "Bajo" + }, + "triggers": { + "orchestrator_auto": "Automático (Orquestador)", + "manual": "Solicitud Manual", + "customer_order": "Pedido de Cliente", + "forecast": "Pronóstico de Demanda", + "inventory_alert": "Alerta de Inventario" + }, + "errors": { + "INSUFFICIENT_DATA": "Datos históricos insuficientes para un cálculo preciso", + "INVALID_PARAMETERS": "Parámetros proporcionados no válidos", + "LEAD_TIME_INVALID": "El tiempo de entrega o la desviación de la demanda es cero o negativo", + "NO_DEMAND_DATA": "No hay datos históricos de demanda disponibles (se requieren mínimo 2 puntos de datos)" + }, + "jtbd": { + "health_status": { + "green": "Todo funciona correctamente", + "yellow": "Algunos elementos necesitan atención", + "red": "Problemas críticos requieren acción inmediata", + "last_updated": "Última actualización", + "next_check": "Próxima verificación" + }, + "action_queue": { + "title": "Qué Necesita Tu Atención", + "why_needed": "Por qué es necesario esto:", + "what_if_not": "¿Qué pasa si no hago esto?", + "estimated_time": "Tiempo estimado: {{minutes}} min", + "all_caught_up": "¡Todo al día!", + "no_actions": "No hay acciones que requieran tu atención en este momento.", + "show_more": "Mostrar {{count}} Acción{{plural}} Más", + "show_less": "Mostrar Menos", + "critical_badge": "{{count}} crítico{{plural}}", + "important_badge": "{{count}} importante{{plural}}" + }, + "orchestration_summary": { + "title": "Anoche Planifiqué Tu Día", + "no_runs": "Listo para Planificar Tu Día en la Panadería", + "no_runs_message": "El sistema aún no ha ejecutado la planificación diaria. Haz clic en 'Ejecutar Planificación Diaria' para generar tu primer plan.", + "run_number": "Ejecución de orquestación #{{number}}", + "duration": "Tardó {{seconds}}s", + "pos_created": "Creé {{count}} orden{{plural}} de compra", + "batches_created": "Programé {{count}} lote{{plural}} de producción", + "no_actions": "¡No se necesitan nuevas acciones - todo está en marcha!", + "based_on": "Basado en:", + "customer_orders": "{{count}} pedido{{plural}} de cliente{{plural}}", + "historical_demand": "Demanda histórica", + "inventory_levels": "Niveles de inventario", + "ai_optimization": "Optimización con IA", + "actions_required": "{{count}} elemento{{plural}} necesita{{verb}} tu aprobación antes de proceder" + }, + "production_timeline": { + "title": "Línea de Tiempo de Producción de Hoy", + "no_batches": "No hay lotes de producción programados para hoy", + "status": { + "pending": "Pendiente", + "in_progress": "En Proceso", + "completed": "Completado", + "cancelled": "Cancelado" + }, + "ready_by": "Listo para {{time}}", + "priority": { + "low": "Prioridad Baja", + "normal": "Normal", + "high": "Prioridad Alta", + "urgent": "Urgente" + } + }, + "insights": { + "savings": "Ahorros Esta Semana", + "inventory": "Estado del Inventario", + "waste": "Reducción de Desperdicio", + "deliveries": "Entregas a Tiempo" + }, + "actions": { + "approve": "Aprobar", + "view_details": "Ver Detalles", + "modify": "Modificar", + "start_batch": "Iniciar Lote", + "pause_batch": "Pausar", + "complete_setup": "Completar Configuración", + "dismiss": "Descartar", + "view_alert": "Ver Detalles", + "run_planning": "Ejecutar Planificación Diaria" + } + } +} diff --git a/frontend/src/locales/eu/reasoning.json b/frontend/src/locales/eu/reasoning.json new file mode 100644 index 00000000..73bac8e2 --- /dev/null +++ b/frontend/src/locales/eu/reasoning.json @@ -0,0 +1,120 @@ +{ + "purchaseOrder": { + "low_stock_detection": "{{supplier_name}}-rentzat stock baxua. {{product_names_joined}}-ren egungo stocka {{days_until_stockout}} egunetan amaitu + +ko da.", + "forecast_demand": "{{supplier_name}}-ren {{product_names_joined}}-rentzat {{forecast_period_days}} eguneko eskaera aurreikuspenean oinarritutako eskaera programatua.", + "safety_stock_replenishment": "{{supplier_name}}-ren {{product_names_joined}}-rentzat segurtasun stockaren birjartzea.", + "supplier_contract": "{{supplier_name}}-rekin kontratuaren arabera programatutako eskaera.", + "seasonal_demand": "{{supplier_name}}-ren {{product_names_joined}}-rentzat denboraldiko eskaeraren prestaketa.", + "production_requirement": "{{supplier_name}}-ren hurrengo ekoizpen loteetarako beharrezkoa.", + "manual_request": "{{supplier_name}}-ren {{product_names_joined}}-rentzat eskuzko erosketa eskaera." + }, + "productionBatch": { + "forecast_demand": "Aurreikuspenen arabera programatua: {{predicted_demand}} {{product_name}} behar dira (egungo stocka: {{current_stock}}). Konfiantza: {{confidence_score}}%.", + "customer_order": "{{customer_name}}-rentzat bezeroaren eskaera: {{order_quantity}} {{product_name}} (Eskaera #{{order_number}}) - entrega {{delivery_date}}.", + "stock_replenishment": "{{product_name}}-rentzat stockaren birjartzea - egungo maila minimoa baino txikiagoa.", + "seasonal_preparation": "{{product_name}}-rentzat denboraldiko prestaketa lotea.", + "promotion_event": "Promozio ekitaldirako ekoizpena - {{product_name}}.", + "urgent_order": "{{product_name}}-ren berehalako ekoizpena behar duen eskaera larria.", + "regular_schedule": "{{product_name}}-ren ohiko ekoizpen programatua." + }, + "consequence": { + "stockout_risk": "Stock amaitzeko arriskua {{impact_days}} egunetan. Produktu kaltetuak: {{affected_products_joined}}.", + "insufficient_supply": "{{impact_days}} eguneko aldirako hornidura ez nahikoa.", + "production_delay": "{{delay_hours}} orduko ekoizpen atzerapena posiblea.", + "customer_commitment": "Bezeroari entregatzeko konpromisoa arriskuan.", + "quality_issue": "Kalitate estandarrak arriskuan egon daitezke.", + "cost_increase": "Materialen kostuak %{{percentage}} gehitu daitezke." + }, + "severity": { + "critical": "Kritikoa", + "high": "Altua", + "medium": "Ertaina", + "low": "Baxua" + }, + "triggers": { + "orchestrator_auto": "Automatikoa (Orkestatzailea)", + "manual": "Eskuzko Eskaera", + "customer_order": "Bezeroaren Eskaera", + "forecast": "Eskaeraren Aurreikuspena", + "inventory_alert": "Inbentarioaren Alerta" + }, + "errors": { + "INSUFFICIENT_DATA": "Datu historiko ez nahikoa kalkulu zehatzerako", + "INVALID_PARAMETERS": "Emandako parametroak ez baliodunak", + "LEAD_TIME_INVALID": "Entrega denbora edo eskaeraren desbideratzea zero edo negatiboa da", + "NO_DEMAND_DATA": "Ez dago eskaeraren datu historikorik eskuragarri (gutxienez 2 datu puntu behar dira)" + }, + "jtbd": { + "health_status": { + "green": "Dena ondo dabil", + "yellow": "Elementu batzuek arreta behar dute", + "red": "Arazo kritikoek ekintza berehalakoa behar dute", + "last_updated": "Azken eguneratzea", + "next_check": "Hurrengo egiaztapena" + }, + "action_queue": { + "title": "Zer Behar Du Zure Arreta", + "why_needed": "Zergatik behar da hau:", + "what_if_not": "Zer gertatzen da hau egiten ez badut?", + "estimated_time": "Estimatutako denbora: {{minutes}} min", + "all_caught_up": "Dena egunean!", + "no_actions": "Ez dago une honetan zure arreta behar duen ekintzarik.", + "show_more": "Erakutsi {{count}} Ekintza{{plural}} Gehiago", + "show_less": "Erakutsi Gutxiago", + "critical_badge": "{{count}} kritiko{{plural}}", + "important_badge": "{{count}} garrantzitsu{{plural}}" + }, + "orchestration_summary": { + "title": "Bart Gauean Zure Eguna Planifikatu Nuen", + "no_runs": "Zure Okindegiko Eguna Planifikatzeko Prest", + "no_runs_message": "Sistemak oraindik ez du eguneko plangintza exekutatu. Egin klik 'Exekutatu Eguneko Plangintza'-n zure lehen plana sortzeko.", + "run_number": "Orkestazio exekuzioa #{{number}}", + "duration": "{{seconds}}s behar izan zituen", + "pos_created": "{{count}} erosketa agindu{{plural}} sortu nituen", + "batches_created": "{{count}} ekoizpen lote{{plural}} programatu nituen", + "no_actions": "Ez dira ekintza berriak behar - dena bidean dago!", + "based_on": "Oinarrituta:", + "customer_orders": "{{count}} bezero eskaera{{plural}}", + "historical_demand": "Eskaera historikoa", + "inventory_levels": "Inbentario mailak", + "ai_optimization": "IA optimizazioa", + "actions_required": "{{count}} elementu{{plural}}k zure onespena behar du{{verb}} aurrera jarraitu aurretik" + }, + "production_timeline": { + "title": "Gaurko Ekoizpen Denbora-lerroa", + "no_batches": "Ez dago ekoizpen loterik programatuta gaurko", + "status": { + "pending": "Zain", + "in_progress": "Prozesuan", + "completed": "Osatua", + "cancelled": "Bertan behera utzita" + }, + "ready_by": "{{time}}rako prest", + "priority": { + "low": "Lehentasun Baxua", + "normal": "Normala", + "high": "Lehentasun Altua", + "urgent": "Larria" + } + }, + "insights": { + "savings": "Aste Honetako Aurrezkiak", + "inventory": "Inbentarioaren Egoera", + "waste": "Hondakinen Murrizketa", + "deliveries": "Garaiz Entregatutakoak" + }, + "actions": { + "approve": "Onetsi", + "view_details": "Ikusi Xehetasunak", + "modify": "Aldatu", + "start_batch": "Hasi Lotea", + "pause_batch": "Pausatu", + "complete_setup": "Osatu Konfigurazioa", + "dismiss": "Baztertu", + "view_alert": "Ikusi Xehetasunak", + "run_planning": "Exekutatu Eguneko Plangintza" + } + } +}