Pārlūkot izejas kodu

feat: 首页文案处理完毕

victor.zhou 4 mēneši atpakaļ
vecāks
revīzija
24abcb0b7f
4 mainītis faili ar 205 papildinājumiem un 99 dzēšanām
  1. 3 3
      index.html
  2. 48 96
      src/components/RecipeCard.vue
  3. 77 0
      src/i18n/locales/en.json
  4. 77 0
      src/i18n/locales/zh.json

+ 3 - 3
index.html

@@ -6,12 +6,12 @@
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 
         <!-- 基础SEO -->
-        <title>recipeMuse</title>
+        <title>Recipe Muse</title>
         <meta
             name="description"
-            content="配方缪斯是专业的AI智能菜谱生成平台,汇聚中华八大菜系厨艺大师,根据您的食材智能生成个性化菜谱。支持营养分析、效果图生成,让烹饪变得简单有趣。"
+            content="Recipe Muse is a professional AI-powered recipe generation platform, bringing together master chefs from China's Eight Great Cuisines to intelligently create personalized recipes based on your ingredients. Featuring nutrition analysis and dish visualization, making cooking simple and enjoyable."
         />
-        <meta name="keywords" content="AI菜谱,智能菜谱生成,八大菜系,川菜,粤菜,苏菜,鲁菜,浙菜,湘菜,闽菜,徽菜,营养分析,烹饪指导,食材搭配,美食推荐" />
+        <meta name="keywords" content="AI recipes,intelligent recipe generation,Chinese Eight Great Cuisines,Sichuan cuisine,Cantonese cuisine,Jiangsu cuisine,Shandong cuisine,Zhejiang cuisine,Hunan cuisine,Fujian cuisine,Anhui cuisine,nutrition analysis,cooking guidance,ingredient pairing,food recommendations" />
         <meta name="author" content="liuziting" />
         <meta name="robots" content="index, follow" />
         <meta name="googlebot" content="index, follow" />

+ 48 - 96
src/components/RecipeCard.vue

@@ -32,7 +32,7 @@
         <div class="p-2 md:p-6">
             <!-- 食材列表 -->
             <div class="mb-4">
-                <h4 class="text-sm font-bold text-dark-800 mb-2 flex items-center gap-1">🥬 所需食材</h4>
+                <h4 class="text-sm font-bold text-dark-800 mb-2 flex items-center gap-1">🥬 {{ t('recipeCard.sections.ingredients') }}</h4>
                 <div class="flex flex-wrap gap-1">
                     <span v-for="ingredient in recipe.ingredients" :key="ingredient" class="bg-yellow-400 text-dark-800 px-2 py-1 rounded text-xs font-medium border border-black">
                         {{ ingredient }}
@@ -43,9 +43,9 @@
             <!-- 制作步骤预览 -->
             <div class="mb-4">
                 <div class="flex items-center justify-between mb-2">
-                    <h4 class="text-sm font-bold text-dark-800 flex items-center gap-1">📝 制作步骤</h4>
+                    <h4 class="text-sm font-bold text-dark-800 flex items-center gap-1">📝 {{ t('recipeCard.sections.steps') }}</h4>
                     <button @click="toggleExpanded" class="bg-gray-100 hover:bg-gray-200 text-dark-800 text-xs px-2 py-1 rounded border border-black transition-colors">
-                        {{ isExpanded ? '收起' : '展开' }}
+                        {{ isExpanded ? t('recipeCard.buttons.collapse') : t('recipeCard.buttons.expand') }}
                     </button>
                 </div>
 
@@ -58,7 +58,7 @@
                         <p class="text-dark-700 text-xs line-clamp-2">{{ step.description }}</p>
                     </div>
                     <div v-if="recipe.steps.length > 3" class="text-center py-1">
-                        <span class="text-gray-500 text-xs">还有 {{ recipe.steps.length - 3 }} 个步骤...</span>
+                        <span class="text-gray-500 text-xs">{{ t('recipeCard.stepsRemaining', { count: recipe.steps.length - 3 }) }}</span>
                     </div>
                 </div>
 
