feat(i18n): 全面实现应用核心功能模块的国际化支持
- 新增 i18n 翻译资源,覆盖睡眠、饮水、体重、锻炼、用药 AI 识别、步数、健身圆环、基础代谢及设置等核心模块 - 重构相关页面及组件(如 SleepDetail, WaterDetail, WorkoutHistory 等)使用 `useI18n` 钩子替换硬编码文本 - 升级 `utils/date` 工具库与 `DateSelector` 组件,支持基于语言环境的日期格式化与显示 - 完善登录页、注销流程及权限申请弹窗的双语提示信息 - 优化部分页面的 UI 细节与字体样式以适配多语言显示
This commit is contained in:
@@ -2,9 +2,10 @@ import { DateSelector } from '@/components/DateSelector';
|
||||
import { HeaderBar } from '@/components/ui/HeaderBar';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useAppSelector } from '@/hooks/redux';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding';
|
||||
import { selectUserAge, selectUserProfile } from '@/store/userSlice';
|
||||
import { getMonthDaysZh, getTodayIndexInMonth } from '@/utils/date';
|
||||
import { getLocalizedDateFormat, getMonthDays, getTodayIndexInMonth } from '@/utils/date';
|
||||
import { fetchBasalEnergyBurned } from '@/utils/health';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import dayjs from 'dayjs';
|
||||
@@ -24,6 +25,7 @@ type BasalMetabolismData = {
|
||||
};
|
||||
|
||||
export default function BasalMetabolismDetailScreen() {
|
||||
const { t, i18n } = useI18n();
|
||||
const userProfile = useAppSelector(selectUserProfile);
|
||||
const userAge = useAppSelector(selectUserAge);
|
||||
const safeAreaTop = useSafeAreaTop()
|
||||
@@ -140,9 +142,9 @@ export default function BasalMetabolismDetailScreen() {
|
||||
|
||||
// 获取当前选中日期
|
||||
const currentSelectedDate = useMemo(() => {
|
||||
const days = getMonthDaysZh();
|
||||
const days = getMonthDays(undefined, i18n.language as 'zh' | 'en');
|
||||
return days[selectedIndex]?.date?.toDate() ?? new Date();
|
||||
}, [selectedIndex]);
|
||||
}, [selectedIndex, i18n.language]);
|
||||
|
||||
|
||||
// 计算BMR范围
|
||||
@@ -203,7 +205,7 @@ export default function BasalMetabolismDetailScreen() {
|
||||
setSelectedIndex(index);
|
||||
|
||||
// 获取选中日期
|
||||
const days = getMonthDaysZh();
|
||||
const days = getMonthDays(undefined, i18n.language as 'zh' | 'en');
|
||||
const selectedDate = days[index]?.date?.toDate();
|
||||
|
||||
if (selectedDate) {
|
||||
@@ -247,7 +249,7 @@ export default function BasalMetabolismDetailScreen() {
|
||||
}
|
||||
} catch (err) {
|
||||
if (!isCancelled) {
|
||||
setError(err instanceof Error ? err.message : '获取数据失败');
|
||||
setError(err instanceof Error ? err.message : t('basalMetabolismDetail.chart.error.fetchFailed'));
|
||||
}
|
||||
} finally {
|
||||
if (!isCancelled) {
|
||||
@@ -280,7 +282,8 @@ export default function BasalMetabolismDetailScreen() {
|
||||
// 显示周数
|
||||
const weekOfYear = dayjs(item.date).week();
|
||||
const firstWeekOfYear = dayjs(item.date).startOf('year').week();
|
||||
return `第${weekOfYear - firstWeekOfYear + 1}周`;
|
||||
const weekNumber = weekOfYear - firstWeekOfYear + 1;
|
||||
return t('basalMetabolismDetail.chart.weekLabel', { week: weekNumber });
|
||||
default:
|
||||
return dayjs(item.date).format('MM-DD');
|
||||
}
|
||||
@@ -319,7 +322,7 @@ export default function BasalMetabolismDetailScreen() {
|
||||
|
||||
{/* 头部导航 */}
|
||||
<HeaderBar
|
||||
title="基础代谢"
|
||||
title={t('basalMetabolismDetail.title')}
|
||||
transparent
|
||||
right={
|
||||
<TouchableOpacity
|
||||
@@ -355,7 +358,9 @@ export default function BasalMetabolismDetailScreen() {
|
||||
{/* 当前日期基础代谢显示 */}
|
||||
<View style={styles.currentDataCard}>
|
||||
<Text style={styles.currentDataTitle}>
|
||||
{dayjs(currentSelectedDate).format('M月D日')} 基础代谢
|
||||
{t('basalMetabolismDetail.currentData.title', {
|
||||
date: getLocalizedDateFormat(dayjs(currentSelectedDate), i18n.language as 'zh' | 'en')
|
||||
})}
|
||||
</Text>
|
||||
<View style={styles.currentValueContainer}>
|
||||
<Text style={styles.currentValue}>
|
||||
@@ -366,21 +371,24 @@ export default function BasalMetabolismDetailScreen() {
|
||||
if (selectedDateData?.value) {
|
||||
return Math.round(selectedDateData.value).toString();
|
||||
}
|
||||
return '--';
|
||||
return t('basalMetabolismDetail.currentData.noData');
|
||||
})()}
|
||||
</Text>
|
||||
<Text style={styles.currentUnit}>千卡</Text>
|
||||
<Text style={styles.currentUnit}>{t('basalMetabolismDetail.currentData.unit')}</Text>
|
||||
</View>
|
||||
{bmrRange && (
|
||||
<Text style={styles.rangeText}>
|
||||
正常范围: {bmrRange.min}-{bmrRange.max} 千卡
|
||||
{t('basalMetabolismDetail.currentData.normalRange', {
|
||||
min: bmrRange.min,
|
||||
max: bmrRange.max
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 基础代谢统计 */}
|
||||
<View style={styles.statsCard}>
|
||||
<Text style={styles.statsTitle}>基础代谢统计</Text>
|
||||
<Text style={styles.statsTitle}>{t('basalMetabolismDetail.stats.title')}</Text>
|
||||
|
||||
{/* Tab 切换 */}
|
||||
<View style={styles.tabContainer}>
|
||||
@@ -390,7 +398,7 @@ export default function BasalMetabolismDetailScreen() {
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={[styles.tabText, activeTab === 'week' && styles.activeTabText]}>
|
||||
按周
|
||||
{t('basalMetabolismDetail.stats.tabs.week')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
@@ -399,7 +407,7 @@ export default function BasalMetabolismDetailScreen() {
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={[styles.tabText, activeTab === 'month' && styles.activeTabText]}>
|
||||
按月
|
||||
{t('basalMetabolismDetail.stats.tabs.month')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
@@ -408,28 +416,30 @@ export default function BasalMetabolismDetailScreen() {
|
||||
{isLoading ? (
|
||||
<View style={styles.loadingChart}>
|
||||
<ActivityIndicator size="large" color="#4ECDC4" />
|
||||
<Text style={styles.loadingText}>加载中...</Text>
|
||||
<Text style={styles.loadingText}>{t('basalMetabolismDetail.chart.loadingText')}</Text>
|
||||
</View>
|
||||
) : error ? (
|
||||
<View style={styles.errorChart}>
|
||||
<Text style={styles.errorText}>加载失败: {error}</Text>
|
||||
<Text style={styles.errorText}>
|
||||
{t('basalMetabolismDetail.chart.error.text', { error })}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.retryButton}
|
||||
onPress={() => {
|
||||
// 重新加载数据
|
||||
// {t('basalMetabolismDetail.comments.reloadData')}
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
fetchBasalMetabolismData(activeTab).then(data => {
|
||||
setChartData(data);
|
||||
setIsLoading(false);
|
||||
}).catch(err => {
|
||||
setError(err instanceof Error ? err.message : '获取数据失败');
|
||||
setError(err instanceof Error ? err.message : t('basalMetabolismDetail.chart.error.fetchFailed'));
|
||||
setIsLoading(false);
|
||||
});
|
||||
}}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={styles.retryText}>重试</Text>
|
||||
<Text style={styles.retryText}>{t('basalMetabolismDetail.chart.error.retry')}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
) : processedChartData.datasets.length > 0 && processedChartData.datasets[0].data.length > 0 ? (
|
||||
@@ -441,7 +451,7 @@ export default function BasalMetabolismDetailScreen() {
|
||||
width={Dimensions.get('window').width - 80}
|
||||
height={220}
|
||||
yAxisLabel=""
|
||||
yAxisSuffix="千卡"
|
||||
yAxisSuffix={t('basalMetabolismDetail.chart.yAxisSuffix')}
|
||||
chartConfig={{
|
||||
backgroundColor: '#ffffff',
|
||||
backgroundGradientFrom: '#ffffff',
|
||||
@@ -470,7 +480,7 @@ export default function BasalMetabolismDetailScreen() {
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.emptyChart}>
|
||||
<Text style={styles.emptyChartText}>暂无数据</Text>
|
||||
<Text style={styles.emptyChartText}>{t('basalMetabolismDetail.chart.empty')}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
@@ -490,56 +500,66 @@ export default function BasalMetabolismDetailScreen() {
|
||||
style={styles.closeButton}
|
||||
onPress={() => setInfoModalVisible(false)}
|
||||
>
|
||||
<Text style={styles.closeButtonText}>×</Text>
|
||||
<Text style={styles.closeButtonText}>{t('basalMetabolismDetail.modal.closeButton')}</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* 标题 */}
|
||||
<Text style={styles.modalTitle}>基础代谢</Text>
|
||||
<Text style={styles.modalTitle}>{t('basalMetabolismDetail.modal.title')}</Text>
|
||||
|
||||
{/* 基础代谢定义 */}
|
||||
<Text style={styles.modalDescription}>
|
||||
基础代谢,也称基础代谢率(BMR),是指人体在完全静息状态下维持基本生命功能(心跳、呼吸、体温调节等)所需的最低能量消耗,通常以卡路里为单位。
|
||||
{t('basalMetabolismDetail.modal.description')}
|
||||
</Text>
|
||||
|
||||
{/* 为什么重要 */}
|
||||
<Text style={styles.sectionTitle}>为什么重要?</Text>
|
||||
<Text style={styles.sectionTitle}>{t('basalMetabolismDetail.modal.sections.importance.title')}</Text>
|
||||
<Text style={styles.sectionContent}>
|
||||
基础代谢占总能量消耗的60-75%,是能量平衡的基础。了解您的基础代谢有助于制定科学的营养计划、优化体重管理策略,以及评估代谢健康状态。
|
||||
{t('basalMetabolismDetail.modal.sections.importance.content')}
|
||||
</Text>
|
||||
|
||||
{/* 正常范围 */}
|
||||
<Text style={styles.sectionTitle}>正常范围</Text>
|
||||
<Text style={styles.sectionTitle}>{t('basalMetabolismDetail.modal.sections.normalRange.title')}</Text>
|
||||
<Text style={styles.formulaText}>
|
||||
- 男性:BMR = 10 × 体重(kg) + 6.25 × 身高(cm) - 5 × 年龄 + 5
|
||||
- {t('basalMetabolismDetail.modal.sections.normalRange.formulas.male')}
|
||||
</Text>
|
||||
<Text style={styles.formulaText}>
|
||||
- 女性:BMR = 10 × 体重(kg) + 6.25 × 身高(cm) - 5 × 年龄 - 161
|
||||
- {t('basalMetabolismDetail.modal.sections.normalRange.formulas.female')}
|
||||
</Text>
|
||||
|
||||
{bmrRange ? (
|
||||
<>
|
||||
<Text style={styles.rangeText}>您的正常区间:{bmrRange.min}-{bmrRange.max}千卡/天</Text>
|
||||
<Text style={styles.rangeText}>
|
||||
{t('basalMetabolismDetail.modal.sections.normalRange.userRange', {
|
||||
min: bmrRange.min,
|
||||
max: bmrRange.max
|
||||
})}
|
||||
</Text>
|
||||
<Text style={styles.rangeNote}>
|
||||
(在公式基础计算值上下浮动15%都属于正常范围)
|
||||
{t('basalMetabolismDetail.modal.sections.normalRange.rangeNote')}
|
||||
</Text>
|
||||
<Text style={styles.userInfoText}>
|
||||
基于您的信息:{userProfile.gender === 'male' ? '男性' : '女性'},{userAge}岁,{userProfile.height}cm,{userProfile.weight}kg
|
||||
{t('basalMetabolismDetail.modal.sections.normalRange.userInfo', {
|
||||
gender: t(`basalMetabolismDetail.gender.${userProfile.gender === 'male' ? 'male' : 'female'}`),
|
||||
age: userAge,
|
||||
height: userProfile.height,
|
||||
weight: userProfile.weight
|
||||
})}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<Text style={styles.rangeText}>请完善基本信息以计算您的代谢率</Text>
|
||||
<Text style={styles.rangeText}>
|
||||
{t('basalMetabolismDetail.modal.sections.normalRange.incompleteInfo')}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* 提高代谢率的策略 */}
|
||||
<Text style={styles.sectionTitle}>提高代谢率的策略</Text>
|
||||
<Text style={styles.strategyText}>科学研究支持以下方法:</Text>
|
||||
<Text style={styles.sectionTitle}>{t('basalMetabolismDetail.modal.sections.strategies.title')}</Text>
|
||||
<Text style={styles.strategyText}>{t('basalMetabolismDetail.modal.sections.strategies.subtitle')}</Text>
|
||||
|
||||
<View style={styles.strategyList}>
|
||||
<Text style={styles.strategyItem}>1.增加肌肉量 (每周2-3次力量训练)</Text>
|
||||
<Text style={styles.strategyItem}>2.高强度间歇训练 (HIIT)</Text>
|
||||
<Text style={styles.strategyItem}>3.充分蛋白质摄入 (体重每公斤1.6-2.2g)</Text>
|
||||
<Text style={styles.strategyItem}>4.保证充足睡眠 (7-9小时/晚)</Text>
|
||||
<Text style={styles.strategyItem}>5.避免过度热量限制 (不低于BMR的80%)</Text>
|
||||
{(t('basalMetabolismDetail.modal.sections.strategies.items', { returnObjects: true }) as string[]).map((item: string, index: number) => (
|
||||
<Text key={index} style={styles.strategyItem}>{item}</Text>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user