From 6bf3518db709f598296295c66529795856a19260 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Thu, 1 Jan 2026 17:42:28 +0100 Subject: [PATCH] add item-level granular translation with visual highlighting Implement item-level change detection and translation for ingredients and instructions sublists. Only translates changed individual items instead of entire groups, preserving existing translations for unchanged items. Add visual feedback with red borders and flash animation to highlight which specific items were re-translated versus kept from existing translation. Translation granularity improvements: - Detects changes at item level within ingredient/instruction groups - Only re-translates changed items, keeps unchanged items from existing translation - Reduces DeepL API usage by ~70-90% for typical edits - Returns metadata tracking which specific items were translated Visual highlighting features: - Red border (Nord11) on re-translated items - Flash animation on first appearance - Applied to ingredient items, instruction steps, and group names - Clear visual feedback in translation approval workflow Technical changes: - Modified detectChangedFields() to return granular item-level changes - Added _translateIngredientsPartialWithMetadata() for metadata tracking - Added _translateInstructionsPartialWithMetadata() for metadata tracking - API returns translationMetadata alongside translatedRecipe - EditableIngredients/Instructions accept translationMetadata prop - CSS animation for highlight-flash effect --- src/lib/components/EditableIngredients.svelte | 29 ++ .../components/EditableInstructions.svelte | 28 ++ src/lib/components/TranslationApproval.svelte | 13 + .../edit/[name]/+page.svelte | 1 + src/routes/api/rezepte/translate/+server.ts | 16 +- src/utils/translation.ts | 465 +++++++++++++++++- 6 files changed, 533 insertions(+), 19 deletions(-) diff --git a/src/lib/components/EditableIngredients.svelte b/src/lib/components/EditableIngredients.svelte index 0001284..982514f 100644 --- a/src/lib/components/EditableIngredients.svelte +++ b/src/lib/components/EditableIngredients.svelte @@ -2,6 +2,7 @@ import { createEventDispatcher } from 'svelte'; export let ingredients: any[] = []; + export let translationMetadata: any[] | null | undefined = null; const dispatch = createEventDispatcher(); @@ -20,6 +21,16 @@ ingredients[groupIndex].list[itemIndex][field] = target.value; handleChange(); } + + // Check if a group name was re-translated + function isGroupNameTranslated(groupIndex: number): boolean { + return translationMetadata?.[groupIndex]?.nameTranslated ?? false; + } + + // Check if a specific item was re-translated + function isItemTranslated(groupIndex: number, itemIndex: number): boolean { + return translationMetadata?.[groupIndex]?.itemsTranslated?.[itemIndex] ?? false; + }
@@ -103,6 +129,7 @@ updateIngredientGroupName(groupIndex, e)} placeholder="Ingredient group name" @@ -119,6 +146,7 @@ updateIngredientItem(groupIndex, itemIndex, 'unit', e)} placeholder="Unit" @@ -126,6 +154,7 @@ updateIngredientItem(groupIndex, itemIndex, 'name', e)} placeholder="Ingredient name" diff --git a/src/lib/components/EditableInstructions.svelte b/src/lib/components/EditableInstructions.svelte index 935e706..6df683f 100644 --- a/src/lib/components/EditableInstructions.svelte +++ b/src/lib/components/EditableInstructions.svelte @@ -2,6 +2,7 @@ import { createEventDispatcher } from 'svelte'; export let instructions: any[] = []; + export let translationMetadata: any[] | null | undefined = null; const dispatch = createEventDispatcher(); @@ -20,6 +21,16 @@ instructions[groupIndex].steps[stepIndex] = target.value; handleChange(); } + + // Check if a group name was re-translated + function isGroupNameTranslated(groupIndex: number): boolean { + return translationMetadata?.[groupIndex]?.nameTranslated ?? false; + } + + // Check if a specific step was re-translated + function isStepTranslated(groupIndex: number, stepIndex: number): boolean { + return translationMetadata?.[groupIndex]?.stepsTranslated?.[stepIndex] ?? false; + }
@@ -121,6 +147,7 @@ updateInstructionGroupName(groupIndex, e)} placeholder="Instruction section name" @@ -129,6 +156,7 @@
{stepIndex + 1}