@@ -81,7 +81,7 @@
 
             <!-- 烹饪技巧 -->
             <div v-if="recipe.tips && recipe.tips.length > 0 && isExpanded" class="mb-4">
-                <h4 class="text-sm font-bold text-dark-800 mb-2 flex items-center gap-1">💡 烹饪技巧</h4>
+                <h4 class="text-sm font-bold text-dark-800 mb-2 flex items-center gap-1">💡 {{ t('recipeCard.sections.tips') }}</h4>
                 <div class="bg-yellow-100 border-l-4 border-yellow-400 p-3 rounded-r">
                     <ul class="space-y-1">
                         <li v-for="tip in recipe.tips" :key="tip" class="flex items-start gap-2 text-dark-700">
@@ -94,11 +94,11 @@
 
             <!-- 营养分析 -->
             <div v-if="isExpanded" class="mb-4">
-                <h4 class="text-sm font-bold text-dark-800 mb-3 flex items-center gap-1">📊 营养分析</h4>
+                <h4 class="text-sm font-bold text-dark-800 mb-3 flex items-center gap-1">📊 {{ t('recipeCard.sections.nutrition') }}</h4>
 
                 <div v-if="isFetchingNutrition" class="bg-gray-50 border-2 border-gray-300 rounded-lg p-6 text-center">
                     <div class="w-12 h-12 border-4 border-gray-300 border-t-green-500 rounded-full animate-spin mx-auto mb-3"></div>
-                    <h5 class="text-sm font-bold text-dark-800 mb-1">营养师正在分析中...</h5>
+                    <h5 class="text-sm font-bold text-dark-800 mb-1">{{ t('recipeCard.loading.nutrition.title') }}</h5>
                     <p class="text-gray-600 text-xs">{{ nutritionLoadingText }}</p>
                 </div>
 
@@ -111,7 +111,7 @@
                 <!-- 营养分析空状态 - 包含获取按钮 -->
                 <div v-else-if="!isFetchingNutrition" class="bg-gray-100 border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:bg-gray-50 transition-colors">
                     <div class="text-gray-400 text-2xl mb-3">🥗</div>
-                    <p class="text-gray-500 text-xs mb-4">暂无营养分析数据</p>
+                    <p class="text-gray-500 text-xs mb-4">{{ t('recipeCard.emptyState.nutrition') }}</p>
                     <button
                         @click="fetchNutritionAnalysis"
                         :disabled="isFetchingNutrition"
@@ -120,31 +120,21 @@
                         <span class="flex items-center gap-1">
                             <template v-if="isFetchingNutrition">
                                 <div class="animate-spin w-3 h-3 border border-white border-t-transparent rounded-full"></div>
-                                获取中...
+                                {{ t('recipeCard.buttons.fetching') }}
                             </template>
-                            <template v-else> ✨ 获取营养分析 </template>
+                            <template v-else> ✨ {{ t('recipeCard.buttons.getNutrition') }} </template>
                         </span>
                     </button>
                 </div>
-
-                <!-- 重新获取按钮 - 当已有数据时显示 -->
-                <!-- <div v-if="recipe.nutritionAnalysis && !isFetchingNutrition" class="mt-3 text-center">
-                    <button
-                        @click="fetchNutritionAnalysis"
-                        class="bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded text-xs font-medium border border-black transition-all duration-200"
-                    >
-                        🔄 重新获取
-                    </button>
-                </div> -->
             </div>
 
             <!-- 饮品搭配 -->
             <div v-if="isExpanded" class="mb-4">
