diff --git a/src/lib/components/CreateIngredientList.svelte b/src/lib/components/CreateIngredientList.svelte index 88d119ab..265f4328 100644 --- a/src/lib/components/CreateIngredientList.svelte +++ b/src/lib/components/CreateIngredientList.svelte @@ -21,6 +21,66 @@ export function set_portions(){ portions.update((p) => portions_local) } +export let lang: 'de' | 'en' = 'de'; + +// Translation strings +const t = { + de: { + portions: 'Portionen:', + ingredients: 'Zutaten', + baseRecipe: 'Basisrezept', + unnamed: 'Unbenannt', + additionalIngredientsBefore: 'Zusätzliche Zutaten davor:', + additionalIngredientsAfter: 'Zusätzliche Zutaten danach:', + addIngredientBefore: 'Zutat davor hinzufügen', + addIngredientAfter: 'Zutat danach hinzufügen', + baseRecipeContent: '→ Inhalt vom Basisrezept wird hier eingefügt ←', + insertBaseRecipe: 'Basisrezept einfügen', + categoryOptional: 'Kategorie (optional)', + editIngredient: 'Zutat verändern', + renameCategory: 'Kategorie umbenennen', + confirmDeleteReference: 'Bist du dir sicher, dass du diese Referenz löschen möchtest?', + confirmDeleteList: 'Bist du dir sicher, dass du diese Liste löschen möchtest? Alle Zutaten der Liste werden hiermit auch gelöscht.', + empty: 'Leer', + editHeading: 'Überschrift bearbeiten', + removeList: 'Liste entfernen', + editIngredientAria: 'Zutat bearbeiten', + removeIngredientAria: 'Zutat entfernen', + moveUpAria: 'Nach oben verschieben', + moveDownAria: 'Nach unten verschieben', + moveReferenceUpAria: 'Referenz nach oben verschieben', + moveReferenceDownAria: 'Referenz nach unten verschieben', + removeReferenceAria: 'Referenz entfernen' + }, + en: { + portions: 'Portions:', + ingredients: 'Ingredients', + baseRecipe: 'Base Recipe', + unnamed: 'Unnamed', + additionalIngredientsBefore: 'Additional ingredients before:', + additionalIngredientsAfter: 'Additional ingredients after:', + addIngredientBefore: 'Add ingredient before', + addIngredientAfter: 'Add ingredient after', + baseRecipeContent: '→ Base recipe content will be inserted here ←', + insertBaseRecipe: 'Insert Base Recipe', + categoryOptional: 'Category (optional)', + editIngredient: 'Edit Ingredient', + renameCategory: 'Rename Category', + confirmDeleteReference: 'Are you sure you want to delete this reference?', + confirmDeleteList: 'Are you sure you want to delete this list? All ingredients in the list will also be deleted.', + empty: 'Empty', + editHeading: 'Edit heading', + removeList: 'Remove list', + editIngredientAria: 'Edit ingredient', + removeIngredientAria: 'Remove ingredient', + moveUpAria: 'Move up', + moveDownAria: 'Move down', + moveReferenceUpAria: 'Move reference up', + moveReferenceDownAria: 'Move reference down', + removeReferenceAria: 'Remove reference' + } +}; + export let ingredients let new_ingredient = { @@ -80,7 +140,7 @@ function handleSelect(recipe: any, options: any) { } export function removeReference(list_index: number) { - const confirmed = confirm("Bist du dir sicher, dass du diese Referenz löschen möchtest?"); + const confirmed = confirm(t[lang].confirmDeleteReference); if (confirmed) { ingredients.splice(list_index, 1); ingredients = ingredients; @@ -208,7 +268,7 @@ export function add_new_ingredient(){ } export function remove_list(list_index){ if(ingredients[list_index].list.length > 1){ - const response = confirm("Bist du dir sicher, dass du diese Liste löschen möchtest? Alle Zutaten der Liste werden hiermit auch gelöscht."); + const response = confirm(t[lang].confirmDeleteList); if(!response){ return } @@ -669,28 +729,28 @@ h3{
-

