fix(review): tracking-type lock on edit, week-label l10n, premium feature list, catalog hygiene
전체 코드 최종 점검(보고)에서 지시받은 A·B·D 항목 처리:
A. 행동 수정 시 추적 방식 변경 잠금
기존 행동을 수정할 때 추적 방식(시간↔횟수) 피커가 활성화되어
있어 도움말("추적 방식은 바꿀 수 없으니")과 어긋났고, 바꾸면
기존 기록이 화면·통계에서 보이지 않는 것처럼 되는 문제.
→ 수정 모드에서 피커 비활성 + "만들 때만 선택할 수 있어요"
푸터 안내(ko/en/ja). 생성 시에는 기존과 동일.
B. 주차 라벨 지역화 2건
- 통계 탭 월간 주차별 꺾은선 x축 "N주차"가 String(localized:)
없이 하드코딩 — en/ja에서 한국어 노출 (내보내기와 불일치)
- ⑤ 행동 통계 위젯 주 단위 라벨 "N주" — 같은 문제 + 앱과 표기
불일치 → 둘 다 기존 번역 키 "%lld주차"(Week %lld/第%lld週) 사용
D. 결제 화면 기능 목록에 '제어 센터에서 실행' 항목 추가(ko/en/ja,
도움말·CLAUDE.md와 일치) + Widgets/InfoPlist.xcstrings의
CFBundleName을 IOS와 동일하게 shouldTranslate=false 처리.
C(시리 문구 미번역 의심)는 오탐 확인 — AppShortcuts 카탈로그는
stringSet(발화 변형) 구조로 ko/en/ja 모두 translated 상태.
검증: Debug·Store 빌드 성공, 영어 실행 통계 월간 화면에서
"Week 1~5" 표시 확인, 결제 화면에 제어 센터 항목 표시 확인,
전 카탈로그 missing 0·stale 0. CLAUDE.md §6.2 갱신.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
This commit is contained in:
parent
49cada45c7
commit
e652aff0e9
@ -162,7 +162,7 @@ xcrun xcstringstool sync IOS/Localizable.xcstrings --stringsdata "${files[@]}"
|
||||
|
||||
### 6.2 행동 탭 (`ActionViews`)
|
||||
- 즐겨찾기 섹션(스와이프로 토글) → 꼬리표별 그룹 → 꼬리표 없음. 탭하면 상세(정보·즐겨찾기·메모 창 토글·누적 통계·삭제)
|
||||
- 추가/수정: 이름, 아이콘(SF Symbols 검색+카테고리 선택기 `SymbolPickerView`/`SymbolCatalog`), 꼬리표 복수 선택(즉석 추가 가능), 추적 방식, 메모 창 여부. 무료 한도 초과 시 프리미엄 안내 얼럿
|
||||
- 추가/수정: 이름, 아이콘(SF Symbols 검색+카테고리 선택기 `SymbolPickerView`/`SymbolCatalog`), 꼬리표 복수 선택(즉석 추가 가능), 추적 방식(**생성 시에만 선택 — 수정에서는 비활성**, 기존 기록의 표시·집계가 뒤섞이는 것 방지), 메모 창 여부. 무료 한도 초과 시 프리미엄 안내 얼럿
|
||||
- ⋯ 메뉴: 꼬리표 순서 변경 시트
|
||||
|
||||
### 6.3 꼬리표 탭 (`TagViews`): 목록(행동 수 표시)·드래그 정렬·수정(이름/색: ColorPicker+프리셋 12색)·스와이프 삭제(행동은 유지)
|
||||
|
||||
@ -7898,6 +7898,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"제어 센터·잠금화면 버튼·액션 버튼에서 행동을 바로 실행해요.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Run actions right from Control Center, the Lock Screen buttons, or the Action button."
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "コントロールセンター・ロック画面ボタン・アクションボタンからアクションを即実行。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"제어 센터를 편집해 '하루 다님'의 '행동 실행' 컨트롤을 추가하면, 화면을 켜자마자 행동을 실행할 수 있어요. 시간형은 시작/종료 토글, 횟수형은 탭마다 +1이에요. 잠금화면 하단 버튼이나 액션 버튼에도 배치할 수 있어요.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
@ -7914,6 +7930,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"제어 센터에서 실행": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Run from Control Center"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "コントロールセンターから実行"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"제어 센터에서 실행 (프리미엄)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
@ -8682,6 +8714,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"추적 방식은 행동을 만들 때만 선택할 수 있어요.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "The tracking type can only be chosen when creating an action."
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "計測方式は行動を作成するときにのみ選択できます。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"취미": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
|
||||
@ -421,7 +421,7 @@ struct ActionEditorView: View {
|
||||
} footer: {
|
||||
Text("행동 버튼의 색은 지정한 꼬리표의 색을 따라요.")
|
||||
}
|
||||
Section("추적 방식") {
|
||||
Section {
|
||||
Picker("추적 방식", selection: $trackingType) {
|
||||
ForEach(TrackingType.allCases) { type in
|
||||
Label(type.label, systemImage: type.symbol).tag(type)
|
||||
@ -429,6 +429,15 @@ struct ActionEditorView: View {
|
||||
}
|
||||
.pickerStyle(.inline)
|
||||
.labelsHidden()
|
||||
// 기존 행동의 추적 방식을 바꾸면 이미 쌓인 기록이 화면·통계에서
|
||||
// 보이지 않는 것처럼 되므로 생성 시에만 선택 가능 (도움말 안내와 일치)
|
||||
.disabled(action != nil)
|
||||
} header: {
|
||||
Text("추적 방식")
|
||||
} footer: {
|
||||
if action != nil {
|
||||
Text("추적 방식은 행동을 만들 때만 선택할 수 있어요.")
|
||||
}
|
||||
}
|
||||
Section {
|
||||
Toggle(isOn: $promptsForNote) {
|
||||
|
||||
@ -387,6 +387,7 @@ struct PremiumView: View {
|
||||
Section {
|
||||
featureRow("infinity", "개수 제한 해제", "행동·목표·다짐을 무제한으로 만들 수 있어요.")
|
||||
featureRow("square.grid.2x2.fill", "홈·잠금화면 위젯", "위젯에서 바로 행동을 실행하고 달성률을 확인해요.")
|
||||
featureRow("switch.2", "제어 센터에서 실행", "제어 센터·잠금화면 버튼·액션 버튼에서 행동을 바로 실행해요.")
|
||||
featureRow("icloud.fill", "아이패드 동기화", "iCloud로 기기 간 데이터가 동기화돼요.")
|
||||
featureRow("applewatch", "애플워치 앱", "워치에서 추적하고 컴플리케이션으로 확인해요.")
|
||||
featureRow("book.closed.fill", "아이패드 일기", "하루 정리와 애플 펜슬 다이어리를 사용해요.")
|
||||
|
||||
@ -542,7 +542,9 @@ struct StatsTabView: View {
|
||||
case .time: agg.seconds(for: action, in: weekRange) / 3600
|
||||
case .count: Double(agg.count(for: action, in: weekRange))
|
||||
}
|
||||
result.append(ActionWeekPoint(weekLabel: "\(index + 1)주차", actionName: action.name, value: value))
|
||||
// String(localized:)로 감싸야 en/ja에서도 지역화됨 (내보내기 ExportBuilder와 동일 키)
|
||||
result.append(ActionWeekPoint(weekLabel: String(localized: "\(index + 1)주차"),
|
||||
actionName: action.name, value: value))
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@ -1,42 +1,43 @@
|
||||
{
|
||||
"sourceLanguage" : "ko",
|
||||
"strings" : {
|
||||
"CFBundleDisplayName" : {
|
||||
"comment" : "위젯 익스텐션 표시 이름",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Haru Danim"
|
||||
"sourceLanguage": "ko",
|
||||
"strings": {
|
||||
"CFBundleDisplayName": {
|
||||
"comment": "위젯 익스텐션 표시 이름",
|
||||
"extractionState": "manual",
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Haru Danim"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Haru Danim"
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Haru Danim"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "하루 다님"
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "하루 다님"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CFBundleName" : {
|
||||
"comment" : "Bundle name",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Haru_DanimWidgets"
|
||||
"CFBundleName": {
|
||||
"comment": "Bundle name",
|
||||
"extractionState": "extracted_with_value",
|
||||
"localizations": {
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "new",
|
||||
"value": "Haru_DanimWidgets"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"shouldTranslate": false
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
"version": "1.0"
|
||||
}
|
||||
@ -402,6 +402,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld주차": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Week %lld"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "第%lld週"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld초": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
|
||||
@ -130,7 +130,9 @@ struct StatsChartProvider: AppIntentTimelineProvider {
|
||||
let week = math.weekRange(containing: cursor)
|
||||
let clipped = max(week.lowerBound, month.lowerBound)..<min(week.upperBound, month.upperBound)
|
||||
for action in actions {
|
||||
points.append(StatsPoint(day: nil, weekLabel: "\(index)주", actionName: action.name,
|
||||
// 앱 통계 탭과 같은 표기("N주차") + String(localized:)로 en/ja 지역화
|
||||
points.append(StatsPoint(day: nil, weekLabel: String(localized: "\(index)주차"),
|
||||
actionName: action.name,
|
||||
value: value(action, in: clipped)))
|
||||
}
|
||||
cursor = week.upperBound
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user