-                <h4 class="text-sm font-bold text-dark-800 mb-3 flex items-center gap-1">🥤 饮品搭配</h4>
+                <h4 class="text-sm font-bold text-dark-800 mb-3 flex items-center gap-1">🥤 {{ t('recipeCard.sections.beverage') }}</h4>
 
                 <div v-if="isFetchingWine" class="bg-gray-50 border-2 border-gray-300 rounded-lg p-6 text-center">
                     <div class="w-12 h-12 border-4 border-gray-300 border-t-purple-500 rounded-full animate-spin mx-auto mb-3"></div>
-                    <h5 class="text-sm font-bold text-dark-800 mb-1">饮品师正在推荐中...</h5>
+                    <h5 class="text-sm font-bold text-dark-800 mb-1">{{ t('recipeCard.loading.beverage.title') }}</h5>
                     <p class="text-gray-600 text-xs">{{ wineLoadingText }}</p>
                 </div>
 
@@ -157,7 +147,7 @@
                 <!-- 饮品搭配空状态 - 包含获取按钮 -->
                 <div v-else-if="!isFetchingWine" class="bg-gray-100 border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:bg-gray-50 transition-colors">
                     <div class="text-gray-400 text-2xl mb-3">🥤</div>
-                    <p class="text-gray-500 text-xs mb-4">暂无饮品搭配推荐</p>
+                    <p class="text-gray-500 text-xs mb-4">{{ t('recipeCard.emptyState.beverage') }}</p>
                     <button
                         @click="fetchWinePairing"
                         :disabled="isFetchingWine"
@@ -166,32 +156,22 @@
                         <span class="flex items-center gap-1">
                             <template v-if="isFetchingWine">
                                 <div class="animate-spin w-3 h-3 border border-white border-t-transparent rounded-full"></div>
-                                获取中...
+                                {{ t('recipeCard.buttons.fetching') }}
                             </template>
-                            <template v-else> ✨ 获取饮品搭配 </template>
+                            <template v-else> ✨ {{ t('recipeCard.buttons.getBeverage') }} </template>
                         </span>
                     </button>
                 </div>
-
-                <!-- 重新获取按钮 - 当已有数据时显示 -->
-                <!-- <div v-if="recipe.winePairing && !isFetchingWine" class="mt-3 text-center">
-                    <button
-                        @click="fetchWinePairing"
-                        class="bg-purple-500 hover:bg-purple-600 text-white px-3 py-1 rounded text-xs font-medium border border-black transition-all duration-200"
-                    >
-                        🔄 重新获取
-                    </button>
-                </div> -->
             </div>
 
             <!-- 效果图区域 -->
             <div class="mt-4 pt-4 border-t border-gray-200">
-                <h4 class="text-sm font-bold text-dark-800 mb-3 flex items-center gap-1">🖼️ 菜品效果图</h4>
+                <h4 class="text-sm font-bold text-dark-800 mb-3 flex items-center gap-1">🖼️ {{ t('recipeCard.sections.image') }}</h4>
 
                 <!-- 加载状态 -->
                 <div v-if="isGeneratingImage" class="bg-gray-50 border-2 border-gray-300 rounded-lg p-6 text-center">
                     <div class="w-12 h-12 border-4 border-gray-300 border-t-blue-500 rounded-full animate-spin mx-auto mb-3"></div>
-                    <h5 class="text-sm font-bold text-dark-800 mb-1">AI画师正在创作中...</h5>
+                    <h5 class="text-sm font-bold text-dark-800 mb-1">{{ t('recipeCard.loading.image.title') }}</h5>
                     <p class="text-gray-600 text-xs">{{ imageLoadingText }}</p>
                 </div>
 
@@ -204,7 +184,7 @@
                 <div v-else-if="generatedImage" class="mb-3">
                     <img
                         :src="generatedImage.url"
-                        :alt="`${recipe.name}效果图`"
+                        :alt="t('recipeCard.imageAlt', { name: recipe.name })"
                         class="w-full object-cover rounded-lg border-2 border-[#0A0910] cursor-pointer transition-all duration-300 hover:brightness-110 hover:scale-[1.02]"
                         @error="handleImageError"
                         @click="openImageModal"
