feat(i18n): 实现应用国际化支持,添加中英文翻译
- 为所有UI组件添加国际化支持,替换硬编码文本 - 新增useI18n钩子函数统一管理翻译 - 完善中英文翻译资源,覆盖统计、用药、通知设置等模块 - 优化Tab布局使用翻译键值替代静态文本 - 更新药品管理、个人资料编辑等页面的多语言支持
This commit is contained in:
@@ -9,6 +9,7 @@ import { calculateRemainingCalories } from '@/utils/nutrition';
|
||||
import dayjs from 'dayjs';
|
||||
import { router } from 'expo-router';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Animated, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import Svg, { Circle } from 'react-native-svg';
|
||||
|
||||
@@ -25,10 +26,12 @@ export type NutritionRadarCardProps = {
|
||||
// 简化的圆环进度组件
|
||||
const SimpleRingProgress = ({
|
||||
remainingCalories,
|
||||
totalAvailable
|
||||
totalAvailable,
|
||||
t
|
||||
}: {
|
||||
remainingCalories: number;
|
||||
totalAvailable: number;
|
||||
t: any;
|
||||
}) => {
|
||||
const animatedProgress = useRef(new Animated.Value(0)).current;
|
||||
const radius = 32;
|
||||
@@ -82,7 +85,7 @@ const SimpleRingProgress = ({
|
||||
<Text style={{ fontSize: 12, fontWeight: '600', color: '#192126' }}>
|
||||
{Math.round(remainingCalories)}
|
||||
</Text>
|
||||
<Text style={{ fontSize: 8, color: '#9AA3AE' }}>还能吃</Text>
|
||||
<Text style={{ fontSize: 8, color: '#9AA3AE' }}>{t('statistics.components.diet.remaining')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
@@ -93,6 +96,7 @@ export function NutritionRadarCard({
|
||||
style,
|
||||
resetToken,
|
||||
}: NutritionRadarCardProps) {
|
||||
const { t } = useTranslation();
|
||||
const [currentMealType] = useState<'breakfast' | 'lunch' | 'dinner' | 'snack'>('breakfast');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -122,7 +126,7 @@ export function NutritionRadarCard({
|
||||
dispatch(fetchDailyBasalMetabolism(targetDate)).unwrap(),
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error('NutritionRadarCard: 获取营养卡片数据失败:', error);
|
||||
console.error('NutritionRadarCard: Failed to get nutrition card data:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -133,14 +137,14 @@ export function NutritionRadarCard({
|
||||
|
||||
const nutritionStats = useMemo(() => {
|
||||
return [
|
||||
{ label: '热量', value: nutritionSummary ? `${Math.round(nutritionSummary.totalCalories)} 千卡` : '0 千卡', color: '#FF6B6B' },
|
||||
{ label: '蛋白质', value: nutritionSummary ? `${nutritionSummary.totalProtein.toFixed(1)} g` : '0.0 g', color: '#4ECDC4' },
|
||||
{ label: '碳水', value: nutritionSummary ? `${nutritionSummary.totalCarbohydrate.toFixed(1)} g` : '0.0 g', color: '#45B7D1' },
|
||||
{ label: '脂肪', value: nutritionSummary ? `${nutritionSummary.totalFat.toFixed(1)} g` : '0.0 g', color: '#FFA07A' },
|
||||
{ label: '纤维', value: nutritionSummary ? `${nutritionSummary.totalFiber.toFixed(1)} g` : '0.0 g', color: '#98D8C8' },
|
||||
{ label: '钠', value: nutritionSummary ? `${Math.round(nutritionSummary.totalSodium)} mg` : '0 mg', color: '#F7DC6F' },
|
||||
{ label: t('statistics.components.diet.calories'), value: nutritionSummary ? `${Math.round(nutritionSummary.totalCalories)} ${t('statistics.components.diet.kcal')}` : `0 ${t('statistics.components.diet.kcal')}`, color: '#FF6B6B' },
|
||||
{ label: t('statistics.components.diet.protein'), value: nutritionSummary ? `${nutritionSummary.totalProtein.toFixed(1)} g` : '0.0 g', color: '#4ECDC4' },
|
||||
{ label: t('statistics.components.diet.carb'), value: nutritionSummary ? `${nutritionSummary.totalCarbohydrate.toFixed(1)} g` : '0.0 g', color: '#45B7D1' },
|
||||
{ label: t('statistics.components.diet.fat'), value: nutritionSummary ? `${nutritionSummary.totalFat.toFixed(1)} g` : '0.0 g', color: '#FFA07A' },
|
||||
{ label: t('statistics.components.diet.fiber'), value: nutritionSummary ? `${nutritionSummary.totalFiber.toFixed(1)} g` : '0.0 g', color: '#98D8C8' },
|
||||
{ label: t('statistics.components.diet.sodium'), value: nutritionSummary ? `${Math.round(nutritionSummary.totalSodium)} mg` : '0 mg', color: '#F7DC6F' },
|
||||
];
|
||||
}, [nutritionSummary]);
|
||||
}, [nutritionSummary, t]);
|
||||
|
||||
// 计算还能吃的卡路里
|
||||
const consumedCalories = nutritionSummary?.totalCalories || 0;
|
||||
@@ -168,10 +172,10 @@ export function NutritionRadarCard({
|
||||
source={require('@/assets/images/icons/icon-healthy-diet.png')}
|
||||
style={styles.titleIcon}
|
||||
/>
|
||||
<Text style={styles.cardTitle}>饮食分析</Text>
|
||||
<Text style={styles.cardTitle}>{t('statistics.components.diet.title')}</Text>
|
||||
</View>
|
||||
<Text style={styles.cardSubtitle}>
|
||||
{loading ? '加载中...' : `更新: ${dayjs(nutritionSummary?.updatedAt).format('MM-DD HH:mm')}`}
|
||||
{loading ? t('statistics.components.diet.loading') : t('statistics.components.diet.updated', { time: dayjs(nutritionSummary?.updatedAt).format('MM-DD HH:mm') })}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -180,6 +184,7 @@ export function NutritionRadarCard({
|
||||
<SimpleRingProgress
|
||||
remainingCalories={(loading || activeCaloriesLoading) ? 0 : remainingCalories}
|
||||
totalAvailable={(loading || activeCaloriesLoading) ? 0 : effectiveBasalMetabolism + effectiveActiveCalories}
|
||||
t={t}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -199,7 +204,7 @@ export function NutritionRadarCard({
|
||||
<View style={styles.calorieSection}>
|
||||
<View style={styles.calorieContent}>
|
||||
<View style={styles.calculationRow}>
|
||||
<Text style={styles.calorieSubtitle}>还能吃</Text>
|
||||
<Text style={styles.calorieSubtitle}>{t('statistics.components.diet.remaining')}</Text>
|
||||
<View style={styles.remainingCaloriesContainer}>
|
||||
<AnimatedNumber
|
||||
value={(loading || activeCaloriesLoading) ? 0 : remainingCalories}
|
||||
@@ -207,11 +212,11 @@ export function NutritionRadarCard({
|
||||
style={styles.mainValue}
|
||||
format={(v) => (loading || activeCaloriesLoading) ? '--' : Math.round(v).toString()}
|
||||
/>
|
||||
<Text style={styles.calorieUnit}>千卡</Text>
|
||||
<Text style={styles.calorieUnit}>{t('statistics.components.diet.kcal')}</Text>
|
||||
</View>
|
||||
<Text style={styles.calculationText}> = </Text>
|
||||
<View style={styles.calculationItem}>
|
||||
<Text style={styles.calculationLabel}>基代</Text>
|
||||
<Text style={styles.calculationLabel}>{t('statistics.components.diet.basal')}</Text>
|
||||
</View>
|
||||
<AnimatedNumber
|
||||
value={loading ? 0 : effectiveBasalMetabolism}
|
||||
@@ -221,7 +226,7 @@ export function NutritionRadarCard({
|
||||
/>
|
||||
<Text style={styles.calculationText}> + </Text>
|
||||
<View style={styles.calculationItem}>
|
||||
<Text style={styles.calculationLabel}>运动</Text>
|
||||
<Text style={styles.calculationLabel}>{t('statistics.components.diet.exercise')}</Text>
|
||||
</View>
|
||||
<AnimatedNumber
|
||||
value={activeCaloriesLoading ? 0 : effectiveActiveCalories}
|
||||
@@ -231,7 +236,7 @@ export function NutritionRadarCard({
|
||||
/>
|
||||
<Text style={styles.calculationText}> - </Text>
|
||||
<View style={styles.calculationItem}>
|
||||
<Text style={styles.calculationLabel}>饮食</Text>
|
||||
<Text style={styles.calculationLabel}>{t('statistics.components.diet.diet')}</Text>
|
||||
</View>
|
||||
<AnimatedNumber
|
||||
value={loading ? 0 : consumedCalories}
|
||||
@@ -260,7 +265,7 @@ export function NutritionRadarCard({
|
||||
style={styles.foodOptionImage}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.foodOptionText}>AI识别</Text>
|
||||
<Text style={styles.foodOptionText}>{t('statistics.components.diet.aiRecognition')}</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
@@ -277,7 +282,7 @@ export function NutritionRadarCard({
|
||||
style={styles.foodOptionImage}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.foodOptionText}>食物库</Text>
|
||||
<Text style={styles.foodOptionText}>{t('statistics.components.diet.foodLibrary')}</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
@@ -294,7 +299,7 @@ export function NutritionRadarCard({
|
||||
style={styles.foodOptionImage}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.foodOptionText}>一句话记录</Text>
|
||||
<Text style={styles.foodOptionText}>{t('statistics.components.diet.voiceRecord')}</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
@@ -311,7 +316,7 @@ export function NutritionRadarCard({
|
||||
style={styles.foodOptionImage}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.foodOptionText}>成分表分析</Text>
|
||||
<Text style={styles.foodOptionText}>{t('statistics.components.diet.nutritionLabel')}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user