feat(i18n): 实现应用国际化支持,添加中英文翻译
- 为所有UI组件添加国际化支持,替换硬编码文本 - 新增useI18n钩子函数统一管理翻译 - 完善中英文翻译资源,覆盖统计、用药、通知设置等模块 - 优化Tab布局使用翻译键值替代静态文本 - 更新药品管理、个人资料编辑等页面的多语言支持
This commit is contained in:
@@ -3,6 +3,7 @@ import dayjs from 'dayjs';
|
||||
import { Image } from 'expo-image';
|
||||
import { useRouter } from 'expo-router';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ActivityIndicator, StyleSheet, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
|
||||
|
||||
import { AnimatedNumber } from '@/components/AnimatedNumber';
|
||||
@@ -40,6 +41,7 @@ const DEFAULT_SUMMARY: WorkoutSummary = {
|
||||
|
||||
|
||||
export const WorkoutSummaryCard: React.FC<WorkoutSummaryCardProps> = ({ date, style }) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const [summary, setSummary] = useState<WorkoutSummary>(DEFAULT_SUMMARY);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -145,13 +147,13 @@ export const WorkoutSummaryCard: React.FC<WorkoutSummaryCardProps> = ({ date, st
|
||||
|
||||
const label = lastWorkout
|
||||
? getWorkoutTypeDisplayName(lastWorkout.workoutActivityType)
|
||||
: '尚无锻炼数据';
|
||||
: t('statistics.components.workout.noData');
|
||||
|
||||
const time = lastWorkout
|
||||
? `${dayjs(lastWorkout.endDate || lastWorkout.startDate).format('HH:mm')} 更新`
|
||||
: '等待同步';
|
||||
? `${dayjs(lastWorkout.endDate || lastWorkout.startDate).format('HH:mm')} ${t('statistics.components.workout.updated')}`
|
||||
: t('statistics.components.workout.syncing');
|
||||
|
||||
let source = '来源:等待同步';
|
||||
let source = t('statistics.components.workout.sourceWaiting');
|
||||
if (hasWorkouts) {
|
||||
const sourceNames = summary.workouts
|
||||
.map((workout) => workout.source?.name?.trim() || workout.source?.bundleIdentifier?.trim())
|
||||
@@ -160,9 +162,11 @@ export const WorkoutSummaryCard: React.FC<WorkoutSummaryCardProps> = ({ date, st
|
||||
if (sourceNames.length) {
|
||||
const uniqueNames = Array.from(new Set(sourceNames));
|
||||
const displayNames = uniqueNames.slice(0, 2).join('、');
|
||||
source = uniqueNames.length > 2 ? `来源:${displayNames} 等` : `来源:${displayNames}`;
|
||||
source = uniqueNames.length > 2
|
||||
? t('statistics.components.workout.sourceFormatMultiple', { source: displayNames })
|
||||
: t('statistics.components.workout.sourceFormat', { source: displayNames });
|
||||
} else {
|
||||
source = '来源:未知';
|
||||
source = t('statistics.components.workout.sourceUnknown');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +188,7 @@ export const WorkoutSummaryCard: React.FC<WorkoutSummaryCardProps> = ({ date, st
|
||||
source,
|
||||
badges: uniqueBadges,
|
||||
};
|
||||
}, [summary]);
|
||||
}, [summary, t]);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
@@ -195,18 +199,18 @@ export const WorkoutSummaryCard: React.FC<WorkoutSummaryCardProps> = ({ date, st
|
||||
<View style={styles.headerRow}>
|
||||
<View style={styles.titleRow}>
|
||||
<Image source={require('@/assets/images/icons/icon-fitness.png')} style={styles.titleIcon} />
|
||||
<Text style={styles.titleText}>近期锻炼</Text>
|
||||
<Text style={styles.titleText}>{t('statistics.components.workout.title')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.metricsRow}>
|
||||
<View style={styles.metricItem}>
|
||||
<AnimatedNumber value={summary.totalMinutes} resetToken={resetToken} style={styles.metricValue} />
|
||||
<Text style={styles.metricLabel}>分钟</Text>
|
||||
<Text style={styles.metricLabel}>{t('statistics.components.workout.minutes')}</Text>
|
||||
</View>
|
||||
<View style={styles.metricItem}>
|
||||
<AnimatedNumber value={summary.totalCalories} resetToken={resetToken} style={styles.metricValue} />
|
||||
<Text style={styles.metricLabel}>千卡</Text>
|
||||
<Text style={styles.metricLabel}>{t('statistics.components.workout.kcal')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user