From 3f518aa91b28aa9503bf58e8010cc68a8ec6dbb9 Mon Sep 17 00:00:00 2001 From: songyc macbook Date: Wed, 15 Jul 2026 15:52:10 +0900 Subject: [PATCH] chore(l10n): purge stale catalog entries + per-target stringsdata sync routine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 전체 코드 점검 중 발견한 번역 카탈로그 정리: - IOS 카탈로그의 stale 항목 2건 삭제 — "다른 행동 %lld개도 함께 추적 중"은 위젯 타깃 전용 문자열(위젯 카탈로그에 번역 완료 상태로 존재)이고, "프리미엄 상태 (테스트)"는 DEBUG 전용 문구 - DEBUG 프리미엄 토글 라벨을 Text(verbatim:)으로 — 개발용 문구는 카탈로그로 추출하지 않는 컨벤션(§15-6)에 맞춤 (같은 섹션의 다른 문구와 동일) - CLAUDE.md l10n 루틴 수정: 카탈로그마다 자기 타깃의 stringsdata만 sync하도록 경로 필터 추가 — 전 타깃을 한꺼번에 넣으면 위젯 전용 문자열이 IOS 카탈로그에 미번역으로 재유입되던 오염의 근본 원인 제거 (이번 stale의 발생 경로) 검증: 전 카탈로그(9개) missing/stale 0건, 카탈로그 의미 diff = stale 2건 삭제뿐 (번역 변경 없음), Debug·Store 스킴 빌드 성공 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7 --- myApp/HaruDanim/CLAUDE.md | 10 +- myApp/HaruDanim/IOS/Localizable.xcstrings | 1424 +++++++++--------- myApp/HaruDanim/IOS/Views/SettingsView.swift | 7 +- 3 files changed, 707 insertions(+), 734 deletions(-) diff --git a/myApp/HaruDanim/CLAUDE.md b/myApp/HaruDanim/CLAUDE.md index 4c0bf3f..4437598 100644 --- a/myApp/HaruDanim/CLAUDE.md +++ b/myApp/HaruDanim/CLAUDE.md @@ -62,9 +62,13 @@ xcodebuild -project Haru_Danim.xcodeproj -scheme Haru_Danim-Store \ 새 `String(localized:)` 문자열 추가 후: ```zsh -# 1) Debug 빌드 → 2) stringsdata 동기화 -files=(${(f)"$(find ~/Library/Developer/Xcode/DerivedData/Haru_Danim-*/ -name '*.stringsdata' -path '*Debug-iphonesimulator*')"}) -xcrun xcstringstool sync IOS/Localizable.xcstrings --stringsdata "${files[@]}" +# 1) Debug 빌드 → 2) stringsdata 동기화 — ⚠️ 카탈로그마다 "자기 타깃"의 stringsdata만 넣을 것 +# (경로의 <타깃명>.build로 구분. 전체를 한꺼번에 넣으면 위젯 전용 문자열이 +# IOS 카탈로그에 미번역으로 추가됐다가 Xcode 재저장 때 stale로 남는 오염이 생긴다) +app_files=(${(f)"$(find ~/Library/Developer/Xcode/DerivedData/Haru_Danim-*/ -name '*.stringsdata' -path '*Debug-iphonesimulator/Haru_Danim.build/*')"}) +xcrun xcstringstool sync IOS/Localizable.xcstrings --stringsdata "${app_files[@]}" +widget_files=(${(f)"$(find ~/Library/Developer/Xcode/DerivedData/Haru_Danim-*/ -name '*.stringsdata' -path '*Debug-iphonesimulator/Haru_DanimWidgets.build/*')"}) +xcrun xcstringstool sync Widgets/Localizable.xcstrings --stringsdata "${widget_files[@]}" # 3) python으로 en/ja 번역 채움 → 4) sync 재실행으로 포맷 정규화 → 5) missing 확인 ``` diff --git a/myApp/HaruDanim/IOS/Localizable.xcstrings b/myApp/HaruDanim/IOS/Localizable.xcstrings index 6470b59..daa9634 100644 --- a/myApp/HaruDanim/IOS/Localizable.xcstrings +++ b/myApp/HaruDanim/IOS/Localizable.xcstrings @@ -1,254 +1,6 @@ { "sourceLanguage" : "ko", "strings" : { - "'%@ · %@' %@ 진행률: %@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "'%@ · %@' %@ progress: %@" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "「%@ · %@」の%@進捗率: %@" - } - }, - "ko" : { - "stringUnit" : { - "state" : "new", - "value" : "'%1$@ · %2$@' %3$@ 진행률: %4$@" - } - } - } - }, - "'%@' %@ 진행률은 %lld%%예요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "'%@' %@ progress is %lld%%." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "「%@」の%@進捗率は%lld%%です。" - } - }, - "ko" : { - "stringUnit" : { - "state" : "new", - "value" : "'%1$@' %2$@ 진행률은 %3$lld%%예요." - } - } - } - }, - "‘%@’ 꼬리표를 삭제할까요? 행동은 삭제되지 않아요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete the tag ‘%@’? Its actions won't be deleted." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "タグ「%@」を削除しますか?アクションは削除されません。" - } - } - } - }, - "‘%@’ 목표를 달성했나요?" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Did you achieve the goal ‘%@’?" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "目標「%@」を達成しましたか?" - } - } - } - }, - "‘%@’ 목표를 삭제할까요? 다짐도 함께 삭제됩니다." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete the goal ‘%@’? Its quests will also be deleted." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "目標「%@」を削除しますか?クエストも一緒に削除されます。" - } - } - } - }, - "'%@' 목표에는 아직 다짐이 없어요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The goal '%@' has no quests yet." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "目標「%@」にはまだクエストがありません。" - } - } - } - }, - "‘%@’ 양식을 삭제할까요?" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete the \"%@\" template?" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "「%@」のテンプレートを削除しますか?" - } - } - } - }, - "‘%@’ 양식을 삭제할까요? 이 양식을 쓰는 노트 페이지 %lld개는 빈 캔버스로 표시돼요. (필기와 요소는 그대로 남아요)" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete the \"%1$@\" template? %2$lld note pages using it will show as a blank canvas. (Your handwriting and items are kept.)" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "「%1$@」のテンプレートを削除しますか?このテンプレートを使う%2$lld枚のノートページは白紙のキャンバスとして表示されます。(手書きや要素はそのまま残ります)" - } - }, - "ko" : { - "stringUnit" : { - "state" : "new", - "value" : "‘%1$@’ 양식을 삭제할까요? 이 양식을 쓰는 노트 페이지 %2$lld개는 빈 캔버스로 표시돼요. (필기와 요소는 그대로 남아요)" - } - } - } - }, - "'%@' 측정이 %lld시간을 넘겼어요. 종료를 잊으셨다면 열어서 꺼 주세요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tracking '%@' has passed %lld hours. If you forgot to stop it, open the app and turn it off." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "「%@」の計測が%lld時間を超えました。終了し忘れた場合はアプリで停止してください。" - } - }, - "ko" : { - "stringUnit" : { - "state" : "new", - "value" : "'%1$@' 측정이 %2$lld시간을 넘겼어요. 종료를 잊으셨다면 열어서 꺼 주세요." - } - } - } - }, - "‘%@’ 행동을 삭제할까요? 기록도 함께 삭제됩니다." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete the action ‘%@’? Its records will also be deleted." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "アクション「%@」を削除しますか?記録も一緒に削除されます。" - } - } - } - }, - "'%@'은(는) 시간 측정 행동이에요. 측정 시작/종료를 사용해 주세요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "'%@' is a time-tracking action. Use start/stop tracking instead." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "「%@」は時間計測タイプです。計測の開始/終了を使ってください。" - } - } - } - }, - "'%@'은(는) 횟수 기록 행동이에요. 횟수 추가를 사용해 주세요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "'%@' is a count action. Use add count instead." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "「%@」は回数記録タイプです。回数の追加を使ってください。" - } - } - } - }, - "‘글래스 버블 메뉴’를 고르면 하단 탭바 대신 화면 하단 중앙의 버튼을 눌렀을 때 유리 구슬 모양의 탭 버튼들이 부채꼴로 퍼져요. 모든 탭이 엄지 반경 안에 있어 한 손으로 조작하기 편해요. (실험적 기능)" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Choose 'Glass Bubble Menu' to replace the bottom tab bar: tap the button at the bottom center and glass-orb tab buttons fan out in an arc. Every tab stays within thumb reach for easy one-handed use. (Experimental)" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "「ガラスバブルメニュー」を選ぶと、下部タブバーの代わりに画面下中央のボタンをタップしたとき、ガラス玉のタブボタンが扇形に広がります。すべてのタブが親指の届く範囲にあり、片手でも操作しやすいです。(実験的機能)" - } - } - } - }, - "‘글래스 버블 메뉴’를 고르면 하단 탭바 대신 화면 하단 중앙의 버튼을 눌렀을 때 유리 구슬 모양의 탭 버튼들이 부채꼴로 퍼져요. 모든 탭이 엄지 반경 안에 있어 한 손으로 조작하기 편해요. (실험적 기능)\n‘두 번 눌러 바로 이동’을 켜면 메뉴가 닫혀 있을 때 버튼을 빠르게 두 번 눌러 선택한 탭으로 바로 이동해요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Choose ‘Glass Bubble Menu’ to replace the bottom tab bar with a single button at the bottom center — tap it and glass-orb tab buttons fan out. Every tab stays within thumb reach for one-handed use. (Experimental)\nTurn on ‘Double-Tap to Jump’ to jump straight to a chosen tab by quickly tapping the button twice while the menu is closed." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "「ガラスバブルメニュー」を選ぶと、下部タブバーの代わりに画面下中央のボタン1つになり、押すとガラス玉のタブボタンが扇状に広がります。すべてのタブが親指の届く範囲にあり、片手で操作しやすいです。(実験的機能)\n「ダブルタップでジャンプ」をオンにすると、メニューが閉じているときにボタンを素早く2回押して、選んだタブへすぐ移動できます。" - } - } - } - }, "\"시리야, 하루 다님에서 측정 시작\" — 말과 단축어 자동화로 기록해요." : { "localizations" : { "en" : { @@ -281,94 +33,6 @@ } } }, - "%@ · %@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ · %@" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ · %@" - } - }, - "ko" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$@ · %2$@" - } - } - } - }, - "%@ · %@ %@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ · %@ %@" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ · %@ %@" - } - }, - "ko" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$@ · %2$@ %3$@" - } - } - } - }, - "%@ / %@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%1$@ / %2$@" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "%1$@ / %2$@" - } - }, - "ko" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$@ / %2$@" - } - } - } - }, - "%@ / 한도 %@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%1$@ / limit %2$@" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "%1$@ / 上限%2$@" - } - }, - "ko" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$@ / 한도 %2$@" - } - } - } - }, "%@ %@" : { "localizations" : { "en" : { @@ -479,6 +143,50 @@ } } }, + "%@ / %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / %2$@" + } + }, + "ko" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@ / %2$@" + } + } + } + }, + "%@ / 한도 %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / limit %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / 上限%2$@" + } + }, + "ko" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@ / 한도 %2$@" + } + } + } + }, "%@ ~ %@" : { "localizations" : { "en" : { @@ -501,6 +209,50 @@ } } }, + "%@ · %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ · %@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ · %@" + } + }, + "ko" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@ · %2$@" + } + } + } + }, + "%@ · %@ %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ · %@ %@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ · %@ %@" + } + }, + "ko" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@ · %2$@ %3$@" + } + } + } + }, "%@ 기록" : { "localizations" : { "en" : { @@ -748,28 +500,6 @@ } } }, - "%lld/%lld" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld/%lld" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld/%lld" - } - }, - "ko" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$lld/%2$lld" - } - } - } - }, "%lld%%" : { "localizations" : { "en" : { @@ -802,6 +532,28 @@ } } }, + "%lld/%lld" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld/%lld" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld/%lld" + } + }, + "ko" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$lld/%2$lld" + } + } + } + }, "%lld개" : { "localizations" : { "en" : { @@ -1000,6 +752,120 @@ } } }, + "'%@ · %@' %@ 진행률: %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "'%@ · %@' %@ progress: %@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "「%@ · %@」の%@進捗率: %@" + } + }, + "ko" : { + "stringUnit" : { + "state" : "new", + "value" : "'%1$@ · %2$@' %3$@ 진행률: %4$@" + } + } + } + }, + "'%@' %@ 진행률은 %lld%%예요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "'%@' %@ progress is %lld%%." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "「%@」の%@進捗率は%lld%%です。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "new", + "value" : "'%1$@' %2$@ 진행률은 %3$lld%%예요." + } + } + } + }, + "'%@' 목표에는 아직 다짐이 없어요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The goal '%@' has no quests yet." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標「%@」にはまだクエストがありません。" + } + } + } + }, + "'%@' 측정이 %lld시간을 넘겼어요. 종료를 잊으셨다면 열어서 꺼 주세요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tracking '%@' has passed %lld hours. If you forgot to stop it, open the app and turn it off." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "「%@」の計測が%lld時間を超えました。終了し忘れた場合はアプリで停止してください。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "new", + "value" : "'%1$@' 측정이 %2$lld시간을 넘겼어요. 종료를 잊으셨다면 열어서 꺼 주세요." + } + } + } + }, + "'%@'은(는) 시간 측정 행동이에요. 측정 시작/종료를 사용해 주세요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "'%@' is a time-tracking action. Use start/stop tracking instead." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "「%@」は時間計測タイプです。計測の開始/終了を使ってください。" + } + } + } + }, + "'%@'은(는) 횟수 기록 행동이에요. 횟수 추가를 사용해 주세요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "'%@' is a count action. Use add count instead." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "「%@」は回数記録タイプです。回数の追加を使ってください。" + } + } + } + }, "+%lld" : { "localizations" : { "en" : { @@ -1064,6 +930,38 @@ } } }, + "10초" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "10 sec" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "10秒" + } + } + } + }, + "12시간" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "12 hours" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "12時間" + } + } + } + }, "1분" : { "localizations" : { "en" : { @@ -1112,6 +1010,22 @@ } } }, + "30초" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "30 sec" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "30秒" + } + } + } + }, "3시간" : { "localizations" : { "en" : { @@ -1176,102 +1090,6 @@ } } }, - "10초" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "10 sec" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "10秒" - } - } - } - }, - "12시간" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "12 hours" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "12時間" - } - } - } - }, - "30초" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "30 sec" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "30秒" - } - } - } - }, - "① 대상 선택" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "① Choose a target" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "① 対象を選択" - } - } - } - }, - "② 주기 선택" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "② Choose a cycle" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "② 周期を選択" - } - } - } - }, - "③ 목표량과 방향" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "③ Amount & direction" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "③ 目標量と方向" - } - } - } - }, "CSV 만들기" : { "localizations" : { "en" : { @@ -1304,54 +1122,6 @@ } } }, - "iCloud 동기화는 네트워크 상황에 따라 몇 초에서 몇 분까지 걸릴 수 있어요. 다른 기기의 기록이 안 보이면 모음 탭 왼쪽 위 새로고침 버튼을 눌러 주세요. 그래도 안 보이면 잠시 후 다시 시도하거나 두 기기 모두 인터넷 연결을 확인해 주세요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "iCloud sync can take from a few seconds to a few minutes depending on the network. If records from another device are missing, tap the refresh button at the top left of the Home tab. If they still don’t appear, try again shortly or check the internet connection on both devices." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "iCloud同期はネットワーク状況により数秒〜数分かかることがあります。ほかのデバイスの記録が見えないときは、ホームタブ左上の更新ボタンをタップしてください。それでも表示されない場合は、しばらくしてから再試行するか、両方のデバイスのインターネット接続を確認してください。" - } - } - } - }, - "iCloud로 기기 간 데이터가 동기화돼요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Your data syncs across devices via iCloud." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "iCloudでデバイス間のデータが同期されます。" - } - } - } - }, - "iCloud로 아이패드 등 다른 기기와 데이터가 동기화돼요. 변경 사항은 앱을 완전히 종료했다가 다시 실행하면 적용돼요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sync your data with iPad and other devices via iCloud. Changes take effect after fully quitting and relaunching the app." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "iCloudでiPadなどの他のデバイスとデータを同期します。変更はアプリを完全に終了して再起動すると反映されます。" - } - } - } - }, "PDF 가져오기" : { "localizations" : { "en" : { @@ -1400,6 +1170,236 @@ } } }, + "iCloud 동기화는 네트워크 상황에 따라 몇 초에서 몇 분까지 걸릴 수 있어요. 다른 기기의 기록이 안 보이면 모음 탭 왼쪽 위 새로고침 버튼을 눌러 주세요. 그래도 안 보이면 잠시 후 다시 시도하거나 두 기기 모두 인터넷 연결을 확인해 주세요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "iCloud sync can take from a few seconds to a few minutes depending on the network. If records from another device are missing, tap the refresh button at the top left of the Home tab. If they still don’t appear, try again shortly or check the internet connection on both devices." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "iCloud同期はネットワーク状況により数秒〜数分かかることがあります。ほかのデバイスの記録が見えないときは、ホームタブ左上の更新ボタンをタップしてください。それでも表示されない場合は、しばらくしてから再試行するか、両方のデバイスのインターネット接続を確認してください。" + } + } + } + }, + "iCloud로 기기 간 데이터가 동기화돼요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Your data syncs across devices via iCloud." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "iCloudでデバイス間のデータが同期されます。" + } + } + } + }, + "iCloud로 아이패드 등 다른 기기와 데이터가 동기화돼요. 변경 사항은 앱을 완전히 종료했다가 다시 실행하면 적용돼요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sync your data with iPad and other devices via iCloud. Changes take effect after fully quitting and relaunching the app." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "iCloudでiPadなどの他のデバイスとデータを同期します。変更はアプリを完全に終了して再起動すると反映されます。" + } + } + } + }, + "‘%@’ 꼬리표를 삭제할까요? 행동은 삭제되지 않아요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete the tag ‘%@’? Its actions won't be deleted." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "タグ「%@」を削除しますか?アクションは削除されません。" + } + } + } + }, + "‘%@’ 목표를 달성했나요?" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Did you achieve the goal ‘%@’?" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標「%@」を達成しましたか?" + } + } + } + }, + "‘%@’ 목표를 삭제할까요? 다짐도 함께 삭제됩니다." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete the goal ‘%@’? Its quests will also be deleted." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標「%@」を削除しますか?クエストも一緒に削除されます。" + } + } + } + }, + "‘%@’ 양식을 삭제할까요?" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete the \"%@\" template?" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "「%@」のテンプレートを削除しますか?" + } + } + } + }, + "‘%@’ 양식을 삭제할까요? 이 양식을 쓰는 노트 페이지 %lld개는 빈 캔버스로 표시돼요. (필기와 요소는 그대로 남아요)" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete the \"%1$@\" template? %2$lld note pages using it will show as a blank canvas. (Your handwriting and items are kept.)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "「%1$@」のテンプレートを削除しますか?このテンプレートを使う%2$lld枚のノートページは白紙のキャンバスとして表示されます。(手書きや要素はそのまま残ります)" + } + }, + "ko" : { + "stringUnit" : { + "state" : "new", + "value" : "‘%1$@’ 양식을 삭제할까요? 이 양식을 쓰는 노트 페이지 %2$lld개는 빈 캔버스로 표시돼요. (필기와 요소는 그대로 남아요)" + } + } + } + }, + "‘%@’ 행동을 삭제할까요? 기록도 함께 삭제됩니다." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete the action ‘%@’? Its records will also be deleted." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アクション「%@」を削除しますか?記録も一緒に削除されます。" + } + } + } + }, + "‘글래스 버블 메뉴’를 고르면 하단 탭바 대신 화면 하단 중앙의 버튼을 눌렀을 때 유리 구슬 모양의 탭 버튼들이 부채꼴로 퍼져요. 모든 탭이 엄지 반경 안에 있어 한 손으로 조작하기 편해요. (실험적 기능)" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choose 'Glass Bubble Menu' to replace the bottom tab bar: tap the button at the bottom center and glass-orb tab buttons fan out in an arc. Every tab stays within thumb reach for easy one-handed use. (Experimental)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "「ガラスバブルメニュー」を選ぶと、下部タブバーの代わりに画面下中央のボタンをタップしたとき、ガラス玉のタブボタンが扇形に広がります。すべてのタブが親指の届く範囲にあり、片手でも操作しやすいです。(実験的機能)" + } + } + } + }, + "‘글래스 버블 메뉴’를 고르면 하단 탭바 대신 화면 하단 중앙의 버튼을 눌렀을 때 유리 구슬 모양의 탭 버튼들이 부채꼴로 퍼져요. 모든 탭이 엄지 반경 안에 있어 한 손으로 조작하기 편해요. (실험적 기능)\n‘두 번 눌러 바로 이동’을 켜면 메뉴가 닫혀 있을 때 버튼을 빠르게 두 번 눌러 선택한 탭으로 바로 이동해요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choose ‘Glass Bubble Menu’ to replace the bottom tab bar with a single button at the bottom center — tap it and glass-orb tab buttons fan out. Every tab stays within thumb reach for one-handed use. (Experimental)\nTurn on ‘Double-Tap to Jump’ to jump straight to a chosen tab by quickly tapping the button twice while the menu is closed." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "「ガラスバブルメニュー」を選ぶと、下部タブバーの代わりに画面下中央のボタン1つになり、押すとガラス玉のタブボタンが扇状に広がります。すべてのタブが親指の届く範囲にあり、片手で操作しやすいです。(実験的機能)\n「ダブルタップでジャンプ」をオンにすると、メニューが閉じているときにボタンを素早く2回押して、選んだタブへすぐ移動できます。" + } + } + } + }, + "① 대상 선택" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "① Choose a target" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "① 対象を選択" + } + } + } + }, + "② 주기 선택" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "② Choose a cycle" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "② 周期を選択" + } + } + } + }, + "③ 목표량과 방향" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "③ Amount & direction" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "③ 目標量と方向" + } + } + } + }, "가장 나중에 시작한 행동" : { "localizations" : { "en" : { @@ -1432,22 +1432,6 @@ } } }, - "값" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Value" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "値" - } - } - } - }, "가져온 양식은 노트 페이지를 추가할 때 배경으로 고를 수 있어요. 여러 쪽 PDF는 원하는 쪽을 골라 쓸 수 있어요. (쪽은 최대 %lld쪽, PDF 파일은 20MB까지)" : { "localizations" : { "en" : { @@ -1480,6 +1464,22 @@ } } }, + "값" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Value" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "値" + } + } + } + }, "개수 제한 해제" : { "localizations" : { "en" : { @@ -2287,22 +2287,6 @@ } } }, - "꼬리표 — 색으로 분류" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tags — Organize by Color" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "タグ — 色で分類" - } - } - } - }, "꼬리표 (복수 선택 가능)" : { "localizations" : { "en" : { @@ -2319,6 +2303,22 @@ } } }, + "꼬리표 — 색으로 분류" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tags — Organize by Color" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "タグ — 色で分類" + } + } + } + }, "꼬리표 삭제" : { "localizations" : { "en" : { @@ -2799,23 +2799,6 @@ } } }, - "다른 행동 %lld개도 함께 추적 중" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Also tracking %lld other actions" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "他の行動%lld件も同時に計測中" - } - } - } - }, "다시 만들기" : { "localizations" : { "en" : { @@ -2960,22 +2943,6 @@ } } }, - "다짐 (하루)" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Quests (daily)" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "クエスト(1日)" - } - } - } - }, "다짐 %lld개" : { "localizations" : { "en" : { @@ -3008,6 +2975,22 @@ } } }, + "다짐 (하루)" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quests (daily)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クエスト(1日)" + } + } + } + }, "다짐 수정" : { "localizations" : { "en" : { @@ -4216,22 +4199,6 @@ } } }, - "목표 1개 · 다짐 16개" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 goal · 16 quests" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "目標1件・クエスト16件" - } - } - } - }, "목표 1개 + 다짐 진행률" : { "localizations" : { "en" : { @@ -4264,6 +4231,22 @@ } } }, + "목표 1개 · 다짐 16개" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 goal · 16 quests" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標1件・クエスト16件" + } + } + } + }, "목표 2개" : { "localizations" : { "en" : { @@ -4600,22 +4583,6 @@ } } }, - "목표·다짐" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Goals & Quests" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "目標・クエスト" - } - } - } - }, "목표+다짐 2개 (위아래)" : { "localizations" : { "en" : { @@ -4632,6 +4599,22 @@ } } }, + "목표·다짐" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Goals & Quests" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標・クエスト" + } + } + } + }, "목표가 끝날 때 다짐들의 평균 달성률이 이 값 이상이면 '달성 완료'로 판정해요. 100%면 모든 다짐을 채워야 달성이에요. 다짐이 없는 목표는 지금처럼 직접 선택해요." : { "localizations" : { "en" : { @@ -4728,22 +4711,6 @@ } } }, - "목표의 하루·주간·월간 진행률을 진행바로 확인해요. (프리미엄)" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Track a goal's daily, weekly, and monthly progress with bars. (Premium)" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "目標の日・週・月の進捗をバーで確認。(プレミアム)" - } - } - } - }, "목표의 하루/주간/월간 진행률(%)을 가져와요. 소속 다짐들의 진행률 평균이에요." : { "localizations" : { "en" : { @@ -4760,6 +4727,22 @@ } } }, + "목표의 하루·주간·월간 진행률을 진행바로 확인해요. (프리미엄)" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Track a goal's daily, weekly, and monthly progress with bars. (Premium)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標の日・週・月の進捗をバーで確認。(プレミアム)" + } + } + } + }, "무료 버전에서는 목표당 다짐을 최대 %lld개까지 만들 수 있어요. 프리미엄으로 업그레이드하면 무제한으로 사용할 수 있습니다." : { "localizations" : { "en" : { @@ -5486,22 +5469,6 @@ } } }, - "색" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Color" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "色" - } - } - } - }, "새로고침" : { "localizations" : { "en" : { @@ -5534,6 +5501,22 @@ } } }, + "색" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "色" + } + } + } + }, "생활" : { "localizations" : { "en" : { @@ -6927,22 +6910,6 @@ } } }, - "오른쪽 위 ‘순서’를 누르면 다짐 순서를 바꿀 수 있어요." : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tap ‘Reorder’ in the top right to reorder quests." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "右上の「順序」からクエストを並べ替えられます。" - } - } - } - }, "오른쪽 위 + 버튼으로 꼬리표를 추가하세요." : { "localizations" : { "en" : { @@ -6975,6 +6942,22 @@ } } }, + "오른쪽 위 ‘순서’를 누르면 다짐 순서를 바꿀 수 있어요." : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tap ‘Reorder’ in the top right to reorder quests." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "右上の「順序」からクエストを並べ替えられます。" + } + } + } + }, "오른쪽 핸들을 끌어서 순서를 바꾼 뒤 ‘완료’를 누르세요. 순서는 저장되어 유지돼요." : { "localizations" : { "en" : { @@ -7183,39 +7166,6 @@ } } }, - "원" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ellipse" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "円" - } - } - } - }, - "월" : { - "comment" : "요일 축약(월요일)", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mon" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "月" - } - } - } - }, "워치 페이스를 길게 눌러 '편집' → 컴플리케이션 자리에서 하루 다님을 골라요. 현재 측정 중인 행동, 특정 목표의 달성률, 특정 다짐의 진행률을 페이스에서 바로 볼 수 있어요. 어떤 목표·다짐을 보여줄지는 컴플리케이션을 탭해 설정해요." : { "localizations" : { "en" : { @@ -7296,6 +7246,22 @@ } } }, + "원" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ellipse" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "円" + } + } + } + }, "원형 게이지" : { "localizations" : { "en" : { @@ -7328,6 +7294,23 @@ } } }, + "월" : { + "comment" : "요일 축약(월요일)", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mon" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "月" + } + } + } + }, "월 구독" : { "localizations" : { "en" : { @@ -10290,23 +10273,6 @@ } } }, - "프리미엄 상태 (테스트)" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Premium status (test)" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "プレミアム状態(テスト)" - } - } - } - }, "프리미엄 알아보기" : { "localizations" : { "en" : { @@ -10500,22 +10466,6 @@ } } }, - "하루 다님 — 하루 습관 및 시간 추적" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Haru Danim — Daily habit & time tracking" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "Haru Danim — 毎日の習慣・時間トラッキング" - } - } - } - }, "하루 다님 · 목표 없음" : { "localizations" : { "en" : { @@ -10548,6 +10498,22 @@ } } }, + "하루 다님 — 하루 습관 및 시간 추적" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haru Danim — Daily habit & time tracking" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haru Danim — 毎日の習慣・時間トラッキング" + } + } + } + }, "하루 다님 일기" : { "localizations" : { "en" : { @@ -10932,22 +10898,6 @@ } } }, - "행동 — 추적의 최소 단위" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actions — the Unit of Tracking" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "アクション — 記録の最小単位" - } - } - } - }, "행동 %lld/%lld개 표시 중" : { "localizations" : { "en" : { @@ -11024,6 +10974,22 @@ } } }, + "행동 — 추적의 최소 단위" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actions — the Unit of Tracking" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アクション — 記録の最小単位" + } + } + } + }, "행동 누적값 가져오기" : { "localizations" : { "en" : { diff --git a/myApp/HaruDanim/IOS/Views/SettingsView.swift b/myApp/HaruDanim/IOS/Views/SettingsView.swift index 6da505d..e80ad20 100644 --- a/myApp/HaruDanim/IOS/Views/SettingsView.swift +++ b/myApp/HaruDanim/IOS/Views/SettingsView.swift @@ -442,10 +442,13 @@ struct PremiumView: View { } #if DEBUG Section { - Toggle("프리미엄 상태 (테스트)", isOn: Binding( + // 개발용 문구는 카탈로그로 추출하지 않는다 (컨벤션 §15-6 — 이 섹션의 다른 문구와 동일) + Toggle(isOn: Binding( get: { premium.isPremium }, set: { premium.setMockPremium($0) } - )) + )) { + Text(verbatim: "프리미엄 상태 (테스트)") + } Button { Task { await premium.clearMockOverride()