From 83b77615cf1b7fcf250cfd1fa59179edd972b47a Mon Sep 17 00:00:00 2001 From: richarjiang Date: Fri, 28 Nov 2025 23:48:38 +0800 Subject: [PATCH] feat: Enhance Oxygen Saturation Card with health permissions and loading state management feat(i18n): Add common translations and mood-related strings in English and Chinese fix(i18n): Update metabolism titles for consistency in health translations chore: Update Podfile.lock to include SDWebImage 5.21.4 and other dependency versions refactor(moodCheckins): Improve mood configuration retrieval with optional translation support refactor(sleepHealthKit): Replace useI18n with direct i18n import for sleep quality descriptions --- app/(tabs)/statistics.tsx | 1 + app/mood/calendar.tsx | 51 +++-- app/mood/edit.tsx | 48 ++-- components/FitnessRingsCard.tsx | 78 +++---- components/MoodCard.tsx | 4 +- components/MoodHistoryCard.tsx | 27 ++- components/MoodIntensitySlider.tsx | 6 +- components/NotificationTest.tsx | 6 +- components/statistic/OxygenSaturationCard.tsx | 69 +++--- i18n/en/common.ts | 11 +- i18n/en/health.ts | 4 +- i18n/en/index.ts | 3 + i18n/en/mood.ts | 95 ++++++++ i18n/zh/common.ts | 11 +- i18n/zh/index.ts | 3 + i18n/zh/mood.ts | 95 ++++++++ ios/Podfile.lock | 216 +++++++++--------- services/moodCheckins.ts | 18 +- utils/sleepHealthKit.ts | 20 +- 19 files changed, 512 insertions(+), 254 deletions(-) create mode 100644 i18n/en/mood.ts create mode 100644 i18n/zh/mood.ts diff --git a/app/(tabs)/statistics.tsx b/app/(tabs)/statistics.tsx index 391f671..3c75b7e 100644 --- a/app/(tabs)/statistics.tsx +++ b/app/(tabs)/statistics.tsx @@ -524,6 +524,7 @@ export default function ExploreScreen() { {/* 血氧饱和度卡片 */} diff --git a/app/mood/calendar.tsx b/app/mood/calendar.tsx index 6449a98..796dfd8 100644 --- a/app/mood/calendar.tsx +++ b/app/mood/calendar.tsx @@ -1,5 +1,6 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; import { useAppSelector } from '@/hooks/redux'; +import { useI18n } from '@/hooks/useI18n'; import { useMoodData } from '@/hooks/useMoodData'; import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding'; import { getMoodOptions } from '@/services/moodCheckins'; @@ -61,6 +62,7 @@ const generateCalendarData = (targetDate: Date) => { }; export default function MoodCalendarScreen() { + const { t } = useI18n(); const safeAreaTop = useSafeAreaTop() const params = useLocalSearchParams(); const { fetchMoodRecords, fetchMoodHistoryRecords } = useMoodData(); @@ -89,9 +91,30 @@ export default function MoodCalendarScreen() { return selectLatestMoodRecordByDate(selectedDateString)(state); }); - const moodOptions = getMoodOptions(); - const weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; - const monthNames = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']; + const moodOptions = getMoodOptions(t); + const weekDays = [ + t('mood.calendar.weekDays.monday'), + t('mood.calendar.weekDays.tuesday'), + t('mood.calendar.weekDays.wednesday'), + t('mood.calendar.weekDays.thursday'), + t('mood.calendar.weekDays.friday'), + t('mood.calendar.weekDays.saturday'), + t('mood.calendar.weekDays.sunday'), + ]; + const monthNames = [ + t('mood.calendar.months.january'), + t('mood.calendar.months.february'), + t('mood.calendar.months.march'), + t('mood.calendar.months.april'), + t('mood.calendar.months.may'), + t('mood.calendar.months.june'), + t('mood.calendar.months.july'), + t('mood.calendar.months.august'), + t('mood.calendar.months.september'), + t('mood.calendar.months.october'), + t('mood.calendar.months.november'), + t('mood.calendar.months.december'), + ]; // 生成当前月份的日历数据 const { calendar, today, month, year } = generateCalendarData(currentMonth); @@ -103,7 +126,7 @@ export default function MoodCalendarScreen() { const endDate = dayjs(targetMonth).endOf('month').format('YYYY-MM-DD'); await fetchMoodHistoryRecordsRef.current({ startDate, endDate }); } catch (error) { - console.error('加载月份心情数据失败:', error); + console.error(t('mood.calendar.errors.loadMonthDataFailed'), error); } }, []); @@ -112,7 +135,7 @@ export default function MoodCalendarScreen() { try { await fetchMoodRecordsRef.current(dateString); } catch (error) { - console.error('加载心情记录失败:', error); + console.error(t('mood.calendar.errors.loadDailyDataFailed'), error); } }, []); @@ -235,7 +258,7 @@ export default function MoodCalendarScreen() { router.back()} withSafeTop={false} transparent={true} @@ -255,7 +278,7 @@ export default function MoodCalendarScreen() { > - {year}年{monthNames[month - 1]} + {year} {monthNames[month - 1]} - {selectedDay ? dayjs(currentMonth).date(selectedDay).format('YYYY年M月D日') : '请选择日期'} + {selectedDay ? dayjs(currentMonth).date(selectedDay).format(t('mood.calendar.selectedDate.dateFormat')) : t('mood.calendar.selectedDate.selectDate')} - 记录 + {t('mood.calendar.selectedDate.record')} @@ -343,7 +366,7 @@ export default function MoodCalendarScreen() { {moodOptions.find(m => m.type === selectedDateMood.moodType)?.label} - 强度: {selectedDateMood.intensity} + {t('mood.calendar.selectedDate.intensity')}: {selectedDateMood.intensity} {selectedDateMood.description && ( {selectedDateMood.description} )} @@ -355,14 +378,14 @@ export default function MoodCalendarScreen() { ) : ( - 暂无心情记录 - 点击右上角"记录"按钮添加心情 + {t('mood.calendar.selectedDate.noRecord')} + {t('mood.calendar.selectedDate.noRecordHint')} ) ) : ( - 请先选择一个日期 - 点击日历中的日期,然后点击"记录"按钮添加心情 + {t('mood.calendar.selectedDate.noDateSelected')} + {t('mood.calendar.selectedDate.noDateSelectedHint')} )} diff --git a/app/mood/edit.tsx b/app/mood/edit.tsx index fee65c1..b97c84a 100644 --- a/app/mood/edit.tsx +++ b/app/mood/edit.tsx @@ -3,6 +3,7 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useColorScheme } from '@/hooks/useColorScheme'; +import { useI18n } from '@/hooks/useI18n'; import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding'; import { getMoodOptions, MoodType } from '@/services/moodCheckins'; import { @@ -31,6 +32,7 @@ import { } from 'react-native'; export default function MoodEditScreen() { + const { t } = useI18n(); const safeAreaTop = useSafeAreaTop() const theme = (useColorScheme() ?? 'light') as 'light' | 'dark'; @@ -51,7 +53,7 @@ export default function MoodEditScreen() { const scrollViewRef = useRef(null); const textInputRef = useRef(null); - const moodOptions = getMoodOptions(); + const moodOptions = getMoodOptions(t); // 从 Redux 获取数据 const moodRecords = useAppSelector(selectMoodRecordsByDate(selectedDate)); @@ -95,7 +97,7 @@ export default function MoodEditScreen() { const handleSave = async () => { if (!selectedMood) { - Alert.alert('提示', '请选择心情'); + Alert.alert(t('common.alert'), t('mood.edit.alerts.selectMood')); return; } @@ -120,12 +122,12 @@ export default function MoodEditScreen() { })).unwrap(); } - Alert.alert('成功', existingMood ? '心情记录已更新' : '心情记录已保存', [ - { text: '确定', onPress: () => router.back() } + Alert.alert(t('common.success'), existingMood ? t('mood.edit.alerts.updateSuccess') : t('mood.edit.alerts.saveSuccess'), [ + { text: t('common.confirm'), onPress: () => router.back() } ]); } catch (error) { console.error('保存心情失败:', error); - Alert.alert('错误', '保存心情失败,请重试'); + Alert.alert(t('common.error'), t('mood.edit.alerts.saveError')); } finally { setIsLoading(false); } @@ -135,24 +137,24 @@ export default function MoodEditScreen() { if (!existingMood) return; Alert.alert( - '确认删除', - '确定要删除这条心情记录吗?', + t('mood.edit.alerts.confirmDeleteTitle'), + t('mood.edit.alerts.confirmDelete'), [ - { text: '取消', style: 'cancel' }, + { text: t('common.cancel'), style: 'cancel' }, { - text: '删除', + text: t('common.delete'), style: 'destructive', onPress: async () => { try { setIsDeleting(true); await dispatch(deleteMoodRecord({ id: existingMood.id })).unwrap(); - Alert.alert('成功', '心情记录已删除', [ - { text: '确定', onPress: () => router.back() } + Alert.alert(t('common.success'), t('mood.edit.alerts.deleteSuccess'), [ + { text: t('common.confirm'), onPress: () => router.back() } ]); } catch (error) { console.error('删除心情失败:', error); - Alert.alert('错误', '删除心情失败,请重试'); + Alert.alert(t('common.error'), t('mood.edit.alerts.deleteError')); } finally { setIsDeleting(false); } @@ -183,7 +185,7 @@ export default function MoodEditScreen() { router.back()} withSafeTop={false} transparent={true} @@ -207,13 +209,13 @@ export default function MoodEditScreen() { {/* 日期显示 */} - {dayjs(selectedDate).format('YYYY年M月D日')} + {dayjs(selectedDate).format(t('mood.edit.dateFormat'))} {/* 心情选择 */} - 选择心情 + {t('mood.edit.selectMood')} {moodOptions.map((mood, index) => ( - 心情强度 + {t('mood.edit.intensity')} - 心情日记 - 记录你的心情,珍藏美好回忆 + {t('mood.edit.diary')} + {t('mood.edit.diarySubtitle')} - {isLoading ? '保存中...' : existingMood ? '更新心情' : '保存心情'} + {isLoading ? t('mood.edit.saving') : existingMood ? t('mood.edit.update') : t('mood.edit.save')} {existingMood && ( diff --git a/components/FitnessRingsCard.tsx b/components/FitnessRingsCard.tsx index 87d7ad6..d87c6c0 100644 --- a/components/FitnessRingsCard.tsx +++ b/components/FitnessRingsCard.tsx @@ -1,5 +1,6 @@ import { ROUTES } from '@/constants/Routes'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; +import { useI18n } from '@/hooks/useI18n'; import { ChallengeType } from '@/services/challengesApi'; import { reportChallengeProgress, selectChallengeList } from '@/store/challengesSlice'; import { ActivityRingsData, fetchActivityRingsForDate } from '@/utils/health'; @@ -26,6 +27,7 @@ export function FitnessRingsCard({ selectedDate, resetToken, }: FitnessRingsCardProps) { + const { t } = useI18n(); const dispatch = useAppDispatch(); const challenges = useAppSelector(selectChallengeList); const [activityData, setActivityData] = useState(null); @@ -135,6 +137,24 @@ export function FitnessRingsCard({ const exerciseProgress = Math.min(1, Math.max(0, exerciseMinutes / exerciseMinutesGoal)); const standProgress = Math.min(1, Math.max(0, standHours / standHoursGoal)); + const units = useMemo( + () => ({ + kcal: t('statistics.components.fitness.kcal'), + minutes: t('statistics.components.fitness.minutes'), + hours: t('statistics.components.fitness.hours'), + }), + [t] + ); + + const fitnessRows = useMemo( + () => [ + { key: 'active', value: Math.round(activeCalories), goal: activeCaloriesGoal, unit: units.kcal }, + { key: 'exercise', value: Math.round(exerciseMinutes), goal: exerciseMinutesGoal, unit: units.minutes }, + { key: 'stand', value: Math.round(standHours), goal: standHoursGoal, unit: units.hours }, + ], + [activeCalories, activeCaloriesGoal, exerciseMinutes, exerciseMinutesGoal, standHours, standHoursGoal, units] + ); + const handlePress = () => { router.push(ROUTES.FITNESS_RINGS_DETAIL); }; @@ -191,47 +211,23 @@ export function FitnessRingsCard({ {/* 右侧数据显示 */} - - - {loading ? ( - -- - ) : ( - <> - {Math.round(activeCalories)} - /{activeCaloriesGoal} - - )} - - 千卡 - - - - - {loading ? ( - -- - ) : ( - <> - {Math.round(exerciseMinutes)} - /{exerciseMinutesGoal} - - )} - - 分钟 - - - - - {loading ? ( - -- - ) : ( - <> - {Math.round(standHours)} - /{standHoursGoal} - - )} - - 小时 - + {fitnessRows.map((row) => ( + + + {loading ? ( + -- + ) : ( + <> + {row.value} + + {t('statistics.components.fitnessRings.goal', { goal: row.goal })} + + + )} + + {row.unit} + + ))} diff --git a/components/MoodCard.tsx b/components/MoodCard.tsx index df343c1..a149fbe 100644 --- a/components/MoodCard.tsx +++ b/components/MoodCard.tsx @@ -13,7 +13,7 @@ interface MoodCardProps { export function MoodCard({ moodCheckin, onPress }: MoodCardProps) { const { t } = useTranslation(); - const moodConfig = moodCheckin ? getMoodConfig(moodCheckin.moodType) : null; + const moodConfig = moodCheckin ? getMoodConfig(moodCheckin.moodType, t) : null; const animationRef = useRef(null); useEffect(() => { @@ -122,4 +122,4 @@ const styles = StyleSheet.create({ marginTop: 22, fontFamily: 'AliRegular', }, -}); \ No newline at end of file +}); diff --git a/components/MoodHistoryCard.tsx b/components/MoodHistoryCard.tsx index 02d7100..11e502e 100644 --- a/components/MoodHistoryCard.tsx +++ b/components/MoodHistoryCard.tsx @@ -1,3 +1,4 @@ +import { useI18n } from '@/hooks/useI18n'; import { MoodCheckin, getMoodConfig } from '@/services/moodCheckins'; import dayjs from 'dayjs'; import React from 'react'; @@ -8,7 +9,9 @@ interface MoodHistoryCardProps { title?: string; } -export function MoodHistoryCard({ moodCheckins, title = '心情记录' }: MoodHistoryCardProps) { +export function MoodHistoryCard({ moodCheckins, title }: MoodHistoryCardProps) { + const { t } = useI18n(); + const defaultTitle = t('mood.history.title'); // 计算心情统计 const moodStats = React.useMemo(() => { const stats = { @@ -26,7 +29,7 @@ export function MoodHistoryCard({ moodCheckins, title = '心情记录' }: MoodHi // 计算心情分布 moodCheckins.forEach(checkin => { - const moodLabel = getMoodConfig(checkin.moodType)?.label || checkin.moodType; + const moodLabel = getMoodConfig(checkin.moodType, t)?.label || checkin.moodType; stats.moodDistribution[moodLabel] = (stats.moodDistribution[moodLabel] || 0) + 1; }); @@ -45,11 +48,11 @@ export function MoodHistoryCard({ moodCheckins, title = '心情记录' }: MoodHi return ( - {title} + {title || defaultTitle} {moodCheckins.length === 0 ? ( - 暂无心情记录 + {t('mood.history.noRecords')} ) : ( <> @@ -57,36 +60,36 @@ export function MoodHistoryCard({ moodCheckins, title = '心情记录' }: MoodHi {moodStats.total} - 总记录 + {t('mood.history.totalRecords')} {moodStats.averageIntensity} - 平均强度 + {t('mood.history.averageIntensity')} {moodStats.mostFrequentMood} - 最常见 + {t('mood.history.mostFrequent')} {/* 最近记录 */} - 最近记录 + {t('mood.history.recentRecords')} {recentMoods.map((checkin, index) => { - const moodConfig = getMoodConfig(checkin.moodType); + const moodConfig = getMoodConfig(checkin.moodType, t); return ( - {moodConfig?.emoji} + 😊 {moodConfig?.label} - {dayjs(checkin.createdAt).format('MM月DD日 HH:mm')} + {dayjs(checkin.createdAt).format(t('mood.history.dateTimeFormat'))} - 强度 {checkin.intensity} + {t('mood.history.intensity')} {checkin.intensity} ); diff --git a/components/MoodIntensitySlider.tsx b/components/MoodIntensitySlider.tsx index 2f7bb91..3935b71 100644 --- a/components/MoodIntensitySlider.tsx +++ b/components/MoodIntensitySlider.tsx @@ -6,6 +6,7 @@ import { Text, View, } from 'react-native'; +import { useTranslation } from 'react-i18next'; import { Gesture, GestureDetector, @@ -38,6 +39,7 @@ export default function MoodIntensitySlider({ width = 320, height = 16, // 更粗的进度条 }: MoodIntensitySliderProps) { + const { t } = useTranslation(); const thumbSize = 32; // 合适的触摸区域 const translateX = useSharedValue(0); const isDragging = useSharedValue(0); @@ -175,8 +177,8 @@ export default function MoodIntensitySlider({ {/* 标签 */} - 轻微 - 强烈 + {t('mood.edit.intensityLow')} + {t('mood.edit.intensityHigh')} {/* 刻度 */} diff --git a/components/NotificationTest.tsx b/components/NotificationTest.tsx index eae3b1f..2caab62 100644 --- a/components/NotificationTest.tsx +++ b/components/NotificationTest.tsx @@ -6,11 +6,13 @@ import { TouchableOpacity, View } from 'react-native'; +import { useI18n } from '../hooks/useI18n'; import { useNotifications } from '../hooks/useNotifications'; import { ThemedText } from './ThemedText'; import { ThemedView } from './ThemedView'; export const NotificationTest: React.FC = () => { + const { t } = useI18n(); const { isInitialized, permissionStatus, @@ -95,8 +97,8 @@ export const NotificationTest: React.FC = () => { const handleSendMoodCheckinReminder = async () => { try { - await sendMoodCheckinReminder('心情打卡', '记得记录今天的心情状态哦'); - Alert.alert('成功', '心情打卡提醒已发送'); + await sendMoodCheckinReminder(t('notifications.moodReminder.title'), t('notifications.moodReminder.body')); + Alert.alert(t('common.success'), t('notifications.moodReminder.sent')); } catch (error) { Alert.alert('错误', '发送心情打卡提醒失败'); } diff --git a/components/statistic/OxygenSaturationCard.tsx b/components/statistic/OxygenSaturationCard.tsx index a933538..40ff859 100644 --- a/components/statistic/OxygenSaturationCard.tsx +++ b/components/statistic/OxygenSaturationCard.tsx @@ -1,7 +1,8 @@ -import { fetchOxygenSaturation } from '@/utils/health'; -import { useFocusEffect } from '@react-navigation/native'; +import { ensureHealthPermissions, fetchOxygenSaturation } from '@/utils/health'; +import { HealthKitUtils } from '@/utils/healthKit'; +import { useIsFocused } from '@react-navigation/native'; import dayjs from 'dayjs'; -import React, { useCallback, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import HealthDataCard from './HealthDataCard'; @@ -15,42 +16,52 @@ const OxygenSaturationCard: React.FC = ({ selectedDate }) => { const { t } = useTranslation(); + const isFocused = useIsFocused(); const [oxygenSaturation, setOxygenSaturation] = useState(null); const [loading, setLoading] = useState(false); const loadingRef = useRef(false); - // 获取血氧饱和度数据 - 在页面聚焦、日期变化时触发 - useFocusEffect( - useCallback(() => { - const loadOxygenSaturationData = async () => { - const dateToUse = selectedDate || new Date(); + useEffect(() => { + const loadOxygenSaturationData = async () => { + const dateToUse = selectedDate || new Date(); - // 防止重复请求 - if (loadingRef.current) return; + if (!isFocused) return; + if (!HealthKitUtils.isAvailable()) { + setOxygenSaturation(null); + return; + } - try { - loadingRef.current = true; - setLoading(true); + // 防止重复请求 + if (loadingRef.current) return; - const options = { - startDate: dayjs(dateToUse).startOf('day').toDate().toISOString(), - endDate: dayjs(dateToUse).endOf('day').toDate().toISOString() - }; + try { + loadingRef.current = true; + setLoading(true); - const data = await fetchOxygenSaturation(options); - setOxygenSaturation(data); - } catch (error) { - console.error('OxygenSaturationCard: Failed to get blood oxygen data:', error); + const hasPermission = await ensureHealthPermissions(); + if (!hasPermission) { setOxygenSaturation(null); - } finally { - setLoading(false); - loadingRef.current = false; + return; } - }; - loadOxygenSaturationData(); - }, [selectedDate]) - ); + const options = { + startDate: dayjs(dateToUse).startOf('day').toDate().toISOString(), + endDate: dayjs(dateToUse).endOf('day').toDate().toISOString() + }; + + const data = await fetchOxygenSaturation(options); + setOxygenSaturation(data); + } catch (error) { + console.error('OxygenSaturationCard: Failed to get blood oxygen data:', error); + setOxygenSaturation(null); + } finally { + setLoading(false); + loadingRef.current = false; + } + }; + + loadOxygenSaturationData(); + }, [isFocused, selectedDate]); return ( = ({ ); }; -export default OxygenSaturationCard; \ No newline at end of file +export default OxygenSaturationCard; diff --git a/i18n/en/common.ts b/i18n/en/common.ts index 3408f74..57168fc 100644 --- a/i18n/en/common.ts +++ b/i18n/en/common.ts @@ -2,4 +2,13 @@ export const dateSelector = { backToToday: 'Back to Today', cancel: 'Cancel', confirm: 'Confirm', -}; \ No newline at end of file +}; + +export const common = { + alert: 'Alert', + success: 'Success', + error: 'Error', + delete: 'Delete', + confirm: 'Confirm', + cancel: 'Cancel', +}; diff --git a/i18n/en/health.ts b/i18n/en/health.ts index 66e253e..95e54e9 100644 --- a/i18n/en/health.ts +++ b/i18n/en/health.ts @@ -98,7 +98,7 @@ export const statistics = { addButton: '+ {{amount}}ml', }, metabolism: { - title: 'Basal Metabolism', + title: 'Metabolism', loading: 'Loading...', unit: 'kcal/day', status: { @@ -406,7 +406,7 @@ export const circumferenceDetail = { }; export const basalMetabolismDetail = { - title: 'Basal Metabolism', + title: 'Metabolism', currentData: { title: '{{date}} Basal Metabolism', unit: 'kcal', diff --git a/i18n/en/index.ts b/i18n/en/index.ts index e0dbeb3..32a6558 100644 --- a/i18n/en/index.ts +++ b/i18n/en/index.ts @@ -3,6 +3,7 @@ import * as Common from './common'; import * as Diet from './diet'; import * as Health from './health'; import * as Medication from './medication'; +import * as Mood from './mood'; import * as Personal from './personal'; import * as Weight from './weight'; @@ -13,5 +14,7 @@ export default { ...Medication, ...Weight, ...Challenge, + ...Mood, ...Common, + ...Common.common, // 确保通用翻译被正确导出 }; diff --git a/i18n/en/mood.ts b/i18n/en/mood.ts new file mode 100644 index 0000000..1480a38 --- /dev/null +++ b/i18n/en/mood.ts @@ -0,0 +1,95 @@ +export const mood = { + calendar: { + title: 'Mood Calendar', + weekDays: { + monday: 'Mon', + tuesday: 'Tue', + wednesday: 'Wed', + thursday: 'Thu', + friday: 'Fri', + saturday: 'Sat', + sunday: 'Sun', + }, + months: { + january: 'Jan', + february: 'Feb', + march: 'Mar', + april: 'Apr', + may: 'May', + june: 'Jun', + july: 'Jul', + august: 'Aug', + september: 'Sep', + october: 'Oct', + november: 'Nov', + december: 'Dec', + }, + selectedDate: { + selectDate: 'Please select a date', + record: 'Record', + noRecord: 'No mood records', + noRecordHint: 'Click the "Record" button in the top right to add mood', + noDateSelected: 'Please select a date first', + noDateSelectedHint: 'Click a date in the calendar, then click the "Record" button to add mood', + intensity: 'Intensity', + dateFormat: 'MMMM D, YYYY', + }, + errors: { + loadMonthDataFailed: 'Failed to load monthly mood data', + loadDailyDataFailed: 'Failed to load mood records', + }, + }, + types: { + happy: 'Happy', + excited: 'Excited', + thrilled: 'Thrilled', + calm: 'Calm', + anxious: 'Anxious', + sad: 'Sad', + lonely: 'Lonely', + wronged: 'Wronged', + angry: 'Angry', + tired: 'Tired', + }, + edit: { + title: 'Record Mood', + editTitle: 'Edit Mood', + selectMood: 'Select Mood', + intensity: 'Mood Intensity', + intensityLow: 'Low', + intensityHigh: 'High', + diary: 'Mood Diary', + diarySubtitle: 'Record your feelings and cherish beautiful memories', + placeholder: `How are you feeling today? + +Did you experience anything special? +What made you happy? +Or, what's bothering you? + +Write down your feelings and let these moments become your precious memories...`, + save: 'Save Mood', + update: 'Update Mood', + saving: 'Saving...', + dateFormat: 'MMMM D, YYYY', + alerts: { + selectMood: 'Please select a mood', + saveSuccess: 'Mood record saved', + updateSuccess: 'Mood record updated', + deleteSuccess: 'Mood record deleted', + saveError: 'Failed to save mood, please try again', + deleteError: 'Failed to delete mood, please try again', + confirmDelete: 'Are you sure you want to delete this mood record?', + confirmDeleteTitle: 'Confirm Delete', + }, + }, + history: { + title: 'Mood Records', + noRecords: 'No mood records', + totalRecords: 'Total Records', + averageIntensity: 'Average Intensity', + mostFrequent: 'Most Frequent', + recentRecords: 'Recent Records', + intensity: 'Intensity', + dateTimeFormat: 'MM/DD HH:mm', + }, +}; diff --git a/i18n/zh/common.ts b/i18n/zh/common.ts index 3841b69..0d0c183 100644 --- a/i18n/zh/common.ts +++ b/i18n/zh/common.ts @@ -2,4 +2,13 @@ export const dateSelector = { backToToday: '回到今天', cancel: '取消', confirm: '确定', -}; \ No newline at end of file +}; + +export const common = { + alert: '提示', + success: '成功', + error: '错误', + delete: '删除', + confirm: '确定', + cancel: '取消', +}; diff --git a/i18n/zh/index.ts b/i18n/zh/index.ts index e0dbeb3..32a6558 100644 --- a/i18n/zh/index.ts +++ b/i18n/zh/index.ts @@ -3,6 +3,7 @@ import * as Common from './common'; import * as Diet from './diet'; import * as Health from './health'; import * as Medication from './medication'; +import * as Mood from './mood'; import * as Personal from './personal'; import * as Weight from './weight'; @@ -13,5 +14,7 @@ export default { ...Medication, ...Weight, ...Challenge, + ...Mood, ...Common, + ...Common.common, // 确保通用翻译被正确导出 }; diff --git a/i18n/zh/mood.ts b/i18n/zh/mood.ts new file mode 100644 index 0000000..18328ec --- /dev/null +++ b/i18n/zh/mood.ts @@ -0,0 +1,95 @@ +export const mood = { + calendar: { + title: '心情日历', + weekDays: { + monday: '周一', + tuesday: '周二', + wednesday: '周三', + thursday: '周四', + friday: '周五', + saturday: '周六', + sunday: '周日', + }, + months: { + january: '1月', + february: '2月', + march: '3月', + april: '4月', + may: '5月', + june: '6月', + july: '7月', + august: '8月', + september: '9月', + october: '10月', + november: '11月', + december: '12月', + }, + selectedDate: { + selectDate: '请选择日期', + record: '记录', + noRecord: '暂无心情记录', + noRecordHint: '点击右上角"记录"按钮添加心情', + noDateSelected: '请先选择一个日期', + noDateSelectedHint: '点击日历中的日期,然后点击"记录"按钮添加心情', + intensity: '强度', + dateFormat: 'YYYY年M月D日', + }, + errors: { + loadMonthDataFailed: '加载月份心情数据失败', + loadDailyDataFailed: '加载心情记录失败', + }, + }, + types: { + happy: '开心', + excited: '心动', + thrilled: '兴奋', + calm: '平静', + anxious: '焦虑', + sad: '难过', + lonely: '孤独', + wronged: '委屈', + angry: '生气', + tired: '心累', + }, + edit: { + title: '记录心情', + editTitle: '编辑心情', + selectMood: '选择心情', + intensity: '心情强度', + intensityLow: '轻微', + intensityHigh: '强烈', + diary: '心情日记', + diarySubtitle: '记录你的心情,珍藏美好回忆', + placeholder: `今天的心情如何? + +你经历过什么特别的事情吗? +有什么让你开心的事? +或者,有什么让你感到困扰? + +写下你的感受,让这些时刻成为你珍贵的记忆...`, + save: '保存心情', + update: '更新心情', + saving: '保存中...', + dateFormat: 'YYYY年M月D日', + alerts: { + selectMood: '请选择心情', + saveSuccess: '心情记录已保存', + updateSuccess: '心情记录已更新', + deleteSuccess: '心情记录已删除', + saveError: '保存心情失败,请重试', + deleteError: '删除心情失败,请重试', + confirmDelete: '确定要删除这条心情记录吗?', + confirmDeleteTitle: '确认删除', + }, + }, + history: { + title: '心情记录', + noRecords: '暂无心情记录', + totalRecords: '总记录', + averageIntensity: '平均强度', + mostFrequent: '最常见', + recentRecords: '最近记录', + intensity: '强度', + dateTimeFormat: 'MM月DD日 HH:mm', + }, +}; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7846eff..696c84e 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2288,9 +2288,9 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - SDWebImage (5.21.3): - - SDWebImage/Core (= 5.21.3) - - SDWebImage/Core (5.21.3) + - SDWebImage (5.21.4): + - SDWebImage/Core (= 5.21.4) + - SDWebImage/Core (5.21.4) - SDWebImageAVIFCoder (0.11.1): - libavif/core (>= 0.11.0) - SDWebImage (~> 5.10) @@ -2684,129 +2684,129 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - EXApplication: 296622817d459f46b6c5fe8691f4aac44d2b79e7 - EXConstants: fd688cef4e401dcf798a021cfb5d87c890c30ba3 - EXImageLoader: 189e3476581efe3ad4d1d3fb4735b7179eb26f05 - EXNotifications: 7cff475adb5d7a255a9ea46bbd2589cb3b454506 - Expo: 111394d38f32be09385d4c7f70cc96d2da438d0d - ExpoAppleAuthentication: bc9de6e9ff3340604213ab9031d4c4f7f802623e - ExpoAsset: d839c8eae8124470332408427327e8f88beb2dfd - ExpoBackgroundTask: e0d201d38539c571efc5f9cb661fae8ab36ed61b - ExpoBlur: 2dd8f64aa31f5d405652c21d3deb2d2588b1852f - ExpoCamera: 2a87c210f8955350ea5c70f1d539520b2fc5d940 - ExpoClipboard: af650d14765f19c60ce2a1eaf9dfe6445eff7365 - ExpoFileSystem: 77157a101e03150a4ea4f854b4dd44883c93ae0a - ExpoFont: cf9d90ec1d3b97c4f513211905724c8171f82961 - ExpoGlassEffect: 265fa3d75b46bc58262e4dfa513135fa9dfe4aac - ExpoHaptics: 807476b0c39e9d82b7270349d6487928ce32df84 - ExpoHead: 95a6ee0be1142320bccf07961d6a1502ded5d6ac - ExpoImage: 9c3428921c536ab29e5c6721d001ad5c1f469566 - ExpoImagePicker: d251aab45a1b1857e4156fed88511b278b4eee1c - ExpoKeepAwake: 1a2e820692e933c94a565ec3fbbe38ac31658ffe - ExpoLinearGradient: a464898cb95153125e3b81894fd479bcb1c7dd27 - ExpoLinking: 77455aa013e9b6a3601de03ecfab09858ee1b031 - ExpoLocalization: b852a5d8ec14c5349c1593eca87896b5b3ebfcca - ExpoMediaLibrary: 641a6952299b395159ccd459bd8f5f6764bf55fe - ExpoModulesCore: e8ec7f8727caf51a49d495598303dd420ca994bf - ExpoQuickActions: 31a70aa6a606128de4416a4830e09cfabfe6667f - ExpoSplashScreen: 268b2f128dc04284c21010540a6c4dd9f95003e3 - ExpoSQLite: 7fa091ba5562474093fef09be644161a65e11b3f - ExpoSymbols: 1ae04ce686de719b9720453b988d8bc5bf776c68 - ExpoSystemUI: 2761aa6875849af83286364811d46e8ed8ea64c7 - ExpoUI: b99a1d1ef5352a60bebf4f4fd3a50d2f896ae804 - ExpoWebBrowser: d04a0d6247a0bea4519fbc2ea816610019ad83e0 - EXTaskManager: cbbb80cbccea6487ccca0631809fbba2ed3e5271 + EXApplication: a9d1c46d473d36f61302a9a81db2379441f3f094 + EXConstants: e6e50cdfcb4524f40121d1fdcff24e97b7dcd2fd + EXImageLoader: e501c001bc40b8326605e82e6e80363c80fe06b5 + EXNotifications: 7aab54f0e5f3023122bc95699eaff7c52bacb559 + Expo: e852e4b236f95ef9fee36ea9ab20bc6f59c76a10 + ExpoAppleAuthentication: 414e4316f8e25a2afbc3943cf725579c910f24b8 + ExpoAsset: ee515c16290e521de1870dcdee66d78173fbc533 + ExpoBackgroundTask: e048da30cd2d669c5ba20d5d704bee8dd6da320c + ExpoBlur: b5b7a26572b3c33a11f0b2aa2f95c17c4c393b76 + ExpoCamera: d1894aad960492f4881c1f83967167963365d937 + ExpoClipboard: 99109306a2d9ed2fbd16f6b856e6267b2afa8472 + ExpoFileSystem: 73a9f3f2e0affc61eba5b9326153f64870438af1 + ExpoFont: b881d43057dceb7b31ff767b24f612609e80f60f + ExpoGlassEffect: 3d147d753d3bfe1a5d6b7920560e206e3e98c19e + ExpoHaptics: b48d913e7e5f23816c6f130e525c9a6501b160b5 + ExpoHead: 16bab3395d4328e88e4282a4f6ef4f44b1225c8d + ExpoImage: 6eb842cd07817402640545c41884dd7f5fbfbca5 + ExpoImagePicker: bd0a5c81d7734548f6908a480609257e85d19ea8 + ExpoKeepAwake: 3f5e3ac53627849174f3603271df8e08f174ed4a + ExpoLinearGradient: f9e7182e5253d53b2de4134b69d70bbfc2d50588 + ExpoLinking: 50a65cd7beb6051ffc82f84ffb33961dd5e55a5b + ExpoLocalization: 6c6f0f89ad2822001ab0bc2eb6d4d980c77f080c + ExpoMediaLibrary: 648cee3f5dcba13410ec9cc8ac9a426e89a61a31 + ExpoModulesCore: e37f2bfc6f5b553989e1a67e15b7c5c8bbcac0cc + ExpoQuickActions: 62b9db8a20618be1cc19efa3b562ac963c803d58 + ExpoSplashScreen: f46795cd52cdad65d30e54043f04c86401c4f85d + ExpoSQLite: f9d1202877e12bfa78a58309a3977ee4ea0b1314 + ExpoSymbols: ef7b8ac77ac2d496b1bc3f0f7daf5e19c3a9933a + ExpoSystemUI: 9441d46a8efbf9224d1b2e6b18042452ffd0ed79 + ExpoUI: 821b058da921ea4aa6172b36d080991ea6fb2fae + ExpoWebBrowser: 51218ce6ef35ea769e33409aac87fea3df4b919d + EXTaskManager: 53f87ed11659341c3f3f02c0041498ef293f5684 FBLazyVector: e95a291ad2dadb88e42b06e0c5fb8262de53ec12 hermes-engine: 9f4dfe93326146a1c99eb535b1cb0b857a3cd172 libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 lottie-ios: a881093fab623c467d3bce374367755c272bdd59 - lottie-react-native: cbe3d931a7c24f7891a8e8032c2bb9b2373c4b9c + lottie-react-native: 97a11537edc72d0763edab0c83e8cc8a0b9d8484 PurchasesHybridCommon: a4837eebc889b973668af685d6c23b89a038461d RCTDeprecation: 943572d4be82d480a48f4884f670135ae30bf990 RCTRequired: 8f3cfc90cc25cf6e420ddb3e7caaaabc57df6043 RCTTypeSafety: 16a4144ca3f959583ab019b57d5633df10b5e97c React: 914f8695f9bf38e6418228c2ffb70021e559f92f React-callinvoker: 1c0808402aee0c6d4a0d8e7220ce6547af9fba71 - React-Core: c61410ef0ca6055e204a963992e363227e0fd1c5 - React-Core-prebuilt: 02f0ad625ddd47463c009c2d0c5dd35c0d982599 - React-CoreModules: 1f6d1744b5f9f2ec684a4bb5ced25370f87e5382 - React-cxxreact: 3af79478e8187b63ffc22b794cd42d3fc1f1f2da + React-Core: 4ae98f9e8135b8ddbd7c98730afb6fdae883db90 + React-Core-prebuilt: 8f4cca589c14e8cf8fc6db4587ef1c2056b5c151 + React-CoreModules: e878a90bb19b8f3851818af997dbae3b3b0a27ac + React-cxxreact: 28af9844f6dc87be1385ab521fbfb3746f19563c React-debug: 6328c2228e268846161f10082e80dc69eac2e90a - React-defaultsnativemodule: d635ef36d755321e5d6fc065bd166b2c5a0e9833 - React-domnativemodule: dd28f6d96cd21236e020be2eff6fe0b7d4ec3b66 - React-Fabric: 2e32c3fdbb1fbcf5fde54607e3abe453c6652ce2 - React-FabricComponents: 5ed0cdb81f6b91656cb4d3be432feaa28a58071a - React-FabricImage: 2bc714f818cb24e454f5d3961864373271b2faf8 - React-featureflags: 847642f41fa71ad4eec5e0351badebcad4fe6171 - React-featureflagsnativemodule: c868a544b2c626fa337bcbd364b1befe749f0d3f - React-graphics: 192ec701def5b3f2a07db2814dfba5a44986cff6 - React-hermes: e875778b496c86d07ab2ccaa36a9505d248a254b - React-idlecallbacksnativemodule: 4d57965cdf82c14ee3b337189836cd8491632b76 - React-ImageManager: bd0b99e370b13de82c9cd15f0f08144ff3de079e - React-jserrorhandler: a2fdef4cbcfdcdf3fa9f5d1f7190f7fd4535248d - React-jsi: 89d43d1e7d4d0663f8ba67e0b39eb4e4672c27de - React-jsiexecutor: abe4874aaab90dfee5dec480680220b2f8af07e3 - React-jsinspector: a0b3e051aef842b0b2be2353790ae2b2a5a65a8f - React-jsinspectorcdp: 6346013b2247c6263fbf5199adf4a8751e53bd89 - React-jsinspectornetwork: 26281aa50d49fc1ec93abf981d934698fa95714f - React-jsinspectortracing: 55eedf6d57540507570259a778663b90060bbd6e - React-jsitooling: 0e001113fa56d8498aa8ac28437ac0d36348e51a - React-jsitracing: b713793eb8a5bbc4d86a84e9d9e5023c0f58cbaf - React-logger: 50fdb9a8236da90c0b1072da5c32ee03aeb5bf28 - React-Mapbuffer: 9050ee10c19f4f7fca8963d0211b2854d624973e - React-microtasksnativemodule: f775db9e991c6f3b8ccbc02bfcde22770f96e23b - react-native-render-html: 5afc4751f1a98621b3009432ef84c47019dcb2bd - react-native-safe-area-context: 42a1b4f8774b577d03b53de7326e3d5757fe9513 - react-native-view-shot: fb3c0774edb448f42705491802a455beac1502a2 - react-native-voice: 908a0eba96c8c3d643e4f98b7232c6557d0a6f9c - react-native-webview: b29007f4723bca10872028067b07abacfa1cb35a - React-NativeModulesApple: 8969913947d5b576de4ed371a939455a8daf28aa + React-defaultsnativemodule: afc9d809ec75780f39464a6949c07987fbea488c + React-domnativemodule: 91a233260411d41f27f67aa1358b7f9f0bfd101d + React-Fabric: 21f349b5e93f305a3c38c885902683a9c79cf983 + React-FabricComponents: 47ac634cc9ecc64b30a9997192f510eebe4177e4 + React-FabricImage: 21873acd6d4a51a0b97c133141051c7acb11cc86 + React-featureflags: 653f469f0c3c9dc271d610373e3b6e66a9fd847d + React-featureflagsnativemodule: c91a8a3880e0f4838286402241ead47db43aed28 + React-graphics: b4bdb0f635b8048c652a5d2b73eb8b1ddd950f24 + React-hermes: fcfad3b917400f49026f3232561e039c9d1c34bf + React-idlecallbacksnativemodule: 8cb83207e39f8179ac1d344b6177c6ab3ccebcdc + React-ImageManager: 396128004783fc510e629124dce682d38d1088e7 + React-jserrorhandler: b58b788d788cdbf8bda7db74a88ebfcffc8a0795 + React-jsi: d2c3f8555175371c02da6dfe7ed1b64b55a9d6c0 + React-jsiexecutor: ba537434eb45ee018b590ed7d29ee233fddb8669 + React-jsinspector: f21b6654baf96cb9f71748844a32468a5f73ad51 + React-jsinspectorcdp: 3f8be4830694c3c1c39442e50f8db877966d43f0 + React-jsinspectornetwork: 70e41469565712ad60e11d9c8b8f999b9f7f61eb + React-jsinspectortracing: eccf9bfa4ec7f130d514f215cfb2222dc3c0e270 + React-jsitooling: b376a695f5a507627f7934748533b24eed1751ca + React-jsitracing: 5c8c3273dda2d95191cc0612fb5e71c4d9018d2a + React-logger: c3e2f8a2e284341205f61eef3d4677ab5a309dfd + React-Mapbuffer: 603c18db65844bb81dbe62fee8fcc976eaeb7108 + React-microtasksnativemodule: d77e0c426fce34c23227394c96ca1033b30c813c + react-native-render-html: 984dfe2294163d04bf5fe25d7c9f122e60e05ebe + react-native-safe-area-context: add9b4ba236fe95ec600604d0fc72f395433dd59 + react-native-view-shot: 26174e54ec6b4b7c5d70b86964b747919759adc1 + react-native-voice: f5e8eec2278451d0017eb6a30a6ccc726aca34e0 + react-native-webview: a4f0775a31b73cf13cfc3d2d2b119aa94ec76e49 + React-NativeModulesApple: 1664340b8750d64e0ef3907c5e53d9481f74bcbd React-oscompat: ce47230ed20185e91de62d8c6d139ae61763d09c - React-perflogger: 02b010e665772c7dcb859d85d44c1bfc5ac7c0e4 - React-performancetimeline: 130db956b5a83aa4fb41ddf5ae68da89f3fb1526 + React-perflogger: b1af3cfb3f095f819b2814910000392a8e17ba9f + React-performancetimeline: f9ec65b77bcadbc7bd8b47a6f4b4b697da7b1490 React-RCTActionSheet: 0b14875b3963e9124a5a29a45bd1b22df8803916 - React-RCTAnimation: a7b90fd2af7bb9c084428867445a1481a8cb112e - React-RCTAppDelegate: 3262bedd01263f140ec62b7989f4355f57cec016 - React-RCTBlob: c17531368702f1ebed5d0ada75a7cf5915072a53 - React-RCTFabric: 6409edd8cfdc3133b6cc75636d3b858fdb1d11ea - React-RCTFBReactNativeSpec: c004b27b4fa3bd85878ad2cf53de3bbec85da797 - React-RCTImage: c68078a120d0123f4f07a5ac77bea3bb10242f32 - React-RCTLinking: cf8f9391fe7fe471f96da3a5f0435235eca18c5b - React-RCTNetwork: ca31f7c879355760c2d9832a06ee35f517938a20 - React-RCTRuntime: a6cf4a1e42754fc87f493e538f2ac6b820e45418 - React-RCTSettings: e0e140b2ff4bf86d34e9637f6316848fc00be035 - React-RCTText: 75915bace6f7877c03a840cc7b6c622fb62bfa6b - React-RCTVibration: 25f26b85e5e432bb3c256f8b384f9269e9529f25 + React-RCTAnimation: 60f6eca214a62b9673f64db6df3830cee902b5af + React-RCTAppDelegate: 37734b39bac108af30a0fd9d3e1149ec68b82c28 + React-RCTBlob: 83fbcbd57755caf021787324aac2fe9b028cc264 + React-RCTFabric: a05cb1df484008db3753c8b4a71e4c6d9f1e43a6 + React-RCTFBReactNativeSpec: d58d7ae9447020bbbac651e3b0674422aba18266 + React-RCTImage: 47aba3be7c6c64f956b7918ab933769602406aac + React-RCTLinking: 2dbaa4df2e4523f68baa07936bd8efdfa34d5f31 + React-RCTNetwork: 1fca7455f9dedf7de2b95bec438da06680f3b000 + React-RCTRuntime: 17819dd1dfc8613efaf4cbb9d8686baae4a83e5b + React-RCTSettings: 01bf91c856862354d3d2f642ccb82f3697a4284a + React-RCTText: cb576a3797dcb64933613c522296a07eaafc0461 + React-RCTVibration: 560af8c086741f3525b8456a482cdbe27f9d098e React-rendererconsistency: 2dac03f448ff337235fd5820b10f81633328870d - React-renderercss: 477da167bb96b5ac86d30c5d295412fb853f5453 - React-rendererdebug: 2a1798c6f3ef5f22d466df24c33653edbabb5b89 - React-RuntimeApple: 28cf4d8eb18432f6a21abbed7d801ab7f6b6f0b4 - React-RuntimeCore: 41bf0fd56a00de5660f222415af49879fa49c4f0 - React-runtimeexecutor: 1afb774dde3011348e8334be69d2f57a359ea43e - React-RuntimeHermes: f3b158ea40e8212b1a723a68b4315e7a495c5fc6 - React-runtimescheduler: 3e1e2bec7300bae512533107d8e54c6e5c63fe0f - React-timing: 6fa9883de2e41791e5dc4ec404e5e37f3f50e801 - React-utils: 6e2035b53d087927768649a11a26c4e092448e34 - ReactAppDependencyProvider: 1bcd3527ac0390a1c898c114f81ff954be35ed79 - ReactCodegen: 7d4593f7591f002d137fe40cef3f6c11f13c88cc - ReactCommon: 08810150b1206cc44aecf5f6ae19af32f29151a8 + React-renderercss: c5c6b7a15948dd28facca39a18ac269073718490 + React-rendererdebug: 3c9d5e1634273f5a24d84cc5669f290ce0bdc812 + React-RuntimeApple: 887637d1e12ea8262df7d32bc100467df2302613 + React-RuntimeCore: 91f779835dc4f8f84777fe5dd24f1a22f96454e4 + React-runtimeexecutor: 8bb6b738f37b0ada4a6269e6f8ab1133dea0285c + React-RuntimeHermes: 4cb93de9fa8b1cc753d200dbe61a01b9ec5f5562 + React-runtimescheduler: 83dc28f530bfbd2fce84ed13aa7feebdc24e5af7 + React-timing: 03c7217455d2bff459b27a3811be25796b600f47 + React-utils: 6d46795ae0444ec8a5d9a5f201157b286bf5250a + ReactAppDependencyProvider: c277c5b231881ad4f00cd59e3aa0671b99d7ebee + ReactCodegen: 4c44b74b77fc41ae25b9e2c7e9bd6e2bc772c23f + ReactCommon: e6e232202a447d353e5531f2be82f50f47cbaa9a ReactNativeDependencies: 71ce9c28beb282aa720ea7b46980fff9669f428a RevenueCat: 1e61140a343a77dc286f171b3ffab99ca09a4b57 - RNCAsyncStorage: 3a4f5e2777dae1688b781a487923a08569e27fe4 - RNCMaskedView: d2578d41c59b936db122b2798ba37e4722d21035 - RNCPicker: c8a3584b74133464ee926224463fcc54dfdaebca - RNDateTimePicker: 19ffa303c4524ec0a2dfdee2658198451c16b7f1 - RNDeviceInfo: bcce8752b5043a623fe3c26789679b473f705d3c - RNGestureHandler: 2914750df066d89bf9d8f48a10ad5f0051108ac3 - RNPurchases: 5f3cd4fea5ef2b3914c925b2201dd5cecd31922f - RNReanimated: 1442a577e066e662f0ce1cd1864a65c8e547aee0 - RNScreens: d8d6f1792f6e7ac12b0190d33d8d390efc0c1845 - RNSentry: 1d7b9fdae7a01ad8f9053335b5d44e75c39a955e - RNSVG: 31d6639663c249b7d5abc9728dde2041eb2a3c34 - RNWorklets: 54d8dffb7f645873a58484658ddfd4bd1a9a0bc1 - SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a + RNCAsyncStorage: e85a99325df9eb0191a6ee2b2a842644c7eb29f4 + RNCMaskedView: 3c9d7586e2b9bbab573591dcb823918bc4668005 + RNCPicker: e0149590451d5eae242cf686014a6f6d808f93c7 + RNDateTimePicker: 5e0a759109b63ebc661a4714712361d2d07142fe + RNDeviceInfo: 8b6fa8379062949dd79a009cf3d6b02a9c03ca59 + RNGestureHandler: 6a488ce85c88e82d8610db1108daf04e9b2d5162 + RNPurchases: 121c761620615ae624ba7a74e2e5b94254995e6c + RNReanimated: 00771d2ba7c810124852092eb29c0d1edf50cc58 + RNScreens: dd61bc3a3e6f6901ad833efa411917d44827cf51 + RNSentry: 54f8041cd06d7ccf484171edde72f1b07323fb2e + RNSVG: 2825ee146e0f6a16221e852299943e4cceef4528 + RNWorklets: 83609071441ac7d623f1e0e63b9043f4f345e2a2 + SDWebImage: d0184764be51240d49c761c37f53dd017e1ccaaf SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57 SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 diff --git a/services/moodCheckins.ts b/services/moodCheckins.ts index d50708b..ffad052 100644 --- a/services/moodCheckins.ts +++ b/services/moodCheckins.ts @@ -146,14 +146,26 @@ export type MoodOption = { }; // 获取心情配置 -export function getMoodConfig(moodType: MoodType) { - return MOOD_CONFIG[moodType]; +export function getMoodConfig(moodType: MoodType, t?: (key: string) => string) { + const config = MOOD_CONFIG[moodType]; + + // When translation function is provided, prefer localized labels. + if (t) { + return { + ...config, + label: t(`mood.types.${moodType}`), + }; + } + + return config; } // 获取所有心情选项 -export function getMoodOptions(): MoodOption[] { +export function getMoodOptions(t?: (key: string) => string): MoodOption[] { return Object.entries(MOOD_CONFIG).map(([type, config]) => ({ type: type as MoodType, ...config, + // 如果提供了翻译函数,则使用翻译后的标签,否则使用默认的中文标签 + label: t ? t(`mood.types.${type}`) : config.label, })); } diff --git a/utils/sleepHealthKit.ts b/utils/sleepHealthKit.ts index cc4f514..dfeac97 100644 --- a/utils/sleepHealthKit.ts +++ b/utils/sleepHealthKit.ts @@ -1,4 +1,4 @@ -import { useI18n } from '@/hooks/useI18n'; +import i18n from '@/i18n'; import dayjs from 'dayjs'; import HealthKitManager, { HealthKitUtils } from './healthKit'; @@ -341,27 +341,25 @@ export const calculateSleepScore = ( * 获取睡眠质量描述和建议 */ export const getSleepQualityInfo = (sleepScore: number): { description: string; recommendation: string } => { - const { t } = useI18n(); - if (sleepScore >= 85) { return { - description: t('sleepQuality.excellent.description'), - recommendation: t('sleepQuality.excellent.recommendation') + description: i18n.t('sleepQuality.excellent.description'), + recommendation: i18n.t('sleepQuality.excellent.recommendation') }; } else if (sleepScore >= 70) { return { - description: t('sleepQuality.good.description'), - recommendation: t('sleepQuality.good.recommendation') + description: i18n.t('sleepQuality.good.description'), + recommendation: i18n.t('sleepQuality.good.recommendation') }; } else if (sleepScore >= 50) { return { - description: t('sleepQuality.fair.description'), - recommendation: t('sleepQuality.fair.recommendation') + description: i18n.t('sleepQuality.fair.description'), + recommendation: i18n.t('sleepQuality.fair.recommendation') }; } else { return { - description: t('sleepQuality.poor.description'), - recommendation: t('sleepQuality.poor.recommendation') + description: i18n.t('sleepQuality.poor.description'), + recommendation: i18n.t('sleepQuality.poor.recommendation') }; } };