From 36a7fac39adcb72bf71450c97fe5ca6c4b71ffc9 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Fri, 26 Dec 2025 20:28:43 +0100 Subject: [PATCH] add English translation support for recipes with DeepL integration - Add embedded translations schema to Recipe model with English support - Create DeepL translation service with batch translation and change detection - Build translation approval UI with side-by-side editing for all recipe fields - Integrate translation workflow into add/edit pages with field comparison - Create complete English recipe routes at /recipes/* mirroring German structure - Add language switcher component with hreflang SEO tags - Support image loading from German short_name for English recipes - Add English API endpoints for all recipe filters (category, tag, icon, season) - Include layout with English navigation header for all recipe subroutes --- src/lib/components/Card.svelte | 16 +- src/lib/components/EditableIngredients.svelte | 137 ++++ .../components/EditableInstructions.svelte | 140 ++++ .../components/RecipeLanguageSwitcher.svelte | 127 ++++ src/lib/components/TranslationApproval.svelte | 677 ++++++++++++++++++ .../TranslationFieldComparison.svelte | 142 ++++ src/models/Recipe.ts | 46 ++ .../api/recipes/items/[name]/+server.ts | 96 +++ .../api/recipes/items/all_brief/+server.ts | 31 + .../items/category/[category]/+server.ts | 35 + .../api/recipes/items/icon/[icon]/+server.ts | 35 + .../items/in_season/[month]/+server.ts | 35 + .../api/recipes/items/tag/[tag]/+server.ts | 35 + src/routes/api/rezepte/translate/+server.ts | 88 +++ src/routes/recipes/+layout.server.ts | 7 + src/routes/recipes/+layout.svelte | 25 + src/routes/recipes/+page.server.ts | 22 + src/routes/recipes/+page.svelte | 50 ++ src/routes/recipes/[name]/+page.svelte | 352 +++++++++ src/routes/recipes/[name]/+page.ts | 102 +++ .../category/[category]/+page.server.ts | 19 + .../recipes/category/[category]/+page.svelte | 24 + .../recipes/icon/[icon]/+page.server.ts | 22 + src/routes/recipes/icon/[icon]/+page.svelte | 17 + .../recipes/season/[month]/+page.server.ts | 19 + .../recipes/season/[month]/+page.svelte | 18 + src/routes/recipes/tag/[tag]/+page.server.ts | 19 + src/routes/recipes/tag/[tag]/+page.svelte | 24 + src/routes/rezepte/[name]/+page.svelte | 16 + src/routes/rezepte/[name]/+page.ts | 10 +- src/routes/rezepte/add/+page.svelte | 111 ++- src/routes/rezepte/edit/[name]/+page.svelte | 137 +++- src/types/types.ts | 45 ++ src/utils/translation.ts | 426 +++++++++++ 34 files changed, 3061 insertions(+), 44 deletions(-) create mode 100644 src/lib/components/EditableIngredients.svelte create mode 100644 src/lib/components/EditableInstructions.svelte create mode 100644 src/lib/components/RecipeLanguageSwitcher.svelte create mode 100644 src/lib/components/TranslationApproval.svelte create mode 100644 src/lib/components/TranslationFieldComparison.svelte create mode 100644 src/routes/api/recipes/items/[name]/+server.ts create mode 100644 src/routes/api/recipes/items/all_brief/+server.ts create mode 100644 src/routes/api/recipes/items/category/[category]/+server.ts create mode 100644 src/routes/api/recipes/items/icon/[icon]/+server.ts create mode 100644 src/routes/api/recipes/items/in_season/[month]/+server.ts create mode 100644 src/routes/api/recipes/items/tag/[tag]/+server.ts create mode 100644 src/routes/api/rezepte/translate/+server.ts create mode 100644 src/routes/recipes/+layout.server.ts create mode 100644 src/routes/recipes/+layout.svelte create mode 100644 src/routes/recipes/+page.server.ts create mode 100644 src/routes/recipes/+page.svelte create mode 100644 src/routes/recipes/[name]/+page.svelte create mode 100644 src/routes/recipes/[name]/+page.ts create mode 100644 src/routes/recipes/category/[category]/+page.server.ts create mode 100644 src/routes/recipes/category/[category]/+page.svelte create mode 100644 src/routes/recipes/icon/[icon]/+page.server.ts create mode 100644 src/routes/recipes/icon/[icon]/+page.svelte create mode 100644 src/routes/recipes/season/[month]/+page.server.ts create mode 100644 src/routes/recipes/season/[month]/+page.svelte create mode 100644 src/routes/recipes/tag/[tag]/+page.server.ts create mode 100644 src/routes/recipes/tag/[tag]/+page.svelte create mode 100644 src/utils/translation.ts diff --git a/src/lib/components/Card.svelte b/src/lib/components/Card.svelte index 204fbbc..5a46d88 100644 --- a/src/lib/components/Card.svelte +++ b/src/lib/components/Card.svelte @@ -11,6 +11,8 @@ export let isFavorite = false; export let showFavoriteIndicator = false; // to manually override lazy loading for top cards export let loading_strat : "lazy" | "eager" | undefined; +// route prefix for language support (/rezepte or /recipes) +export let routePrefix = '/rezepte'; if(loading_strat === undefined){ loading_strat = "lazy" } @@ -27,7 +29,9 @@ onMount(() => { isloaded = document.querySelector("img")?.complete ? true : false }) -const img_name=recipe.short_name + ".webp?v=" + recipe.dateModified +// Use germanShortName for images if available (English recipes), otherwise use short_name (German recipes) +const imageShortName = recipe.germanShortName || recipe.short_name; +const img_name = imageShortName + ".webp?v=" + recipe.dateModified + +
+ {#each ingredients as group, groupIndex} +
+ updateIngredientGroupName(groupIndex, e)} + placeholder="Ingredient group name" + /> + {#each group.list as item, itemIndex} +
+ updateIngredientItem(groupIndex, itemIndex, 'amount', e)} + placeholder="Amt" + /> + updateIngredientItem(groupIndex, itemIndex, 'unit', e)} + placeholder="Unit" + /> + updateIngredientItem(groupIndex, itemIndex, 'name', e)} + placeholder="Ingredient name" + /> +
+ {/each} +
+ {/each} +
diff --git a/src/lib/components/EditableInstructions.svelte b/src/lib/components/EditableInstructions.svelte new file mode 100644 index 0000000..935e706 --- /dev/null +++ b/src/lib/components/EditableInstructions.svelte @@ -0,0 +1,140 @@ + + + + +
+ {#each instructions as group, groupIndex} +
+ updateInstructionGroupName(groupIndex, e)} + placeholder="Instruction section name" + /> + {#each group.steps as step, stepIndex} +
+
{stepIndex + 1}
+