Add Chinese translations for medication management and personal settings

- Introduced new translation files for medication, personal, and weight management in Chinese.
- Updated the main index file to include the new translation modules.
- Enhanced the medication type definitions to include 'ointment'.
- Refactored workout type labels to utilize i18n for better localization support.
- Improved sleep quality descriptions and recommendations with i18n integration.
This commit is contained in:
richarjiang
2025-11-28 17:29:51 +08:00
parent fbe0c92f0f
commit bca6670390
42 changed files with 7972 additions and 6632 deletions

View File

@@ -3,6 +3,7 @@ import { HeaderBar } from '@/components/ui/HeaderBar';
import { Colors } from '@/constants/Colors';
import { ROUTES } from '@/constants/Routes';
import { useAppSelector } from '@/hooks/redux';
import { useI18n } from '@/hooks/useI18n';
import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding';
import { addDietRecord, type CreateDietRecordDto, type MealType } from '@/services/dietRecords';
import { selectFoodRecognitionResult } from '@/store/foodRecognitionSlice';
@@ -65,15 +66,8 @@ const mockFoodItems = [
}
];
// 餐次映射
const MEAL_TYPE_MAP = {
breakfast: '早餐',
lunch: '午餐',
dinner: '晚餐',
snack: '加餐'
};
export default function FoodAnalysisResultScreen() {
const { t } = useI18n();
const safeAreaTop = useSafeAreaTop()
const router = useRouter();
const params = useLocalSearchParams<{
@@ -190,6 +184,15 @@ export default function FoodAnalysisResultScreen() {
}
};
// 餐次映射
const MEAL_TYPE_MAP = {
breakfast: t('nutritionRecords.mealTypes.breakfast'),
lunch: t('nutritionRecords.mealTypes.lunch'),
dinner: t('nutritionRecords.mealTypes.dinner'),
snack: t('nutritionRecords.mealTypes.snack'),
other: t('nutritionRecords.mealTypes.other'),
};
// 计算所有食物的总营养数据
const totalCalories = foodItems.reduce((sum, item) => sum + item.calories, 0);
const totalProtein = foodItems.reduce((sum, item) => sum + item.protein, 0);
@@ -253,24 +256,24 @@ export default function FoodAnalysisResultScreen() {
// 餐次选择选项
const mealOptions = [
{ key: 'breakfast' as const, label: '早餐', color: '#FF6B35' },
{ key: 'lunch' as const, label: '午餐', color: '#4CAF50' },
{ key: 'dinner' as const, label: '晚餐', color: '#2196F3' },
{ key: 'snack' as const, label: '加餐', color: '#FF9800' },
{ key: 'breakfast' as const, label: t('nutritionRecords.mealTypes.breakfast'), color: '#FF6B35' },
{ key: 'lunch' as const, label: t('nutritionRecords.mealTypes.lunch'), color: '#4CAF50' },
{ key: 'dinner' as const, label: t('nutritionRecords.mealTypes.dinner'), color: '#2196F3' },
{ key: 'snack' as const, label: t('nutritionRecords.mealTypes.snack'), color: '#FF9800' },
];
if (!imageUri && !recognitionResult) {
return (
<View style={styles.container}>
<HeaderBar
title="分析结果"
title={t('foodAnalysisResult.title')}
onBack={() => router.back()}
/>
<View style={{
paddingTop: safeAreaTop
}} />
<View style={styles.errorContainer}>
<Text style={styles.errorText}></Text>
<Text style={styles.errorText}>{t('foodAnalysisResult.error.notFound')}</Text>
</View>
</View>
);
@@ -287,7 +290,7 @@ export default function FoodAnalysisResultScreen() {
/>
<HeaderBar
title="分析结果"
title={t('foodAnalysisResult.title')}
onBack={() => router.back()}
transparent={true}
/>
@@ -316,7 +319,7 @@ export default function FoodAnalysisResultScreen() {
<View style={styles.placeholderContainer}>
<View style={styles.placeholderContent}>
<Ionicons name="restaurant-outline" size={48} color="#666" />
<Text style={styles.placeholderText}></Text>
<Text style={styles.placeholderText}>{t('foodAnalysisResult.placeholder')}</Text>
</View>
</View>
)}
@@ -325,8 +328,8 @@ export default function FoodAnalysisResultScreen() {
<View style={styles.descriptionBubble}>
<Text style={styles.descriptionText}>
{recognitionResult ?
`置信度: ${recognitionResult.confidence}%` :
dayjs().format('YYYY年M月D日')
t('foodAnalysisResult.confidence', { value: recognitionResult.confidence }) :
dayjs().format(t('foodAnalysisResult.dateFormats.today'))
}
</Text>
</View>
@@ -337,31 +340,31 @@ export default function FoodAnalysisResultScreen() {
{/* 卡路里 */}
<View style={styles.calorieSection}>
<Text style={styles.calorieValue}>{totalCalories}</Text>
<Text style={styles.calorieUnit}></Text>
<Text style={styles.calorieUnit}>{t('foodAnalysisResult.nutrients.caloriesUnit')}</Text>
</View>
{/* 营养圆环图 */}
<View style={styles.nutritionRings}>
<NutritionRing
label="蛋白质"
label={t('foodAnalysisResult.nutrients.protein')}
value={totalProtein.toFixed(1)}
unit="克"
unit={t('foodAnalysisResult.nutrients.unit')}
percentage={Math.min(100, proteinPercentage)}
color="#4CAF50"
resetToken={animationTrigger}
/>
<NutritionRing
label="脂肪"
label={t('foodAnalysisResult.nutrients.fat')}
value={totalFat.toFixed(1)}
unit="克"
unit={t('foodAnalysisResult.nutrients.unit')}
percentage={Math.min(100, fatPercentage)}
color="#FF9800"
resetToken={animationTrigger}
/>
<NutritionRing
label="碳水"
label={t('foodAnalysisResult.nutrients.carbs')}
value={totalCarbohydrate.toFixed(1)}
unit="克"
unit={t('foodAnalysisResult.nutrients.unit')}
percentage={Math.min(100, carbohydratePercentage)}
color="#2196F3"
resetToken={animationTrigger}
@@ -372,7 +375,7 @@ export default function FoodAnalysisResultScreen() {
{/* 食物摄入部分 */}
<View style={styles.foodIntakeSection}>
<Text style={styles.foodIntakeTitle}>
{recognitionResult ? '识别结果' : '食物摄入'}
{recognitionResult ? t('foodAnalysisResult.sections.recognitionResult') : t('foodAnalysisResult.sections.foodIntake')}
</Text>
{recognitionResult && recognitionResult.analysisText && (
<Text style={styles.analysisText}>{recognitionResult.analysisText}</Text>
@@ -384,15 +387,15 @@ export default function FoodAnalysisResultScreen() {
<View style={styles.nonFoodIcon}>
<Ionicons name="alert-circle-outline" size={48} color="#FF9800" />
</View>
<Text style={styles.nonFoodTitle}></Text>
<Text style={styles.nonFoodTitle}>{t('foodAnalysisResult.nonFood.title')}</Text>
<Text style={styles.nonFoodMessage}>
{recognitionResult.nonFoodMessage || recognitionResult.analysisText}
</Text>
<View style={styles.nonFoodSuggestions}>
<Text style={styles.nonFoodSuggestionsTitle}></Text>
<Text style={styles.nonFoodSuggestionItem}> </Text>
<Text style={styles.nonFoodSuggestionItem}> </Text>
<Text style={styles.nonFoodSuggestionItem}> 线</Text>
<Text style={styles.nonFoodSuggestionsTitle}>{t('foodAnalysisResult.nonFood.suggestions.title')}</Text>
<Text style={styles.nonFoodSuggestionItem}>{t('foodAnalysisResult.nonFood.suggestions.item1')}</Text>
<Text style={styles.nonFoodSuggestionItem}>{t('foodAnalysisResult.nonFood.suggestions.item2')}</Text>
<Text style={styles.nonFoodSuggestionItem}>{t('foodAnalysisResult.nonFood.suggestions.item3')}</Text>
</View>
</View>
)}
@@ -411,7 +414,7 @@ export default function FoodAnalysisResultScreen() {
</View>
<View style={styles.foodIntakeCalories}>
<Text style={styles.foodIntakeCaloriesValue}>{item.calories}</Text>
<Text style={styles.foodIntakeCaloriesValue}>{item.calories}{t('foodAnalysisResult.nutrients.caloriesUnit')}</Text>
{shouldHideRecordBar ? null : <TouchableOpacity
style={styles.editButton}
onPress={() => handleEditFood(item)}
@@ -442,7 +445,7 @@ export default function FoodAnalysisResultScreen() {
activeOpacity={0.8}
>
<Ionicons name="camera-outline" size={20} color={Colors.light.onPrimary} style={{ marginRight: 8 }} />
<Text style={styles.retakePhotoButtonText}></Text>
<Text style={styles.retakePhotoButtonText}>{t('foodAnalysisResult.actions.retake')}</Text>
</TouchableOpacity>
</View>
) : (
@@ -471,7 +474,7 @@ export default function FoodAnalysisResultScreen() {
{isRecording ? (
<ActivityIndicator size="small" color="#FFF" />
) : (
<Text style={styles.recordButtonText}></Text>
<Text style={styles.recordButtonText}>{t('foodAnalysisResult.actions.record')}</Text>
)}
</TouchableOpacity>
</View>
@@ -492,7 +495,7 @@ export default function FoodAnalysisResultScreen() {
/>
<View style={styles.mealSelectorModal}>
<View style={styles.mealSelectorHeader}>
<Text style={styles.mealSelectorTitle}></Text>
<Text style={styles.mealSelectorTitle}>{t('foodAnalysisResult.mealSelector.title')}</Text>
<TouchableOpacity onPress={() => setShowMealSelector(false)}>
<Ionicons name="close" size={24} color="#666" />
</TouchableOpacity>
@@ -539,8 +542,8 @@ export default function FoodAnalysisResultScreen() {
<View style={styles.imageViewerHeader}>
<Text style={styles.imageViewerHeaderText}>
{recognitionResult ?
`置信度: ${recognitionResult.confidence}%` :
dayjs().format('YYYY年M月D日 HH:mm')
t('foodAnalysisResult.confidence', { value: recognitionResult.confidence }) :
dayjs().format(t('foodAnalysisResult.dateFormats.full'))
}
</Text>
</View>
@@ -551,7 +554,7 @@ export default function FoodAnalysisResultScreen() {
style={styles.imageViewerFooterButton}
onPress={() => setShowImagePreview(false)}
>
<Text style={styles.imageViewerFooterButtonText}></Text>
<Text style={styles.imageViewerFooterButtonText}>{t('foodAnalysisResult.actions.close')}</Text>
</TouchableOpacity>
</View>
)}
@@ -587,6 +590,8 @@ function FoodEditModal({
onFormDataChange({ ...formData, [field]: value });
};
const { t } = useI18n();
return (
<Modal
visible={visible}
@@ -598,14 +603,14 @@ function FoodEditModal({
<View style={styles.editModalSheet}>
<View style={styles.modalHandle} />
<Text style={styles.modalTitle}></Text>
<Text style={styles.modalTitle}>{t('foodAnalysisResult.editModal.title')}</Text>
{/* 食物名称 */}
<View style={styles.editFieldContainer}>
<Text style={styles.editFieldLabel}></Text>
<Text style={styles.editFieldLabel}>{t('foodAnalysisResult.editModal.fields.name')}</Text>
<TextInput
style={styles.editInput}
placeholder="输入食物名称"
placeholder={t('foodAnalysisResult.editModal.fields.namePlaceholder')}
placeholderTextColor="#999"
value={formData.name}
onChangeText={(value) => handleFieldChange('name', value)}
@@ -615,10 +620,10 @@ function FoodEditModal({
{/* 重量/数量 */}
<View style={styles.editFieldContainer}>
<Text style={styles.editFieldLabel}> ()</Text>
<Text style={styles.editFieldLabel}>{t('foodAnalysisResult.editModal.fields.amount')}</Text>
<TextInput
style={styles.editInput}
placeholder="输入重量"
placeholder={t('foodAnalysisResult.editModal.fields.amountPlaceholder')}
placeholderTextColor="#999"
value={formData.amount}
onChangeText={(value) => handleFieldChange('amount', value)}
@@ -628,10 +633,10 @@ function FoodEditModal({
{/* 卡路里 */}
<View style={styles.editFieldContainer}>
<Text style={styles.editFieldLabel}> ()</Text>
<Text style={styles.editFieldLabel}>{t('foodAnalysisResult.editModal.fields.calories')}</Text>
<TextInput
style={styles.editInput}
placeholder="输入卡路里"
placeholder={t('foodAnalysisResult.editModal.fields.caloriesPlaceholder')}
placeholderTextColor="#999"
value={formData.calories}
onChangeText={(value) => handleFieldChange('calories', value)}
@@ -645,13 +650,13 @@ function FoodEditModal({
onPress={onClose}
style={styles.modalCancelBtn}
>
<Text style={styles.modalCancelText}></Text>
<Text style={styles.modalCancelText}>{t('foodAnalysisResult.editModal.actions.cancel')}</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={onSave}
style={[styles.modalSaveBtn, { backgroundColor: Colors.light.primary }]}
>
<Text style={[styles.modalSaveText, { color: Colors.light.onPrimary }]}></Text>
<Text style={[styles.modalSaveText, { color: Colors.light.onPrimary }]}>{t('foodAnalysisResult.editModal.actions.save')}</Text>
</TouchableOpacity>
</View>
</View>