diff --git a/myApp/HaruDanim/CLAUDE.md b/myApp/HaruDanim/CLAUDE.md index e95a1bb..c010546 100644 --- a/myApp/HaruDanim/CLAUDE.md +++ b/myApp/HaruDanim/CLAUDE.md @@ -330,3 +330,4 @@ xcrun xcstringstool sync IOS/Localizable.xcstrings --stringsdata "${files[@]}" 7. 화면과 내보내기가 같은 값을 보여야 하는 곳은 ExportBuilder 스냅숏을 공유할 것 (일기 요약이 그 예) 8. 위젯에서 흰색 하드코딩 금지 — `widgetRenderingMode` 분기 (§10) 9. 시뮬레이터 검증 스크린샷은 `xcrun simctl io screenshot`, 런치 인자는 콜드 스타트로 +10. **아이콘 전용 버튼에는 `accessibilityLabel` 필수** (보이스오버가 "버튼"으로만 읽는 것 방지). 여러 요소로 된 셀은 label+value로 한 덩어리 낭독(모음 탭 행동 셀 참고), 타임테이블 블록·점처럼 텍스트 없는 시각 요소는 verbatim 라벨(이름+시각)로 diff --git a/myApp/HaruDanim/IOS/Localizable.xcstrings b/myApp/HaruDanim/IOS/Localizable.xcstrings index 3f40b73..48827ab 100644 --- a/myApp/HaruDanim/IOS/Localizable.xcstrings +++ b/myApp/HaruDanim/IOS/Localizable.xcstrings @@ -2863,6 +2863,54 @@ } } }, + "다음 기간": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Next period" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "次の期間" + } + } + } + }, + "다음 날짜": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Next day" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "次の日" + } + } + } + }, + "다음 달": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Next month" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "次の月" + } + } + } + }, "다이나믹 아일랜드 · 잠금화면": { "localizations": { "en": { @@ -3926,6 +3974,22 @@ } } }, + "메뉴": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Menu" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "メニュー" + } + } + } + }, "메뉴 닫기": { "localizations": { "en": { @@ -6766,6 +6830,22 @@ } } }, + "오늘 %lld회": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld times today" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "今日%lld回" + } + } + } + }, "오늘 0분": { "localizations": { "en": { @@ -7839,6 +7919,54 @@ } } }, + "이전 기간": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Previous period" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "前の期間" + } + } + } + }, + "이전 날짜": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Previous day" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "前の日" + } + } + } + }, + "이전 달": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Previous month" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "前の月" + } + } + } + }, "이하 유지": { "localizations": { "en": { @@ -10289,6 +10417,22 @@ } } }, + "필터 해제": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Clear filter" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "フィルターを解除" + } + } + } + }, "필터와 하루 기준": { "localizations": { "en": { diff --git a/myApp/HaruDanim/IOS/Views/ActionViews.swift b/myApp/HaruDanim/IOS/Views/ActionViews.swift index 45d664f..a6cf820 100644 --- a/myApp/HaruDanim/IOS/Views/ActionViews.swift +++ b/myApp/HaruDanim/IOS/Views/ActionViews.swift @@ -92,6 +92,7 @@ struct ActionListView: View { } label: { Image(systemName: "ellipsis.circle") } + .accessibilityLabel(Text("메뉴")) } ToolbarItem(placement: .topBarTrailing) { Button { @@ -103,6 +104,7 @@ struct ActionListView: View { } label: { Image(systemName: "plus") } + .accessibilityLabel(Text("행동 추가")) } } .sheet(isPresented: $showingAdd) { diff --git a/myApp/HaruDanim/IOS/Views/DiaryView.swift b/myApp/HaruDanim/IOS/Views/DiaryView.swift index ac58340..c46cdea 100644 --- a/myApp/HaruDanim/IOS/Views/DiaryView.swift +++ b/myApp/HaruDanim/IOS/Views/DiaryView.swift @@ -129,6 +129,7 @@ struct DiaryRootView: View { } label: { Image(systemName: "square.and.arrow.up") } + .accessibilityLabel(Text("일기 내보내기")) } } .sheet(isPresented: $showingExport) { @@ -214,6 +215,7 @@ struct DiaryRootView: View { .frame(width: 44, height: 36) .contentShape(Rectangle()) } + .accessibilityLabel(Text("이전 달")) Spacer() Text(monthAnchor.formatted(.dateTime.year().month())) .font(.title3.weight(.bold)) @@ -225,6 +227,7 @@ struct DiaryRootView: View { .frame(width: 44, height: 36) .contentShape(Rectangle()) } + .accessibilityLabel(Text("다음 달")) Button("이번 달") { monthAnchor = math.dayKey(for: .now) } @@ -603,6 +606,7 @@ struct DiaryDetailView: View { } label: { Image(systemName: "ellipsis.circle") } + .accessibilityLabel(Text("메뉴")) } private func pager(_ entry: DiaryEntry) -> some View { @@ -1751,6 +1755,7 @@ private struct DiaryMoodCard: View { } .buttonStyle(.plain) .disabled(readOnly) + .accessibilityLabel(Text("오늘 기분")) } private var emojiPicker: some View { @@ -1884,6 +1889,7 @@ private struct DiaryTodoRow: View { .contentShape(Rectangle()) } .buttonStyle(.plain) + .accessibilityLabel(Text("삭제")) } } } diff --git a/myApp/HaruDanim/IOS/Views/GoalViews.swift b/myApp/HaruDanim/IOS/Views/GoalViews.swift index 8e78383..690828d 100644 --- a/myApp/HaruDanim/IOS/Views/GoalViews.swift +++ b/myApp/HaruDanim/IOS/Views/GoalViews.swift @@ -137,6 +137,7 @@ struct GoalListView: View { } label: { Image(systemName: "plus") } + .accessibilityLabel(Text("목표 추가")) } } } diff --git a/myApp/HaruDanim/IOS/Views/HistoryView.swift b/myApp/HaruDanim/IOS/Views/HistoryView.swift index 4322bcd..0c77a16 100644 --- a/myApp/HaruDanim/IOS/Views/HistoryView.swift +++ b/myApp/HaruDanim/IOS/Views/HistoryView.swift @@ -124,6 +124,7 @@ struct HistoryView: View { } label: { Image(systemName: "square.and.arrow.up") } + .accessibilityLabel(Text("내보내기")) } ToolbarItem(placement: .topBarTrailing) { Button { @@ -133,6 +134,7 @@ struct HistoryView: View { ? "line.3.horizontal.decrease.circle.fill" : "line.3.horizontal.decrease.circle") } + .accessibilityLabel(Text("기록 필터")) } } .sheet(item: $editingSession) { session in @@ -220,6 +222,7 @@ struct HistoryView: View { .contentShape(Rectangle()) } .buttonStyle(.plain) + .accessibilityLabel(Text("필터 해제")) } .foregroundStyle(AppTheme.green) .padding(.leading, 10) @@ -242,6 +245,7 @@ struct HistoryView: View { .frame(width: 44, height: 36) .contentShape(Rectangle()) } + .accessibilityLabel(Text("이전 날짜")) Spacer() Button { showingCalendar = true @@ -283,6 +287,7 @@ struct HistoryView: View { .frame(width: 44, height: 36) .contentShape(Rectangle()) } + .accessibilityLabel(Text("다음 날짜")) Button("오늘") { selectedDayKey = math.dayKey(for: .now) } @@ -649,6 +654,9 @@ struct TimetableView: View { } } .buttonStyle(.plain) + // 색 블록만으로는 보이스오버가 읽을 내용이 없다 — 행동 이름과 시간대를 라벨로 + .accessibilityLabel(Text(verbatim: + "\(item.action.name) \(Format.time(item.range.lowerBound)) ~ \(Format.time(item.range.upperBound))")) .frame(width: max(width - 4, 8), height: height) .offset(x: 2, y: y) } @@ -674,6 +682,8 @@ struct TimetableView: View { .frame(width: size, height: size) } .buttonStyle(.plain) + .accessibilityLabel(Text(verbatim: + "\(item.action.name) +\(item.entry.amount) \(Format.time(item.entry.timestamp))")) .offset(x: min(x, width - size), y: max(y - size / 2, 0)) } } diff --git a/myApp/HaruDanim/IOS/Views/MainView.swift b/myApp/HaruDanim/IOS/Views/MainView.swift index e5754ed..320b4e7 100644 --- a/myApp/HaruDanim/IOS/Views/MainView.swift +++ b/myApp/HaruDanim/IOS/Views/MainView.swift @@ -846,6 +846,7 @@ struct RunningSessionRow: View { .foregroundStyle(AppTheme.yellow) } .buttonStyle(.plain) + .accessibilityLabel(Text("측정 종료")) } } } @@ -906,6 +907,19 @@ struct ActionButtonCell: View { .buttonStyle(PressableButtonStyle()) .sensoryFeedback(.impact(weight: .medium), trigger: bounce) .disabled(isEditing) + // 보이스오버: 셀 전체를 "이름 + 오늘 값" 한 덩어리로 읽는다 (아이콘·타이머 개별 낭독 방지) + .accessibilityLabel(Text(action.name)) + .accessibilityValue(Text(accessibilityValueText)) + } + + /// 보이스오버용 현재 상태 문구 (측정 중이면 상태, 아니면 오늘 누적) + private var accessibilityValueText: String { + switch action.trackingType { + case .time: + return action.isRunning ? String(localized: "측정 중") : todayLabel + case .count: + return String(localized: "오늘 \(todayCount)회") + } } @ViewBuilder diff --git a/myApp/HaruDanim/IOS/Views/StatsTabView.swift b/myApp/HaruDanim/IOS/Views/StatsTabView.swift index 28904ca..20b8551 100644 --- a/myApp/HaruDanim/IOS/Views/StatsTabView.swift +++ b/myApp/HaruDanim/IOS/Views/StatsTabView.swift @@ -87,6 +87,7 @@ struct StatsTabView: View { } label: { Image(systemName: "square.and.arrow.up") } + .accessibilityLabel(Text("내보내기")) } ToolbarItem(placement: .topBarTrailing) { Button { @@ -96,6 +97,7 @@ struct StatsTabView: View { ? "line.3.horizontal.decrease.circle.fill" : "line.3.horizontal.decrease.circle") } + .accessibilityLabel(Text("통계 필터")) } } .sheet(isPresented: $showingFilter) { @@ -150,6 +152,7 @@ struct StatsTabView: View { .frame(width: 44, height: 36) .contentShape(Rectangle()) } + .accessibilityLabel(Text("이전 기간")) Spacer() VStack(spacing: 1) { Text(periodLabel) @@ -168,6 +171,7 @@ struct StatsTabView: View { .frame(width: 44, height: 36) .contentShape(Rectangle()) } + .accessibilityLabel(Text("다음 기간")) Button(currentPeriodLabel) { anchorDayKey = math.dayKey(for: .now) } @@ -247,6 +251,7 @@ struct StatsTabView: View { .contentShape(Rectangle()) } .buttonStyle(.plain) + .accessibilityLabel(Text("필터 해제")) } .foregroundStyle(AppTheme.green) .padding(.leading, 10) diff --git a/myApp/HaruDanim/IOS/Views/TagViews.swift b/myApp/HaruDanim/IOS/Views/TagViews.swift index 39f4de3..10c7868 100644 --- a/myApp/HaruDanim/IOS/Views/TagViews.swift +++ b/myApp/HaruDanim/IOS/Views/TagViews.swift @@ -74,6 +74,7 @@ struct TagListView: View { } label: { Image(systemName: "plus") } + .accessibilityLabel(Text("꼬리표 추가")) } } .sheet(item: $editingTag) { tag in diff --git a/myApp/HaruDanim/Widgets/Localizable.xcstrings b/myApp/HaruDanim/Widgets/Localizable.xcstrings index c6c826e..cd3f95b 100644 --- a/myApp/HaruDanim/Widgets/Localizable.xcstrings +++ b/myApp/HaruDanim/Widgets/Localizable.xcstrings @@ -2649,6 +2649,22 @@ } } }, + "측정 종료": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Stop" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "計測終了" + } + } + } + }, "측정 중": { "localizations": { "en": { diff --git a/myApp/HaruDanim/Widgets/NowRunningWidget.swift b/myApp/HaruDanim/Widgets/NowRunningWidget.swift index e332fd1..075c1ff 100644 --- a/myApp/HaruDanim/Widgets/NowRunningWidget.swift +++ b/myApp/HaruDanim/Widgets/NowRunningWidget.swift @@ -216,6 +216,7 @@ struct NowRunningStopButton: View { .contentShape(.rect) } .buttonStyle(.plain) + .accessibilityLabel(Text("측정 종료")) } }