feat(i18n): 全面实现应用核心功能模块的国际化支持
- 新增 i18n 翻译资源,覆盖睡眠、饮水、体重、锻炼、用药 AI 识别、步数、健身圆环、基础代谢及设置等核心模块 - 重构相关页面及组件(如 SleepDetail, WaterDetail, WorkoutHistory 等)使用 `useI18n` 钩子替换硬编码文本 - 升级 `utils/date` 工具库与 `DateSelector` 组件,支持基于语言环境的日期格式化与显示 - 完善登录页、注销流程及权限申请弹窗的双语提示信息 - 优化部分页面的 UI 细节与字体样式以适配多语言显示
This commit is contained in:
@@ -25,6 +25,7 @@ const CIRCUMFERENCE_TYPES = [
|
||||
{ key: 'calfCircumference', label: '小腿围', color: '#DDA0DD' },
|
||||
];
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding';
|
||||
import { CircumferencePeriod } from '@/services/circumferenceAnalysis';
|
||||
|
||||
@@ -35,6 +36,7 @@ export default function CircumferenceDetailScreen() {
|
||||
const dispatch = useAppDispatch();
|
||||
const userProfile = useAppSelector(selectUserProfile);
|
||||
const { ensureLoggedIn } = useAuthGuard();
|
||||
const { t } = useI18n();
|
||||
|
||||
// 日期相关状态
|
||||
const [selectedIndex, setSelectedIndex] = useState(getTodayIndexInMonth());
|
||||
@@ -78,37 +80,37 @@ export default function CircumferenceDetailScreen() {
|
||||
const measurements = [
|
||||
{
|
||||
key: 'chestCircumference',
|
||||
label: '胸围',
|
||||
label: t('circumferenceDetail.measurements.chest'),
|
||||
value: userProfile?.chestCircumference,
|
||||
color: '#FF6B6B',
|
||||
},
|
||||
{
|
||||
key: 'waistCircumference',
|
||||
label: '腰围',
|
||||
label: t('circumferenceDetail.measurements.waist'),
|
||||
value: userProfile?.waistCircumference,
|
||||
color: '#4ECDC4',
|
||||
},
|
||||
{
|
||||
key: 'upperHipCircumference',
|
||||
label: '上臀围',
|
||||
label: t('circumferenceDetail.measurements.upperHip'),
|
||||
value: userProfile?.upperHipCircumference,
|
||||
color: '#45B7D1',
|
||||
},
|
||||
{
|
||||
key: 'armCircumference',
|
||||
label: '臂围',
|
||||
label: t('circumferenceDetail.measurements.arm'),
|
||||
value: userProfile?.armCircumference,
|
||||
color: '#96CEB4',
|
||||
},
|
||||
{
|
||||
key: 'thighCircumference',
|
||||
label: '大腿围',
|
||||
label: t('circumferenceDetail.measurements.thigh'),
|
||||
value: userProfile?.thighCircumference,
|
||||
color: '#FFEAA7',
|
||||
},
|
||||
{
|
||||
key: 'calfCircumference',
|
||||
label: '小腿围',
|
||||
label: t('circumferenceDetail.measurements.calf'),
|
||||
value: userProfile?.calfCircumference,
|
||||
color: '#DDA0DD',
|
||||
},
|
||||
@@ -243,10 +245,10 @@ export default function CircumferenceDetailScreen() {
|
||||
// 将YYYY-MM-DD格式转换为第几周
|
||||
const weekOfYear = dayjs(item.label).week();
|
||||
const firstWeekOfMonth = dayjs(item.label).startOf('month').week();
|
||||
return `第${weekOfYear - firstWeekOfMonth + 1}周`;
|
||||
return t('circumferenceDetail.chart.weekLabel', { week: weekOfYear - firstWeekOfMonth + 1 });
|
||||
case 'year':
|
||||
// 将YYYY-MM格式转换为月份
|
||||
return dayjs(item.label).format('M月');
|
||||
return t('circumferenceDetail.chart.monthLabel', { month: dayjs(item.label).format('M') });
|
||||
default:
|
||||
return item.label;
|
||||
}
|
||||
@@ -287,7 +289,7 @@ export default function CircumferenceDetailScreen() {
|
||||
|
||||
{/* 头部导航 */}
|
||||
<HeaderBar
|
||||
title="围度统计"
|
||||
title={t('circumferenceDetail.title')}
|
||||
transparent
|
||||
/>
|
||||
|
||||
@@ -338,7 +340,7 @@ export default function CircumferenceDetailScreen() {
|
||||
|
||||
{/* 围度统计 */}
|
||||
<View style={styles.statsCard}>
|
||||
<Text style={styles.statsTitle}>围度统计</Text>
|
||||
<Text style={styles.statsTitle}>{t('circumferenceDetail.title')}</Text>
|
||||
|
||||
{/* Tab 切换 */}
|
||||
<View style={styles.tabContainer}>
|
||||
@@ -348,7 +350,7 @@ export default function CircumferenceDetailScreen() {
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={[styles.tabText, activeTab === 'week' && styles.activeTabText]}>
|
||||
按周
|
||||
{t('circumferenceDetail.tabs.week')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
@@ -357,7 +359,7 @@ export default function CircumferenceDetailScreen() {
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={[styles.tabText, activeTab === 'month' && styles.activeTabText]}>
|
||||
按月
|
||||
{t('circumferenceDetail.tabs.month')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
@@ -366,7 +368,7 @@ export default function CircumferenceDetailScreen() {
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={[styles.tabText, activeTab === 'year' && styles.activeTabText]}>
|
||||
按年
|
||||
{t('circumferenceDetail.tabs.year')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
@@ -390,7 +392,7 @@ export default function CircumferenceDetailScreen() {
|
||||
styles.legendText,
|
||||
!isVisible && styles.legendTextHidden
|
||||
]}>
|
||||
{type.label}
|
||||
{t(`circumferenceDetail.measurements.${type.key.replace('Circumference', '').toLowerCase()}`)}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
@@ -401,17 +403,17 @@ export default function CircumferenceDetailScreen() {
|
||||
{isLoading ? (
|
||||
<View style={styles.loadingChart}>
|
||||
<ActivityIndicator size="large" color="#4ECDC4" />
|
||||
<Text style={styles.loadingText}>加载中...</Text>
|
||||
<Text style={styles.loadingText}>{t('circumferenceDetail.loading')}</Text>
|
||||
</View>
|
||||
) : error ? (
|
||||
<View style={styles.errorChart}>
|
||||
<Text style={styles.errorText}>加载失败: {error}</Text>
|
||||
<Text style={styles.errorText}>{t('circumferenceDetail.error')}: {error}</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.retryButton}
|
||||
onPress={() => dispatch(fetchCircumferenceAnalysis(activeTab))}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={styles.retryText}>重试</Text>
|
||||
<Text style={styles.retryText}>{t('circumferenceDetail.retry')}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
) : processedChartData.datasets.length > 0 ? (
|
||||
@@ -453,8 +455,8 @@ export default function CircumferenceDetailScreen() {
|
||||
<View style={styles.emptyChart}>
|
||||
<Text style={styles.emptyChartText}>
|
||||
{processedChartData.datasets.length === 0 && !isLoading && !error
|
||||
? '暂无数据'
|
||||
: '请选择要显示的围度数据'
|
||||
? t('circumferenceDetail.chart.empty')
|
||||
: t('circumferenceDetail.chart.noSelection')
|
||||
}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -469,12 +471,12 @@ export default function CircumferenceDetailScreen() {
|
||||
setModalVisible(false);
|
||||
setSelectedMeasurement(null);
|
||||
}}
|
||||
title={selectedMeasurement ? `设置${selectedMeasurement.label}` : '设置围度'}
|
||||
title={selectedMeasurement ? t('circumferenceDetail.modal.title', { label: selectedMeasurement.label }) : t('circumferenceDetail.modal.defaultTitle')}
|
||||
items={circumferenceOptions}
|
||||
selectedValue={selectedMeasurement?.currentValue}
|
||||
onValueChange={() => { }} // Real-time update not needed
|
||||
onConfirm={handleUpdateMeasurement}
|
||||
confirmButtonText="确认"
|
||||
confirmButtonText={t('circumferenceDetail.modal.confirm')}
|
||||
pickerHeight={180}
|
||||
/>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user