feat: German translations for TheMealDB recipes + recipe detail view

- Add GermanTranslator service with 200+ ingredient and meal name mappings
- Translate titles and ingredients when fetching from TheMealDB
- Add click-to-view recipe detail modal on week plan meal cards
- Import RecipeDetail component in WeekPlanView

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 07:20:08 +00:00
parent 2004883e02
commit 165acc9d22
4 changed files with 203 additions and 3 deletions

View File

@@ -0,0 +1,188 @@
using MealPlanner.Models;
namespace MealPlanner.Services;
public static class GermanTranslator
{
private static readonly Dictionary<string, string> MealNames = new(StringComparer.OrdinalIgnoreCase)
{
// Common TheMealDB meal titles
{"Beef", "Rind"}, {"Chicken", "Hähnchen"}, {"Pork", "Schwein"}, {"Lamb", "Lamm"},
{"Fish", "Fisch"}, {"Salmon", "Lachs"}, {"Tuna", "Thunfisch"}, {"Shrimp", "Garnelen"},
{"Prawn", "Garnelen"}, {"Turkey", "Truthahn"}, {"Duck", "Ente"}, {"Veal", "Kalb"},
{"Steak", "Steak"}, {"Roast", "Braten"}, {"Grilled", "Gegrilltes"}, {"Fried", "Gebratenes"},
{"Baked", "Gebackenes"}, {"Braised", "Geschmortes"}, {"Smoked", "Geräuchertes"},
{"Soup", "Suppe"}, {"Stew", "Eintopf"}, {"Salad", "Salat"}, {"Pie", "Pastete"},
{"Pasta", "Pasta"}, {"Noodles", "Nudeln"}, {"Rice", "Reis"}, {"Bread", "Brot"},
{"Cake", "Kuchen"}, {"Pancake", "Pfannkuchen"}, {"Pancakes", "Pfannkuchen"},
{"Curry", "Curry"}, {"Casserole", "Auflauf"}, {"Sandwich", "Sandwich"},
{"Wrap", "Wrap"}, {"Tacos", "Tacos"}, {"Burrito", "Burrito"},
{"Spaghetti", "Spaghetti"}, {"Lasagne", "Lasagne"}, {"Risotto", "Risotto"},
{"Pizza", "Pizza"}, {"Burger", "Burger"}, {"Meatballs", "Fleischbällchen"},
{"Teriyaki", "Teriyaki"}, {"Sweet", "Süß"}, {"Spicy", "Scharf"},
{"Creamy", "Cremig"}, {"Crispy", "Knusprig"}, {"Honey", "Honig"},
{"Garlic", "Knoblauch"}, {"Lemon", "Zitronen"}, {"Mushroom", "Pilz"},
{"Tomato", "Tomaten"}, {"Potato", "Kartoffel"}, {"Potatoes", "Kartoffeln"},
{"Cheese", "Käse"}, {"Cream", "Sahne"}, {"Butter", "Butter"},
{"Chocolate", "Schokoladen"}, {"Caramel", "Karamell"}, {"Vanilla", "Vanille"},
{"Apple", "Apfel"}, {"Orange", "Orangen"}, {"Banana", "Bananen"},
{"Beans", "Bohnen"}, {"Lentil", "Linsen"}, {"Lentils", "Linsen"},
{"Vegetable", "Gemüse"}, {"Vegetables", "Gemüse"},
{"with", "mit"}, {"and", "und"}, {"in", "in"}, {"on", "auf"},
{"Sauce", "Soße"}, {"Gravy", "Bratensaft"}, {"Dressing", "Dressing"},
{"Stuffed", "Gefüllte"}, {"Roasted", "Geröstete"}, {"Sautéed", "Sautierte"},
{"Chili", "Chili"}, {"Ginger", "Ingwer"}, {"Coconut", "Kokos"},
{"Spinach", "Spinat"}, {"Broccoli", "Brokkoli"}, {"Carrot", "Karotten"},
{"Onion", "Zwiebel"}, {"Pepper", "Pfeffer"}, {"Peppers", "Paprika"},
{"Corn", "Mais"}, {"Peas", "Erbsen"}, {"Cabbage", "Kohl"},
{"Eggplant", "Aubergine"}, {"Zucchini", "Zucchini"}, {"Celery", "Sellerie"},
{"Parsley", "Petersilie"}, {"Basil", "Basilikum"}, {"Thyme", "Thymian"},
{"Oregano", "Oregano"}, {"Rosemary", "Rosmarin"}, {"Cinnamon", "Zimt"},
{"Nutmeg", "Muskatnuss"}, {"Cumin", "Kreuzkümmel"}, {"Paprika", "Paprika"},
{"Soy", "Soja"}, {"Sesame", "Sesam"}, {"Peanut", "Erdnuss"},
{"Almond", "Mandel"}, {"Walnut", "Walnuss"},
{"Egg", "Ei"}, {"Eggs", "Eier"}, {"Milk", "Milch"}, {"Yogurt", "Joghurt"},
{"Oil", "Öl"}, {"Olive", "Oliven"}, {"Vinegar", "Essig"},
{"Sugar", "Zucker"}, {"Salt", "Salz"}, {"Flour", "Mehl"},
};
private static readonly Dictionary<string, string> Ingredients = new(StringComparer.OrdinalIgnoreCase)
{
// All from above plus more specific ingredient names
{"Beef", "Rindfleisch"}, {"Chicken", "Hähnchen"}, {"Chicken Breast", "Hähnchenbrust"},
{"Chicken Thighs", "Hähnchenschenkel"}, {"Chicken Stock", "Hühnerbrühe"},
{"Minced Beef", "Rinderhackfleisch"}, {"Ground Beef", "Rinderhackfleisch"},
{"Pork", "Schweinefleisch"}, {"Bacon", "Speck"}, {"Sausage", "Wurst"},
{"Lamb", "Lammfleisch"}, {"Fish", "Fisch"}, {"Salmon", "Lachs"},
{"Tuna", "Thunfisch"}, {"Shrimp", "Garnelen"}, {"Prawns", "Garnelen"},
{"Butter", "Butter"}, {"Olive Oil", "Olivenöl"}, {"Vegetable Oil", "Pflanzenöl"},
{"Sesame Oil", "Sesamöl"}, {"Coconut Oil", "Kokosöl"},
{"Milk", "Milch"}, {"Cream", "Sahne"}, {"Sour Cream", "Saure Sahne"},
{"Heavy Cream", "Schlagsahne"}, {"Double Cream", "Sahne"},
{"Cheese", "Käse"}, {"Parmesan", "Parmesan"}, {"Cheddar", "Cheddar"},
{"Mozzarella", "Mozzarella"}, {"Feta", "Feta"}, {"Cream Cheese", "Frischkäse"},
{"Yogurt", "Joghurt"}, {"Yoghurt", "Joghurt"}, {"Egg", "Ei"}, {"Eggs", "Eier"},
{"Egg Yolks", "Eigelb"}, {"Egg Whites", "Eiweiß"},
{"Onion", "Zwiebel"}, {"Onions", "Zwiebeln"}, {"Red Onion", "Rote Zwiebel"},
{"Garlic", "Knoblauch"}, {"Garlic Clove", "Knoblauchzehe"},
{"Garlic Cloves", "Knoblauchzehen"}, {"Garlic Minced", "Knoblauch gehackt"},
{"Ginger", "Ingwer"}, {"Celery", "Sellerie"}, {"Carrot", "Karotte"},
{"Carrots", "Karotten"}, {"Potato", "Kartoffel"}, {"Potatoes", "Kartoffeln"},
{"Sweet Potato", "Süßkartoffel"}, {"Tomato", "Tomate"}, {"Tomatoes", "Tomaten"},
{"Cherry Tomatoes", "Kirschtomaten"}, {"Tomato Paste", "Tomatenmark"},
{"Tomato Puree", "Tomatenmark"}, {"Chopped Tomatoes", "Gehackte Tomaten"},
{"Tomato Sauce", "Tomatensoße"}, {"Passata", "Passata"},
{"Pepper", "Pfeffer"}, {"Bell Pepper", "Paprika"}, {"Red Pepper", "Rote Paprika"},
{"Green Pepper", "Grüne Paprika"}, {"Chilli", "Chili"}, {"Chili", "Chili"},
{"Chilli Powder", "Chilipulver"}, {"Chili Flakes", "Chiliflocken"},
{"Mushroom", "Pilze"}, {"Mushrooms", "Pilze"},
{"Spinach", "Spinat"}, {"Broccoli", "Brokkoli"}, {"Cauliflower", "Blumenkohl"},
{"Cabbage", "Kohl"}, {"Lettuce", "Salat"}, {"Cucumber", "Gurke"},
{"Zucchini", "Zucchini"}, {"Courgette", "Zucchini"}, {"Eggplant", "Aubergine"},
{"Aubergine", "Aubergine"}, {"Corn", "Mais"}, {"Peas", "Erbsen"},
{"Green Beans", "Grüne Bohnen"}, {"Kidney Beans", "Kidneybohnen"},
{"Chickpeas", "Kichererbsen"}, {"Lentils", "Linsen"},
{"Rice", "Reis"}, {"Basmati Rice", "Basmatireis"}, {"Long Grain Rice", "Langkornreis"},
{"Pasta", "Nudeln"}, {"Spaghetti", "Spaghetti"}, {"Penne", "Penne"},
{"Noodles", "Nudeln"}, {"Lasagne Sheets", "Lasagneplatten"},
{"Bread", "Brot"}, {"Breadcrumbs", "Semmelbrösel"}, {"Flour", "Mehl"},
{"Plain Flour", "Mehl"}, {"Self Raising Flour", "Mehl mit Backpulver"},
{"Cornflour", "Speisestärke"}, {"Cornstarch", "Speisestärke"},
{"Baking Powder", "Backpulver"}, {"Baking Soda", "Natron"},
{"Sugar", "Zucker"}, {"Brown Sugar", "Brauner Zucker"}, {"Caster Sugar", "Feiner Zucker"},
{"Icing Sugar", "Puderzucker"}, {"Honey", "Honig"}, {"Maple Syrup", "Ahornsirup"},
{"Salt", "Salz"}, {"Black Pepper", "Schwarzer Pfeffer"},
{"Cumin", "Kreuzkümmel"}, {"Paprika", "Paprika"}, {"Turmeric", "Kurkuma"},
{"Coriander", "Koriander"}, {"Cinnamon", "Zimt"}, {"Nutmeg", "Muskatnuss"},
{"Oregano", "Oregano"}, {"Basil", "Basilikum"}, {"Thyme", "Thymian"},
{"Rosemary", "Rosmarin"}, {"Parsley", "Petersilie"}, {"Bay Leaf", "Lorbeerblatt"},
{"Bay Leaves", "Lorbeerblätter"}, {"Mint", "Minze"}, {"Dill", "Dill"},
{"Chives", "Schnittlauch"}, {"Cilantro", "Koriander"},
{"Soy Sauce", "Sojasoße"}, {"Worcestershire Sauce", "Worcestersoße"},
{"Fish Sauce", "Fischsoße"}, {"Hot Sauce", "Scharfe Soße"},
{"Vinegar", "Essig"}, {"Balsamic Vinegar", "Balsamico-Essig"},
{"Red Wine Vinegar", "Rotweinessig"}, {"White Wine Vinegar", "Weißweinessig"},
{"Lemon", "Zitrone"}, {"Lemon Juice", "Zitronensaft"}, {"Lime", "Limette"},
{"Lime Juice", "Limettensaft"}, {"Orange", "Orange"}, {"Orange Juice", "Orangensaft"},
{"Wine", "Wein"}, {"Red Wine", "Rotwein"}, {"White Wine", "Weißwein"},
{"Beer", "Bier"}, {"Coconut Milk", "Kokosmilch"}, {"Stock", "Brühe"},
{"Beef Stock", "Rinderbrühe"}, {"Vegetable Stock", "Gemüsebrühe"},
{"Water", "Wasser"}, {"Ice", "Eis"},
{"Chocolate", "Schokolade"}, {"Dark Chocolate", "Zartbitterschokolade"},
{"Cocoa", "Kakao"}, {"Vanilla", "Vanille"}, {"Vanilla Extract", "Vanilleextrakt"},
{"Apple", "Apfel"}, {"Banana", "Banane"}, {"Strawberries", "Erdbeeren"},
{"Blueberries", "Blaubeeren"}, {"Raisins", "Rosinen"},
{"Almonds", "Mandeln"}, {"Walnuts", "Walnüsse"}, {"Peanuts", "Erdnüsse"},
{"Peanut Butter", "Erdnussbutter"}, {"Pine Nuts", "Pinienkerne"},
{"Sesame Seeds", "Sesamkörner"}, {"Sunflower Oil", "Sonnenblumenöl"},
{"Spring Onions", "Frühlingszwiebeln"}, {"Leek", "Lauch"},
{"Fennel", "Fenchel"}, {"Avocado", "Avocado"}, {"Olives", "Oliven"},
{"Capers", "Kapern"}, {"Anchovies", "Sardellen"},
{"Tortilla", "Tortilla"}, {"Pita", "Fladenbrot"}, {"Naan", "Naan"},
{"Tofu", "Tofu"}, {"Coconut", "Kokosnuss"}, {"Coconut Cream", "Kokoscreme"},
{"Curry Powder", "Currypulver"}, {"Garam Masala", "Garam Masala"},
{"Mustard", "Senf"}, {"Dijon Mustard", "Dijon-Senf"}, {"Ketchup", "Ketchup"},
{"Mayonnaise", "Mayonnaise"}, {"Sriracha", "Sriracha"},
};
public static string TranslateTitle(string title)
{
// Word-by-word translation for meal titles
var words = title.Split(' ');
var translated = new List<string>();
int i = 0;
while (i < words.Length)
{
// Try two-word match first
if (i + 1 < words.Length)
{
var twoWord = $"{words[i]} {words[i + 1]}";
if (MealNames.TryGetValue(twoWord, out var tw))
{
translated.Add(tw);
i += 2;
continue;
}
}
if (MealNames.TryGetValue(words[i], out var single))
translated.Add(single);
else
translated.Add(words[i]); // Keep original if no translation
i++;
}
return string.Join(' ', translated);
}
public static string TranslateIngredient(string name)
{
// Try exact match first
if (Ingredients.TryGetValue(name, out var exact)) return exact;
// Try two-word, three-word combos from start
var words = name.Split(' ');
if (words.Length >= 3)
{
var three = $"{words[0]} {words[1]} {words[2]}";
if (Ingredients.TryGetValue(three, out var tw3)) return tw3;
}
if (words.Length >= 2)
{
var two = $"{words[0]} {words[1]}";
if (Ingredients.TryGetValue(two, out var tw2)) return tw2;
}
// Single word
if (words.Length >= 1 && Ingredients.TryGetValue(words[0], out var sw)) return sw;
return name; // Keep original
}
public static Recipe TranslateRecipe(Recipe recipe)
{
recipe.Title = TranslateTitle(recipe.Title);
foreach (var ing in recipe.Ingredients)
{
ing.Name = TranslateIngredient(ing.Name);
}
return recipe;
}
}