// // Formatters.swift // Haru_Danim // import Foundation enum Format { /// 1:23:45 또는 23:45 형식 (진행 중 타이머용) static func timer(_ interval: TimeInterval) -> String { let total = Int(interval.rounded(.down)) let h = total / 3600 let m = (total % 3600) / 60 let s = total % 60 if h > 0 { return String(format: "%d:%02d:%02d", h, m, s) } return String(format: "%d:%02d", m, s) } /// "2시간 30분" / "45분" / "30초" 형식 static func durationShort(_ interval: TimeInterval) -> String { let total = Int(interval.rounded()) let h = total / 3600 let m = (total % 3600) / 60 if h > 0 { return m > 0 ? String(localized: "\(h)시간 \(m)분") : String(localized: "\(h)시간") } if m > 0 { return String(localized: "\(m)분") } return String(localized: "\(total)초") } static func weekdayShort(_ weekday: Int) -> String { let names = [ String(localized: "일", comment: "요일 축약(일요일)"), String(localized: "월", comment: "요일 축약(월요일)"), String(localized: "화", comment: "요일 축약(화요일)"), String(localized: "수", comment: "요일 축약(수요일)"), String(localized: "목", comment: "요일 축약(목요일)"), String(localized: "금", comment: "요일 축약(금요일)"), String(localized: "토", comment: "요일 축약(토요일)"), ] let index = (weekday - 1) % 7 return names[max(0, index)] } static func shortDate(_ date: Date) -> String { date.formatted(.dateTime.month(.defaultDigits).day()) } static func fullDate(_ date: Date) -> String { date.formatted(.dateTime.year().month().day().weekday(.short)) } static func time(_ date: Date) -> String { date.formatted(.dateTime.hour().minute()) } static func percent(_ ratio: Double) -> String { "\(Int((ratio * 100).rounded()))%" } /// 횟수 평균 표기 ("3.5회"). String(format:)은 카탈로그로 추출되지 않아 /// 영어·일본어에서 '회'가 그대로 노출되던 것을 지역화한다. static func countAverage(_ value: Double) -> String { let rounded = (value * 10).rounded() / 10 return String(localized: "\(rounded.formatted(.number.precision(.fractionLength(1))))회", comment: "횟수 평균 (소수 1자리)") } /// 숫자와 분리 렌더링되는 횟수 단위 라벨("회") — 모음 탭 셀처럼 숫자를 크게, 단위를 /// 작게 따로 그리는 곳 전용. 단독 "회" 키(en "times")는 1일 때 "1 times"가 되므로 /// 영어 단복수(time/times)를 나눈다. 명시 키를 쓰는 이유: ko 원문이 둘 다 "회"라 /// 리터럴 키로는 두 변형을 가질 수 없음 static func countUnit(_ count: Int) -> String { count == 1 ? String(localized: "count.unit.one", defaultValue: "회") : String(localized: "count.unit.other", defaultValue: "회") } /// 동명 항목 구분 표시 이름 — 이름이 곧 차트 시리즈 키·범례·Identifiable id가 되는 곳 /// (통계 탭·통계 내보내기·⑤ 행동 통계 위젯)에서 같은 이름의 행동/꼬리표가 한 시리즈로 /// 합쳐지거나 id가 충돌하는 것을 막는다. 첫 항목은 이름 그대로, 이후 중복만 /// "이름 (2)", "이름 (3)"… (입력 순서 기준 — 세 표면 모두 같은 배치 순서를 넘기므로 라벨 일치. /// 숫자·괄호는 언어 무관이라 별도 번역 불필요) static func disambiguated(_ names: [String]) -> [String] { var counts: [String: Int] = [:] return names.map { name in let n = (counts[name] ?? 0) + 1 counts[name] = n return n == 1 ? name : "\(name) (\(n))" } } }