@@ -214,7 +194,7 @@
                 <!-- 效果图空状态 - 包含生成按钮 -->
                 <div v-else class="bg-gray-100 border-2 border-dashed border-gray-300 rounded-lg p-10 text-center hover:bg-gray-50 transition-colors">
                     <div class="text-gray-400 text-2xl mb-3">📷</div>
-                    <p class="text-gray-500 text-xs mb-4">暂无菜品效果图</p>
+                    <p class="text-gray-500 text-xs mb-4">{{ t('recipeCard.emptyState.image') }}</p>
                     <button
                         @click="generateImage"
                         :disabled="isGeneratingImage"
@@ -223,9 +203,9 @@
                         <span class="flex items-center gap-1">
                             <template v-if="isGeneratingImage">
                                 <div class="animate-spin w-3 h-3 border border-white border-t-transparent rounded-full"></div>
-                                生成中...
+                                {{ t('recipeCard.buttons.generating') }}
                             </template>
-                            <template v-else> ✨ 生成效果图 </template>
+                            <template v-else> ✨ {{ t('recipeCard.buttons.generateImage') }} </template>
                         </span>
                     </button>
                 </div>
@@ -236,7 +216,7 @@
                         @click="generateImage"
                         class="bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded text-xs font-medium border border-black transition-all duration-200"
                     >
-                        🔄 重新生成
+                        🔄 {{ t('recipeCard.buttons.regenerate') }}
                     </button>
                 </div>
             </div>
@@ -249,6 +229,7 @@
 
 <script setup lang="ts">
 import { computed, ref, onUnmounted } from 'vue'
+import { useI18n } from 'vue-i18n'
 import type { Recipe } from '@/types'
 import { generateRecipeImage, type GeneratedImage } from '@/services/imageService'
 import { getNutritionAnalysis, getWinePairing } from '@/services/aiService'
@@ -258,6 +239,8 @@ import NutritionAnalysis from './NutritionAnalysis.vue'
 import WinePairing from './WinePairing.vue'
 import ImageModal from './ImageModal.vue'
 
