Files
bakery-ia/frontend/src/utils/eventI18n.ts
2025-12-05 20:07:01 +01:00

178 lines
5.6 KiB
TypeScript

/**
* Clean i18n Parameter System for Event Content in Frontend
*
* Handles rendering of parameterized content for:
* - Alert titles and messages
* - Notification titles and messages
* - Recommendation titles and messages
* - AI reasoning summaries
* - Action labels and consequences
*/
import { I18nContent, Event, Alert, Notification, Recommendation, SmartAction } from '../api/types/events';
import { useTranslation } from 'react-i18next';
interface I18nRenderer {
renderTitle: (titleKey: string, titleParams?: Record<string, any>) => string;
renderMessage: (messageKey: string, messageParams?: Record<string, any>) => string;
renderReasoningSummary: (summaryKey: string, summaryParams?: Record<string, any>) => string;
renderActionLabel: (labelKey: string, labelParams?: Record<string, any>) => string;
renderUrgencyReason: (reasonKey: string, reasonParams?: Record<string, any>) => string;
}
/**
* Render a parameterized template with given parameters
*/
export const renderTemplate = (template: string, params: Record<string, any> = {}): string => {
if (!template) return '';
let result = template;
for (const [key, value] of Object.entries(params)) {
// Replace {{key}} with the value, handling nested properties
const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g');
result = result.replace(regex, String(value ?? ''));
}
return result;
};
/**
* Hook for accessing the i18n renderer within React components
*/
export const useEventI18n = (): I18nRenderer => {
const { t } = useTranslation(['events', 'common']);
const renderTitle = (titleKey: string, titleParams: Record<string, any> = {}): string => {
return t(titleKey, { defaultValue: titleKey, ...titleParams });
};
const renderMessage = (messageKey: string, messageParams: Record<string, any> = {}): string => {
return t(messageKey, { defaultValue: messageKey, ...messageParams });
};
const renderReasoningSummary = (summaryKey: string, summaryParams: Record<string, any> = {}): string => {
return t(summaryKey, { defaultValue: summaryKey, ...summaryParams });
};
const renderActionLabel = (labelKey: string, labelParams: Record<string, any> = {}): string => {
return t(labelKey, { defaultValue: labelKey, ...labelParams });
};
const renderUrgencyReason = (reasonKey: string, reasonParams: Record<string, any> = {}): string => {
return t(reasonKey, { defaultValue: reasonKey, ...reasonParams });
};
return {
renderTitle,
renderMessage,
renderReasoningSummary,
renderActionLabel,
renderUrgencyReason
};
};
/**
* Render i18n content for an event
*/
export const renderEventContent = (i18n: I18nContent, language?: string): { title: string; message: string } => {
const title = renderTemplate(i18n.title_key, i18n.title_params);
const message = renderTemplate(i18n.message_key, i18n.message_params);
return { title, message };
};
/**
* Render all content for an alert
*/
export const renderAlertContent = (alert: Alert, language?: string) => {
const { title, message } = renderEventContent(alert.i18n, language);
let reasoningSummary = '';
if (alert.ai_reasoning?.summary_key) {
reasoningSummary = renderTemplate(
alert.ai_reasoning.summary_key,
alert.ai_reasoning.summary_params
);
}
// Render smart actions with parameterized labels
const renderedActions = alert.smart_actions.map(action => ({
...action,
label: renderTemplate(action.label_key, action.label_params),
consequence: action.consequence_key
? renderTemplate(action.consequence_key, action.consequence_params)
: undefined,
disabled_reason: action.disabled_reason_key
? renderTemplate(action.disabled_reason_key, action.disabled_reason_params)
: action.disabled_reason
}));
return {
title,
message,
reasoningSummary,
renderedActions
};
};
/**
* Render all content for a notification
*/
export const renderNotificationContent = (notification: Notification, language?: string) => {
const { title, message } = renderEventContent(notification.i18n, language);
return {
title,
message
};
};
/**
* Render all content for a recommendation
*/
export const renderRecommendationContent = (recommendation: Recommendation, language?: string) => {
const { title, message } = renderEventContent(recommendation.i18n, language);
let reasoningSummary = '';
if (recommendation.ai_reasoning?.summary_key) {
reasoningSummary = renderTemplate(
recommendation.ai_reasoning.summary_key,
recommendation.ai_reasoning.summary_params
);
}
// Render suggested actions with parameterized labels
const renderedSuggestedActions = recommendation.suggested_actions.map(action => ({
...action,
label: renderTemplate(action.label_key, action.label_params),
consequence: action.consequence_key
? renderTemplate(action.consequence_key, action.consequence_params)
: undefined,
disabled_reason: action.disabled_reason_key
? renderTemplate(action.disabled_reason_key, action.disabled_reason_params)
: action.disabled_reason
}));
return {
title,
message,
reasoningSummary,
renderedSuggestedActions
};
};
/**
* Render content for any event type
*/
export const renderEvent = (event: Event, language?: string) => {
switch (event.event_class) {
case 'alert':
return renderAlertContent(event as Alert, language);
case 'notification':
return renderNotificationContent(event as Notification, language);
case 'recommendation':
return renderRecommendationContent(event as Recommendation, language);
default:
throw new Error(`Unknown event class: ${(event as any).event_class}`);
}
};