Portionen:

+

{t[lang].portions}

-

Zutaten

+

{t[lang].ingredients}

{#each ingredients as list, list_index} {#if list.type === 'reference'}
- -
- 📋 Basisrezept: {list.name || 'Unbenannt'} + 📋 {t[lang].baseRecipe}: {list.name || t[lang].unnamed}
-
@@ -698,7 +758,7 @@ h3{ {#if list.itemsBefore && list.itemsBefore.length > 0} -

Zusätzliche Zutaten davor:

+

{t[lang].additionalIngredientsBefore}

{#each list.itemsBefore as item, item_index}
@@ -711,10 +771,10 @@ h3{ {@html item.name}
- -
@@ -722,20 +782,20 @@ h3{
{/if}
- → Inhalt vom Basisrezept wird hier eingefügt ← + {t[lang].baseRecipeContent}
{#if list.itemsAfter && list.itemsAfter.length > 0} -

Zusätzliche Zutaten danach:

+

{t[lang].additionalIngredientsAfter}

{#each list.itemsAfter as item, item_index}
@@ -748,10 +808,10 @@ h3{ {@html item.name}
- -
@@ -775,23 +835,23 @@ h3{ {#if list.name } {list.name} {:else} - Leer + {t[lang].empty} {/if}
- -
{#each list.list as ingredient, ingredient_index (ingredient_index)}
- -
@@ -801,9 +861,9 @@ h3{ -
-
+
{/each}
{/if} @@ -812,12 +872,12 @@ h3{
- do_on_key(event, 'Enter', false, add_new_ingredient)}> + do_on_key(event, 'Enter', false, add_new_ingredient)}>
do_on_key(event, 'Enter', false, add_new_ingredient)}> do_on_key(event, 'Enter', false, add_new_ingredient)}> @@ -828,9 +888,9 @@ h3{
-

Zutat verändern

+

{t[lang].editIngredient}

- +
do_on_key(event, 'Enter', false, edit_ingredient_and_close_modal)}> do_on_key(event, 'Enter', false, edit_ingredient_and_close_modal)}> do_on_key(event, 'Enter', false, edit_ingredient_and_close_modal)}> @@ -843,7 +903,7 @@ h3{
-

Kategorie umbenennen

+

{t[lang].renameCategory}

do_on_key(event, 'Enter', false, edit_subheading_and_close_modal)} > -
- 📋 Basisrezept: {list.name || 'Unbenannt'} + 📋 {t[lang].baseRecipe}: {list.name || t[lang].unnamed}
-
@@ -733,7 +809,7 @@ h3{ {#if list.stepsBefore && list.stepsBefore.length > 0} -

Zusätzliche Schritte davor:

+

{t[lang].additionalStepsBefore}

    {#each list.stepsBefore as step, step_index}
  1. @@ -745,10 +821,10 @@ h3{ {@html step}
    - -
    @@ -758,20 +834,20 @@ h3{
{/if}
- → Inhalt vom Basisrezept wird hier eingefügt ← + {t[lang].baseRecipeContent}
{#if list.stepsAfter && list.stepsAfter.length > 0} -

Zusätzliche Schritte danach:

+

{t[lang].additionalStepsAfter}

    {#each list.stepsAfter as step, step_index}
  1. @@ -783,10 +859,10 @@ h3{ {@html step}
    - -
    @@ -800,10 +876,10 @@ h3{

    - -
    @@ -811,12 +887,12 @@ h3{ {#if list.name} {list.name} {:else} - Leer + {t[lang].empty} {/if} - -

    @@ -825,10 +901,10 @@ h3{
  2. - -
    @@ -836,10 +912,10 @@ h3{ -
    -
@@ -852,12 +928,12 @@ h3{
- do_on_key(event, 'Enter', false , add_new_step)} > + do_on_key(event, 'Enter', false , add_new_step)} >

do_on_key(event, 'Enter', true , add_new_step)}>

-

Schritt verändern

+

{t[lang].editStep}

- do_on_key(event, 'Enter', false , edit_step_and_close_modal)}> + do_on_key(event, 'Enter', false , edit_step_and_close_modal)}>

do_on_key(event, 'Enter', true , edit_step_and_close_modal)}>

-

Kategorie umbenennen

+

{t[lang].renameCategory}

do_on_key(event, 'Enter', false, edit_subheading_steps_and_close_modal)}>
{/if} + {#if checkingBaseRecipes} +
+

Checking if referenced base recipes are translated...

+
+ {/if} + + {#if untranslatedBaseRecipes.length > 0} +
+

⚠️ Base Recipes Need Translation

+

The following base recipes need to be translated to English before you can translate this recipe:

+ +

+ +

+
+ {/if} + {#if translationState === 'idle'} -
-

Click "Auto-translate" to generate English translation using DeepL.

-
- - -
+
+ Preview (Not yet translated) +

The structure below shows what will be translated. Click "Auto-translate" to generate English translation.

- {:else if translationState === 'translating'} + {/if} + + {#if translationState === 'translating'}

Translating recipe...

+ {/if} - {:else if translationState === 'preview' || translationState === 'approved'} + {#if translationState === 'idle' || translationState === 'preview' || translationState === 'approved'}

🇬🇧 English Translation

@@ -575,12 +748,12 @@ button:disabled {
{#if editableEnglish?.ingredients} - + {/if}
{#if editableEnglish?.instructions && englishAddInfo} - + {/if}
@@ -602,7 +775,21 @@ button:disabled {
- {#if translationState !== 'approved'} + {#if translationState === 'idle'} + + + + {:else if translationState !== 'approved'} diff --git a/src/utils/translation.ts b/src/utils/translation.ts index ea09711a..bbfa8c89 100644 --- a/src/utils/translation.ts +++ b/src/utils/translation.ts @@ -931,6 +931,98 @@ class DeepLTranslationService { const group = newIngredients[i]; const existingGroup = existingTranslatedIngredients[i]; + // Handle base recipe references + if (group.type === 'reference') { + // If entire group doesn't exist in old version or no change info, translate all reference fields + if (!changeInfo || !existingGroup) { + const textsToTranslate: string[] = [group.labelOverride || '']; + (group.itemsBefore || []).forEach((item: any) => { + textsToTranslate.push(item.name || ''); + textsToTranslate.push(item.unit || ''); + }); + (group.itemsAfter || []).forEach((item: any) => { + textsToTranslate.push(item.name || ''); + textsToTranslate.push(item.unit || ''); + }); + + const translated = await this.translateBatch(textsToTranslate); + let index = 0; + + result.push({ + type: 'reference', + name: group.name, + baseRecipeRef: group.baseRecipeRef, + includeIngredients: group.includeIngredients, + showLabel: group.showLabel, + labelOverride: translated[index++], + itemsBefore: (group.itemsBefore || []).map((item: any) => ({ + name: translated[index++], + unit: translated[index++], + amount: item.amount, + })), + itemsAfter: (group.itemsAfter || []).map((item: any) => ({ + name: translated[index++], + unit: translated[index++], + amount: item.amount, + })) + }); + continue; + } + + // Reference changed - translate changed fields + const translatedRef: any = { + type: 'reference', + name: group.name, + baseRecipeRef: group.baseRecipeRef, + includeIngredients: group.includeIngredients, + showLabel: group.showLabel, + labelOverride: existingGroup.labelOverride, + itemsBefore: existingGroup.itemsBefore || [], + itemsAfter: existingGroup.itemsAfter || [] + }; + + // Translate labelOverride if changed + if (changeInfo.nameChanged) { + translatedRef.labelOverride = await this.translateText(group.labelOverride || ''); + } + + // Translate itemsBefore if changed + if (JSON.stringify(group.itemsBefore) !== JSON.stringify(existingGroup.itemsBefore)) { + const textsToTranslate: string[] = []; + (group.itemsBefore || []).forEach((item: any) => { + textsToTranslate.push(item.name || ''); + textsToTranslate.push(item.unit || ''); + }); + const translated = await this.translateBatch(textsToTranslate); + let index = 0; + translatedRef.itemsBefore = (group.itemsBefore || []).map((item: any) => ({ + name: translated[index++], + unit: translated[index++], + amount: item.amount, + })); + } + + // Translate itemsAfter if changed + if (JSON.stringify(group.itemsAfter) !== JSON.stringify(existingGroup.itemsAfter)) { + const textsToTranslate: string[] = []; + (group.itemsAfter || []).forEach((item: any) => { + textsToTranslate.push(item.name || ''); + textsToTranslate.push(item.unit || ''); + }); + const translated = await this.translateBatch(textsToTranslate); + let index = 0; + translatedRef.itemsAfter = (group.itemsAfter || []).map((item: any) => ({ + name: translated[index++], + unit: translated[index++], + amount: item.amount, + })); + } + + result.push(translatedRef); + continue; + } + + // Handle regular ingredient sections // If entire group doesn't exist in old version or no change info, translate everything if (!changeInfo || !existingGroup) { const textsToTranslate: string[] = [group.name || '']; @@ -1068,6 +1160,76 @@ class DeepLTranslationService { const group = newInstructions[i]; const existingGroup = existingTranslatedInstructions[i]; + // Handle base recipe references + if (group.type === 'reference') { + // If entire group doesn't exist in old version or no change info, translate all reference fields + if (!changeInfo || !existingGroup) { + const textsToTranslate: string[] = [group.labelOverride || '']; + (group.stepsBefore || []).forEach((step: string) => { + textsToTranslate.push(step || ''); + }); + (group.stepsAfter || []).forEach((step: string) => { + textsToTranslate.push(step || ''); + }); + + const translated = await this.translateBatch(textsToTranslate); + let index = 0; + + result.push({ + type: 'reference', + name: group.name, + baseRecipeRef: group.baseRecipeRef, + includeInstructions: group.includeInstructions, + showLabel: group.showLabel, + labelOverride: translated[index++], + stepsBefore: (group.stepsBefore || []).map(() => translated[index++]), + stepsAfter: (group.stepsAfter || []).map(() => translated[index++]) + }); + continue; + } + + // Reference changed - translate changed fields + const translatedRef: any = { + type: 'reference', + name: group.name, + baseRecipeRef: group.baseRecipeRef, + includeInstructions: group.includeInstructions, + showLabel: group.showLabel, + labelOverride: existingGroup.labelOverride, + stepsBefore: existingGroup.stepsBefore || [], + stepsAfter: existingGroup.stepsAfter || [] + }; + + // Translate labelOverride if changed + if (changeInfo.nameChanged) { + translatedRef.labelOverride = await this.translateText(group.labelOverride || ''); + } + + // Translate stepsBefore if changed + if (JSON.stringify(group.stepsBefore) !== JSON.stringify(existingGroup.stepsBefore)) { + const textsToTranslate: string[] = []; + (group.stepsBefore || []).forEach((step: string) => { + textsToTranslate.push(step || ''); + }); + const translated = await this.translateBatch(textsToTranslate); + translatedRef.stepsBefore = translated; + } + + // Translate stepsAfter if changed + if (JSON.stringify(group.stepsAfter) !== JSON.stringify(existingGroup.stepsAfter)) { + const textsToTranslate: string[] = []; + (group.stepsAfter || []).forEach((step: string) => { + textsToTranslate.push(step || ''); + }); + const translated = await this.translateBatch(textsToTranslate); + translatedRef.stepsAfter = translated; + } + + result.push(translatedRef); + continue; + } + + // Handle regular instruction sections // If entire group doesn't exist in old version or no change info, translate everything if (!changeInfo || !existingGroup) { const textsToTranslate: string[] = [group.name || ''];