+const { t, locale } = useI18n()
+
 interface Props {
     recipe: Recipe
     showFavoriteButton?: boolean
@@ -274,57 +257,19 @@ const isExpanded = ref(false)
 const isGeneratingImage = ref(false)
 const generatedImage = ref<GeneratedImage | null>(null)
 const imageError = ref<string>('')
-const imageLoadingText = ref('正在构思画面布局...')
-const nutritionLoadingText = ref('营养师正在分析中...')
-const wineLoadingText = ref('侍酒师正在推荐中...')
+const imageLoadingText = ref(t('recipeCard.loading.image.texts.0'))
+const nutritionLoadingText = ref(t('recipeCard.loading.nutrition.texts.0'))
+const wineLoadingText = ref(t('recipeCard.loading.beverage.texts.0'))
 const isFetchingNutrition = ref(false)
 const nutritionError = ref('')
 const isFetchingWine = ref(false)
 const wineError = ref('')
 const showImageModal = ref(false)
 
-// 图片生成加载文字轮播
-const imageLoadingTexts = [
-    '正在构思画面布局...',
-    '正在调配色彩搭配...',
-    '正在绘制食材细节...',
-    '正在优化光影效果...',
-    '正在精修画面质感...',
-    '正在添加最后润色...',
-    '精美效果图即将完成...'
-]
-
-// 营养分析加载文字轮播
-const nutritionLoadingTexts = [
-    '营养师正在分析中...',
-    '正在计算卡路里...',
-    '正在分析蛋白质含量...',
-    '正在评估维生素含量...',
-    '正在生成健康建议...',
-    '正在准备饮食建议...',
-    '营养分析即将完成...'
-]
-
-// 饮品搭配加载文字轮播
-const wineLoadingTexts = [
-    '饮品师正在推荐中...',
-    '正在匹配口味特征...',
-    '正在考虑饮品平衡...',
-    '正在评估搭配效果...',
-    '正在选择最佳温度...',
-    '正在准备搭配理由...',
-    '完美搭配即将呈现...'
-]
-
 let imageLoadingInterval: ReturnType<typeof setTimeout> | null = null
 
 const difficultyText = computed(() => {
-    const difficultyMap = {
-        easy: '简单',
-        medium: '中等',
-        hard: '困难'
-    }
-    return difficultyMap[props.recipe.difficulty] || '中等'
+    return t(`recipeCard.difficulty.${props.recipe.difficulty}`) || t('recipeCard.difficulty.medium')
 })
 
 // 格式化时间显示
@@ -372,8 +317,8 @@ const generateImage = async () => {
     // 开始图片生成加载文字轮播
     let textIndex = 0
     imageLoadingInterval = setInterval(() => {
-        imageLoadingText.value = imageLoadingTexts[textIndex]
-        textIndex = (textIndex + 1) % imageLoadingTexts.length
+        imageLoadingText.value = t(`recipeCard.loading.image.texts.${textIndex}`)
+        textIndex = (textIndex + 1) % 7
     }, 2000)
 
     try {
@@ -382,11 +327,16 @@ const generateImage = async () => {
 
         // 将生成的图片添加到图库
         const { GalleryService } = await import('@/services/galleryService')
-        const prompt = `一道精美的${props.recipe.cuisine.replace('大师', '').replace('菜', '')}菜肴:${props.recipe.name}`
+
+        const prompt = locale.value === 'en'
+            ? `An exquisite ${props.recipe.cuisine} dish: ${props.recipe.name}`
+            : `一道精美的${props.recipe.cuisine}菜肴:${props.recipe.name}`
+
+        // const prompt = `一道精美的${props.recipe.cuisine.replace('大师', '').replace('菜', '')}菜肴:${props.recipe.name}`
         GalleryService.addToGallery(props.recipe, image.url, image.id, prompt)
     } catch (error) {
         console.error('生成图片失败:', error)
-        imageError.value = 'AI画师表示这道菜太有艺术挑战性了,哈哈!'
+        imageError.value = t('recipeCard.errors.imageGeneration')
     } finally {
         isGeneratingImage.value = false
         if (imageLoadingInterval) {
@@ -397,7 +347,7 @@ const generateImage = async () => {
 }
 
 const handleImageError = () => {
-    imageError.value = '图片加载失败'
+    imageError.value = t('recipeCard.errors.imageLoad')
     generatedImage.value = null
 }
 
@@ -409,8 +359,8 @@ const fetchNutritionAnalysis = async () => {
 
     let textIndex = 0
     const interval = setInterval(() => {
-        nutritionLoadingText.value = nutritionLoadingTexts[textIndex]
-        textIndex = (textIndex + 1) % nutritionLoadingTexts.length
+        nutritionLoadingText.value = t(`recipeCard.loading.nutrition.texts.${textIndex}`)
+        textIndex = (textIndex + 1) % 7
     }, 2000)
 
     try {
@@ -418,7 +368,7 @@ const fetchNutritionAnalysis = async () => {
         props.recipe.nutritionAnalysis = analysis
     } catch (error) {
         console.error('获取营养分析失败:', error)
-        nutritionError.value = '获取营养分析失败,请稍后重试'
+        nutritionError.value = t('recipeCard.errors.nutritionFetch')
     } finally {
         isFetchingNutrition.value = false
         clearInterval(interval)
@@ -433,8 +383,8 @@ const fetchWinePairing = async () => {
 
     let textIndex = 0
     const interval = setInterval(() => {
-        wineLoadingText.value = wineLoadingTexts[textIndex]
-        textIndex = (textIndex + 1) % wineLoadingTexts.length
+        wineLoadingText.value = t(`recipeCard.loading.beverage.texts.${textIndex}`)
+        textIndex = (textIndex + 1) % 7
     }, 2000)
 
     try {
@@ -442,7 +392,7 @@ const fetchWinePairing = async () => {
         props.recipe.winePairing = pairing
     } catch (error) {
         console.error('获取饮品搭配失败:', error)
-        wineError.value = '获取饮品搭配失败,请稍后重试'
+        wineError.value = t('recipeCard.errors.beverageFetch')
     } finally {
         isFetchingWine.value = false
         clearInterval(interval)
@@ -472,7 +422,9 @@ const getModalImageData = (): GalleryImage | null => {
         recipeId: props.recipe.id,
         cuisine: props.recipe.cuisine,
         ingredients: props.recipe.ingredients,
-        prompt: `一道精美的${props.recipe.cuisine.replace('大师', '').replace('菜', '')}菜肴:${props.recipe.name}`,
+        prompt: locale.value === 'en'
+            ? `An exquisite ${props.recipe.cuisine} dish: ${props.recipe.name}`
+            : `一道精美的${props.recipe.cuisine}菜肴:${props.recipe.name}`,
         generatedAt: new Date().toISOString()
     }
 }

+ 77 - 0
src/i18n/locales/en.json

@@ -876,6 +876,83 @@
       }
     }
   },
+  "recipeCard": {
+    "difficulty": {
+      "easy": "Easy",
+      "medium": "Medium",
+      "hard": "Hard"
+    },
+    "sections": {
+      "ingredients": "Ingredients",
+      "steps": "Cooking Steps",
+      "tips": "Cooking Tips",
+      "nutrition": "Nutrition Analysis",
+      "beverage": "Beverage Pairing",
+      "image": "Dish Image"
+    },
+    "buttons": {
+      "expand": "Expand",
+      "collapse": "Collapse",
+      "getNutrition": "Get Nutrition Analysis",
+      "getBeverage": "Get Beverage Pairing",
+      "generateImage": "Generate Image",
+      "regenerate": "Regenerate",
+      "fetching": "Fetching...",
+      "generating": "Generating..."
+    },
+    "loading": {
+      "image": {
+        "title": "AI artist is creating...",
+        "texts": [
+          "Conceptualizing the layout...",
+          "Mixing color palette...",
+          "Drawing ingredient details...",
+          "Optimizing lighting effects...",
+          "Refining image texture...",
+          "Adding final touches...",
+          "Beautiful image almost ready..."
+        ]
+      },
+      "nutrition": {
+        "title": "Nutritionist is analyzing...",
+        "texts": [
+          "Nutritionist is analyzing...",
+          "Calculating calories...",
+          "Analyzing protein content...",
+          "Evaluating vitamin levels...",
+          "Generating health suggestions...",
+          "Preparing dietary advice...",
+          "Nutrition analysis almost complete..."
+        ]
+      },
+      "beverage": {
+        "title": "Beverage expert is recommending...",
+        "texts": [
+          "Beverage expert is recommending...",
+          "Matching flavor profiles...",
+          "Considering beverage balance...",
+          "Evaluating pairing effects...",
+          "Selecting optimal temperature...",
+          "Preparing pairing reasons...",
+          "Perfect pairing almost ready..."
+        ]
+      }
+    },
+    "emptyState": {
+      "nutrition": "No nutrition analysis data available",
+      "beverage": "No beverage pairing recommendations",
+      "image": "No dish image available"
+    },
+    "errors": {
+      "imageGeneration": "AI artist says this dish is too artistically challenging, haha!",
+      "imageLoad": "Image failed to load",
+      "nutritionFetch": "Failed to fetch nutrition analysis, please try again later",
+      "beverageFetch": "Failed to fetch beverage pairing, please try again later"
+    },
+    "stepsRemaining": "{count} more steps...",
+    "imageAlt": "{name} image",
+    "imagePrompt": "An exquisite {cuisine} dish: {name}"
+  },
   "ai": {
     "systemPrompt": "You are a professional chef. Please generate detailed recipes based on the ingredients and cuisine requirements provided by the user.",
     "responseFormat": "Please return the recipe in the following JSON format:\n{\n  \"name\": \"Dish Name\",\n  \"ingredients\": [\"Ingredient 1\", \"Ingredient 2\"],\n  \"steps\": [\n    {\n      \"step\": 1,\n      \"description\": \"Step description\",\n      \"time\": 5,\n      \"temperature\": \"Medium heat\"\n    }\n  ],\n  \"cookingTime\": 30,\n  \"difficulty\": \"medium\",\n  \"tips\": [\"Tip 1\", \"Tip 2\"]\n}"

+ 77 - 0
src/i18n/locales/zh.json

@@ -876,6 +876,83 @@
       }
     }
   },
+  "recipeCard": {
+    "difficulty": {
+      "easy": "简单",
+      "medium": "中等",
+      "hard": "困难"
+    },
+    "sections": {
+      "ingredients": "所需食材",
+      "steps": "制作步骤",
+      "tips": "烹饪技巧",
+      "nutrition": "营养分析",
+      "beverage": "饮品搭配",
+      "image": "菜品效果图"
+    },
+    "buttons": {
+      "expand": "展开",
+      "collapse": "收起",
+      "getNutrition": "获取营养分析",
+      "getBeverage": "获取饮品搭配",
+      "generateImage": "生成效果图",
+      "regenerate": "重新生成",
+      "fetching": "获取中...",
+      "generating": "生成中..."
+    },
+    "loading": {
+      "image": {
+        "title": "AI画师正在创作中...",
+        "texts": [
+          "正在构思画面布局...",
+          "正在调配色彩搭配...",
+          "正在绘制食材细节...",
+          "正在优化光影效果...",
+          "正在精修画面质感...",
+          "正在添加最后润色...",
+          "精美效果图即将完成..."
+        ]
+      },
+      "nutrition": {
+        "title": "营养师正在分析中...",
+        "texts": [
+          "营养师正在分析中...",
+          "正在计算卡路里...",
+          "正在分析蛋白质含量...",
+          "正在评估维生素含量...",
+          "正在生成健康建议...",
+          "正在准备饮食建议...",
+          "营养分析即将完成..."
+        ]
+      },
+      "beverage": {
+        "title": "饮品师正在推荐中...",
+        "texts": [
+          "饮品师正在推荐中...",
+          "正在匹配口味特征...",
+          "正在考虑饮品平衡...",
+          "正在评估搭配效果...",
+          "正在选择最佳温度...",
+          "正在准备搭配理由...",
+          "完美搭配即将呈现..."
+        ]
+      }
+    },
+    "emptyState": {
+      "nutrition": "暂无营养分析数据",
+      "beverage": "暂无饮品搭配推荐",
+      "image": "暂无菜品效果图"
+    },
+    "errors": {
+      "imageGeneration": "AI画师表示这道菜太有艺术挑战性了,哈哈!",
+      "imageLoad": "图片加载失败",
+      "nutritionFetch": "获取营养分析失败,请稍后重试",
+      "beverageFetch": "获取饮品搭配失败,请稍后重试"
+    },
+    "stepsRemaining": "还有 {count} 个步骤...",
+    "imageAlt": "{name}效果图",
+    "imagePrompt": "一道精美的{cuisine}菜肴:{name}"
+  },
   "ai": {
     "systemPrompt": "你是一位专业的厨师,请根据用户提供的食材和菜系要求,生成详细的菜谱。",
     "responseFormat": "请按照以下JSON格式返回菜谱:\n{\n  \"name\": \"菜品名称\",\n  \"ingredients\": [\"食材1\", \"食材2\"],\n  \"steps\": [\n    {\n      \"step\": 1,\n      \"description\": \"步骤描述\",\n      \"time\": 5,\n      \"temperature\": \"中火\"\n    }\n  ],\n  \"cookingTime\": 30,\n  \"difficulty\": \"medium\",\n  \"tips\": [\"技巧1\", \"技巧2\"]\n}"