feat(i18n): 전체 UI 다국어 지원 (한국어·영어·일본어)
- 언어 설정 실적용: 변경 시 AppleLanguages 오버라이드 저장, 재시작 안내 footer
- 자동 추출되지 않던 일반 문자열(탭 이름, 픽커 라벨, 상태 뱃지, 포매터,
삼항 문구, 심볼 카테고리 등)을 String(localized:)로 전환
- 위젯·워치 앱·컴플리케이션 타깃에 Localizable.xcstrings 신설
(중복 리소스 방지 pbxproj 예외 추가), xcstringstool sync로 키 추출
- 4개 카탈로그 전체 키(고유 430개)에 en/ja 번역 주입
- 영어의 긴 횟수 단위("times")가 잘리지 않도록 모음 탭 셀 축소 허용
- 검증: -AppleLanguages "(en)"/"(ja)"로 모음 탭·설정 화면 확인
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
This commit is contained in:
parent
1e62c082d3
commit
a6c040cd6a
@ -18,5 +18,9 @@
|
||||
<array>
|
||||
<string>group.com.yechan.HaruDanim</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.yechan.HaruDanim</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -106,6 +106,7 @@
|
||||
membershipExceptions = (
|
||||
HaruDanimWidgets.swift,
|
||||
Info.plist,
|
||||
Localizable.xcstrings,
|
||||
);
|
||||
target = 725A9E832FFCC65000846FFB /* Haru_Danim */;
|
||||
};
|
||||
@ -115,6 +116,7 @@
|
||||
Assets.xcassets,
|
||||
Haru_DanimWatchWidgetsBundle.swift,
|
||||
Info.plist,
|
||||
Localizable.xcstrings,
|
||||
);
|
||||
target = 7263D0DF3000433300EA47FC /* Haru_DanimWatch Watch App */;
|
||||
};
|
||||
@ -662,6 +664,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = "Haru_DanimWatch Watch App/Haru_DanimWatch Watch App.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 5GNS4Z2SYT;
|
||||
@ -677,6 +680,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yechan.HaruDanim.watchkitapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
@ -696,6 +700,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = "Haru_DanimWatch Watch App/Haru_DanimWatch Watch App.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 5GNS4Z2SYT;
|
||||
@ -711,6 +716,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yechan.HaruDanim.watchkitapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
@ -730,6 +736,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = Haru_DanimWatchWidgetsExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 5GNS4Z2SYT;
|
||||
@ -746,6 +753,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.yechan.HaruDanim.watchkitapp.Haru-DanimWatchWidgets";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
@ -764,6 +772,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = Haru_DanimWatchWidgetsExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 5GNS4Z2SYT;
|
||||
@ -780,6 +789,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.yechan.HaruDanim.watchkitapp.Haru-DanimWatchWidgets";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
|
||||
@ -41,9 +41,6 @@
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
queueDebuggingEnableBacktraceRecording = "Yes">
|
||||
<StoreKitConfigurationFileReference
|
||||
identifier = "../../HaruDanim.storekit">
|
||||
</StoreKitConfigurationFileReference>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
@ -54,6 +51,9 @@
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<StoreKitConfigurationFileReference
|
||||
identifier = "../../HaruDanim.storekit">
|
||||
</StoreKitConfigurationFileReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2660"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7263D0DF3000433300EA47FC"
|
||||
BuildableName = "Haru_DanimWatch Watch App.app"
|
||||
BlueprintName = "Haru_DanimWatch Watch App"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "725A9E832FFCC65000846FFB"
|
||||
BuildableName = "Haru_Danim.app"
|
||||
BlueprintName = "Haru_Danim"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
queueDebuggingEnableBacktraceRecording = "Yes">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7263D0DF3000433300EA47FC"
|
||||
BuildableName = "Haru_DanimWatch Watch App.app"
|
||||
BlueprintName = "Haru_DanimWatch Watch App"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7263D0DF3000433300EA47FC"
|
||||
BuildableName = "Haru_DanimWatch Watch App.app"
|
||||
BlueprintName = "Haru_DanimWatch Watch App"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2660"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7263D0F3300044E900EA47FC"
|
||||
BuildableName = "Haru_DanimWatchWidgetsExtension.appex"
|
||||
BlueprintName = "Haru_DanimWatchWidgetsExtension"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "725A9E832FFCC65000846FFB"
|
||||
BuildableName = "Haru_Danim.app"
|
||||
BlueprintName = "Haru_Danim"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7263D0DF3000433300EA47FC"
|
||||
BuildableName = "Haru_DanimWatch Watch App.app"
|
||||
BlueprintName = "Haru_DanimWatch Watch App"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2"
|
||||
queueDebuggingEnableBacktraceRecording = "Yes">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "725A9E832FFCC65000846FFB"
|
||||
BuildableName = "Haru_Danim.app"
|
||||
BlueprintName = "Haru_Danim"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetKind"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetDefaultView"
|
||||
value = "timeline"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetFamily"
|
||||
value = "systemMedium"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7263D0F3300044E900EA47FC"
|
||||
BuildableName = "Haru_DanimWatchWidgetsExtension.appex"
|
||||
BlueprintName = "Haru_DanimWatchWidgetsExtension"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -0,0 +1,125 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2660"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "AA0000000000000000000003"
|
||||
BuildableName = "Haru_DanimWidgets.appex"
|
||||
BlueprintName = "Haru_DanimWidgets"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "725A9E832FFCC65000846FFB"
|
||||
BuildableName = "Haru_Danim.app"
|
||||
BlueprintName = "Haru_Danim"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2"
|
||||
queueDebuggingEnableBacktraceRecording = "Yes">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "2"
|
||||
BundleIdentifier = "com.apple.springboard">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "AA0000000000000000000003"
|
||||
BuildableName = "Haru_DanimWidgets.appex"
|
||||
BlueprintName = "Haru_DanimWidgets"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "725A9E832FFCC65000846FFB"
|
||||
BuildableName = "Haru_Danim.app"
|
||||
BlueprintName = "Haru_Danim"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetKind"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetDefaultView"
|
||||
value = "timeline"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetFamily"
|
||||
value = "systemMedium"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "725A9E832FFCC65000846FFB"
|
||||
BuildableName = "Haru_Danim.app"
|
||||
BlueprintName = "Haru_Danim"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -206,8 +206,12 @@ enum WatchFormat {
|
||||
let total = Int(seconds.rounded())
|
||||
let h = total / 3600
|
||||
let m = (total % 3600) / 60
|
||||
if h > 0 { return m > 0 ? "\(h)시간 \(m)분" : "\(h)시간" }
|
||||
if m > 0 { return "\(m)분" }
|
||||
return "\(total)초"
|
||||
if h > 0 {
|
||||
return m > 0
|
||||
? String(localized: "\(h)시간 \(m)분")
|
||||
: String(localized: "\(h)시간")
|
||||
}
|
||||
if m > 0 { return String(localized: "\(m)분") }
|
||||
return String(localized: "\(total)초")
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,5 +6,9 @@
|
||||
<array>
|
||||
<string>group.com.yechan.HaruDanim</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.yechan.HaruDanim</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
718
myApp/HaruDanim/Haru_DanimWatch Watch App/Localizable.xcstrings
Normal file
718
myApp/HaruDanim/Haru_DanimWatch Watch App/Localizable.xcstrings
Normal file
@ -0,0 +1,718 @@
|
||||
{
|
||||
"sourceLanguage": "ko",
|
||||
"strings": {
|
||||
"%@": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%@ %@": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ %@"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ %@"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "new",
|
||||
"value": "%1$@ %2$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%@ %lld%%": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ %lld%%"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ %lld%%"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "new",
|
||||
"value": "%1$@ %2$lld%%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%@ +%lld": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ +%lld"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ +%lld"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "new",
|
||||
"value": "%1$@ +%2$lld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld%%": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld%%"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld%%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld분": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lldm"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld分"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld시간": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lldh"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld時間"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld시간 %lld분": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lldh %lldm"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld時間%lld分"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "new",
|
||||
"value": "%1$lld시간 %2$lld분"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld초": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%llds"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld秒"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"+%lld": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "+%lld"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "+%lld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iPhone 앱의 설정 → 프리미엄에서 잠금 해제해 주세요": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Unlock in the iPhone app: Settings → Premium"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "iPhoneアプリの設定→プレミアムでロックを解除してください"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iPhone과 연결 중...": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Connecting to iPhone..."
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "iPhoneに接続中..."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iPhone에 연결할 수 없어요. 근처에 있는지 확인해 주세요.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Can't reach your iPhone. Make sure it's nearby."
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "iPhoneに接続できません。近くにあるか確認してください。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"기간": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Period"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "期間"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"꼬리표 없음": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "No tag"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "タグなし"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"다짐": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Quest"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "クエスト"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"다짐 달성률": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Quest Progress"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "クエスト達成率"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"다짐 달성률 (%@)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Quest progress (%@)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "クエスト達成率(%@)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"목표": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Goals"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "目標"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"목표 달성률": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Goal Progress"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "目標達成率"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"목표 달성률 (%@)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Goal progress (%@)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "目標達成率(%@)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"선택한 다짐의 달성률을 표시해요. 한도형 다짐은 상태로 표시돼요. (프리미엄)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Shows the selected quest's progress. Limit quests show their status. (Premium)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "選択したクエストの達成率を表示します。上限型はステータス表示になります。(プレミアム)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"선택한 목표의 하루/주간/월간 달성률을 표시해요. (프리미엄)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Shows the selected goal's daily/weekly/monthly progress. (Premium)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "選択した目標の日/週/月の達成率を表示します。(プレミアム)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"실행 중 아님": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Not running"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "実行中なし"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"없음": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "None"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "なし"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"오늘 %@": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Today %@"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "今日 %@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"오늘 %lld회": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Today %lld times"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "今日%lld回"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"월간": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Monthly"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "月間"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"주간": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Weekly"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "週間"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"초과": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Over"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "超過"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"측정 중": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Tracking"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "計測中"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"측정 중인 행동과 경과 시간을 표시해요. (프리미엄)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Shows the running action and elapsed time. (Premium)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "計測中のアクションと経過時間を表示します。(プレミアム)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"표시할 다짐과 기간을 선택하세요.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Choose a quest and period to display."
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "表示するクエストと期間を選んでください。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"표시할 목표와 기간을 선택하세요.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Choose a goal and period to display."
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "表示する目標と期間を選んでください。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"프리미엄": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Premium"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "プレミアム"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"프리미엄 기능": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Premium Features"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "プレミアム機能"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"하루": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Daily"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "1日"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"하루다님": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "HaruDanim"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "ハルダニム"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"한도": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Limit"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "上限"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"한도 안": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Within"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "上限内"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"한도 초과": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Over limit"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "上限超過"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"현재 현황": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Current Status"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "現在の状況"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": "1.0"
|
||||
}
|
||||
@ -37,9 +37,9 @@ enum WatchSpanOption: String, AppEnum {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .day: return "하루"
|
||||
case .week: return "주간"
|
||||
case .month: return "월간"
|
||||
case .day: return String(localized: "하루")
|
||||
case .week: return String(localized: "주간")
|
||||
case .month: return String(localized: "월간")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
536
myApp/HaruDanim/Haru_DanimWatchWidgets/Localizable.xcstrings
Normal file
536
myApp/HaruDanim/Haru_DanimWatchWidgets/Localizable.xcstrings
Normal file
@ -0,0 +1,536 @@
|
||||
{
|
||||
"sourceLanguage": "ko",
|
||||
"strings": {
|
||||
"%@": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%@ %@": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ %@"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ %@"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "new",
|
||||
"value": "%1$@ %2$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%@ %lld%%": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ %lld%%"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ %lld%%"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "new",
|
||||
"value": "%1$@ %2$lld%%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%@ +%lld": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ +%lld"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%@ +%lld"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "new",
|
||||
"value": "%1$@ +%2$lld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld%%": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld%%"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "%lld%%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"+%lld": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "+%lld"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "+%lld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"기간": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Period"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "期間"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"다짐": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Quest"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "クエスト"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"다짐 달성률": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Quest Progress"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "クエスト達成率"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"다짐 달성률 (%@)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Quest progress (%@)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "クエスト達成率(%@)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"목표": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Goals"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "目標"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"목표 달성률": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Goal Progress"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "目標達成率"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"목표 달성률 (%@)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Goal progress (%@)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "目標達成率(%@)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"선택한 다짐의 달성률을 표시해요. 한도형 다짐은 상태로 표시돼요. (프리미엄)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Shows the selected quest's progress. Limit quests show their status. (Premium)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "選択したクエストの達成率を表示します。上限型はステータス表示になります。(プレミアム)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"선택한 목표의 하루/주간/월간 달성률을 표시해요. (프리미엄)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Shows the selected goal's daily/weekly/monthly progress. (Premium)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "選択した目標の日/週/月の達成率を表示します。(プレミアム)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"실행 중 아님": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Not running"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "実行中なし"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"없음": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "None"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "なし"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"월간": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Monthly"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "月間"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"주간": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Weekly"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "週間"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"초과": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Over"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "超過"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"측정 중": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Tracking"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "計測中"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"측정 중인 행동과 경과 시간을 표시해요. (프리미엄)": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Shows the running action and elapsed time. (Premium)"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "計測中のアクションと経過時間を表示します。(プレミアム)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"표시할 다짐과 기간을 선택하세요.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Choose a quest and period to display."
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "表示するクエストと期間を選んでください。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"표시할 목표와 기간을 선택하세요.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Choose a goal and period to display."
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "表示する目標と期間を選んでください。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"프리미엄": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Premium"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "プレミアム"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"하루": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Daily"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "1日"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"하루다님": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "HaruDanim"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "ハルダニム"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"한도": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Limit"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "上限"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"한도 안": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Within"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "上限内"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"한도 초과": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Over limit"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "上限超過"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"현재 현황": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Current Status"
|
||||
}
|
||||
},
|
||||
"ja": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "現在の状況"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": "1.0"
|
||||
}
|
||||
@ -261,7 +261,7 @@ struct QuestRateComplicationView: View {
|
||||
Image(systemName: quest.achieved(forSpanRaw: entry.spanRaw)
|
||||
? "checkmark.circle.fill" : "exclamationmark.triangle.fill")
|
||||
.font(.system(size: 16))
|
||||
Text(quest.achieved(forSpanRaw: entry.spanRaw) ? "한도 안" : "초과")
|
||||
Text(quest.achieved(forSpanRaw: entry.spanRaw) ? String(localized: "한도 안") : String(localized: "초과"))
|
||||
.font(.system(size: 9))
|
||||
}
|
||||
} else {
|
||||
@ -277,7 +277,7 @@ struct QuestRateComplicationView: View {
|
||||
|
||||
private func inlineText(_ quest: WatchQuestInfo) -> String {
|
||||
if quest.isAtMost {
|
||||
return quest.achieved(forSpanRaw: entry.spanRaw) ? "한도 안" : "한도 초과"
|
||||
return quest.achieved(forSpanRaw: entry.spanRaw) ? String(localized: "한도 안") : String(localized: "한도 초과")
|
||||
}
|
||||
return "\(Int((quest.ratio(forSpanRaw: entry.spanRaw) * 100).rounded()))%"
|
||||
}
|
||||
|
||||
@ -6,5 +6,9 @@
|
||||
<array>
|
||||
<string>group.com.yechan.HaruDanim</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.yechan.HaruDanim</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -151,13 +151,13 @@ enum AppTab: String, CaseIterable, Identifiable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .main: return "모음"
|
||||
case .action: return "행동"
|
||||
case .tag: return "꼬리표"
|
||||
case .goal: return "목표"
|
||||
case .history: return "기록"
|
||||
case .stats: return "통계"
|
||||
case .settings: return "설정"
|
||||
case .main: return String(localized: "모음")
|
||||
case .action: return String(localized: "행동")
|
||||
case .tag: return String(localized: "꼬리표")
|
||||
case .goal: return String(localized: "목표")
|
||||
case .history: return String(localized: "기록")
|
||||
case .stats: return String(localized: "통계")
|
||||
case .settings: return String(localized: "설정")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,14 +26,14 @@ enum SymbolCatalog {
|
||||
}
|
||||
|
||||
private static let rawCategories: [(String, [String])] = [
|
||||
("인기", [
|
||||
(String(localized: "인기"), [
|
||||
"star.fill", "heart.fill", "flame.fill", "bolt.fill", "leaf.fill",
|
||||
"book.fill", "pencil", "figure.walk", "figure.run", "dumbbell.fill",
|
||||
"cup.and.saucer.fill", "fork.knife", "bed.double.fill", "alarm.fill",
|
||||
"laptopcomputer", "gamecontroller.fill", "music.note", "drop.fill",
|
||||
"pills.fill", "brain.head.profile", "checkmark.circle.fill", "timer",
|
||||
]),
|
||||
("운동", [
|
||||
(String(localized: "운동"), [
|
||||
"figure.walk", "figure.run", "figure.hiking", "figure.yoga",
|
||||
"figure.cooldown", "figure.strengthtraining.traditional",
|
||||
"figure.strengthtraining.functional", "figure.core.training",
|
||||
@ -50,7 +50,7 @@ enum SymbolCatalog {
|
||||
"volleyball.fill", "american.football.fill", "cricket.ball.fill",
|
||||
"hockey.puck.fill", "medal.fill", "trophy.fill", "stopwatch.fill",
|
||||
]),
|
||||
("건강", [
|
||||
(String(localized: "건강"), [
|
||||
"heart.fill", "heart.text.square.fill", "cross.case.fill",
|
||||
"pills.fill", "pill.fill", "syringe.fill", "bandage.fill",
|
||||
"stethoscope", "thermometer.variable", "lungs.fill", "brain.fill",
|
||||
@ -60,7 +60,7 @@ enum SymbolCatalog {
|
||||
"staroflife.fill", "leaf.fill", "carrot.fill", "scalemass.fill",
|
||||
"toilet.fill", "shower.fill", "bathtub.fill", "comb.fill",
|
||||
]),
|
||||
("학습", [
|
||||
(String(localized: "학습"), [
|
||||
"book.fill", "book.closed.fill", "books.vertical.fill",
|
||||
"text.book.closed.fill", "bookmark.fill", "graduationcap.fill",
|
||||
"pencil", "pencil.and.ruler.fill", "highlighter",
|
||||
@ -72,7 +72,7 @@ enum SymbolCatalog {
|
||||
"brain.head.profile", "lightbulb.fill", "puzzlepiece.fill",
|
||||
"backpack.fill", "ruler.fill", "paperclip", "folder.fill",
|
||||
]),
|
||||
("일·업무", [
|
||||
(String(localized: "일·업무"), [
|
||||
"briefcase.fill", "laptopcomputer", "desktopcomputer",
|
||||
"keyboard.fill", "printer.fill", "envelope.fill", "phone.fill",
|
||||
"video.fill", "person.2.fill", "person.3.fill", "calendar",
|
||||
@ -84,7 +84,7 @@ enum SymbolCatalog {
|
||||
"checklist", "list.bullet.clipboard.fill", "tray.full.fill",
|
||||
"archivebox.fill", "presentation.fill" ,"megaphone.fill",
|
||||
]),
|
||||
("생활", [
|
||||
(String(localized: "생활"), [
|
||||
"house.fill", "sofa.fill", "bed.double.fill", "lamp.desk.fill",
|
||||
"lightbulb.fill", "washer.fill", "dryer.fill", "dishwasher.fill",
|
||||
"refrigerator.fill", "oven.fill", "stove.fill", "microwave.fill",
|
||||
@ -97,14 +97,14 @@ enum SymbolCatalog {
|
||||
"pawprint.fill", "dog.fill", "cat.fill", "fish.fill",
|
||||
"bird.fill", "teddybear.fill", "stroller.fill",
|
||||
]),
|
||||
("음식", [
|
||||
(String(localized: "음식"), [
|
||||
"fork.knife", "cup.and.saucer.fill", "mug.fill", "wineglass.fill",
|
||||
"waterbottle.fill", "takeoutbag.and.cup.and.straw.fill",
|
||||
"birthday.cake.fill", "carrot.fill", "fish.fill",
|
||||
"frying.pan.fill", "popcorn.fill", "cooktop.fill",
|
||||
"drop.fill", "flame.fill", "leaf.fill",
|
||||
]),
|
||||
("취미", [
|
||||
(String(localized: "취미"), [
|
||||
"gamecontroller.fill", "arcade.stick.console.fill", "dice.fill",
|
||||
"puzzlepiece.fill", "music.note", "music.mic", "guitars.fill",
|
||||
"pianokeys", "headphones", "hifispeaker.fill", "radio.fill",
|
||||
@ -116,7 +116,7 @@ enum SymbolCatalog {
|
||||
"fishing.rod" ,"tree.fill", "camera.macro", "bird.fill",
|
||||
"balloon.2.fill", "party.popper.fill", "crown.fill",
|
||||
]),
|
||||
("이동", [
|
||||
(String(localized: "이동"), [
|
||||
"car.fill", "bus.fill", "tram.fill", "train.side.front.car",
|
||||
"airplane", "ferry.fill", "sailboat.fill", "bicycle",
|
||||
"scooter", "motorcycle.fill" ,"fuelpump.fill", "ev.charger.fill",
|
||||
@ -124,7 +124,7 @@ enum SymbolCatalog {
|
||||
"mappin.and.ellipse", "signpost.right.fill", "road.lanes",
|
||||
"parkingsign", "steeringwheel", "suitcase.rolling.fill",
|
||||
]),
|
||||
("자연", [
|
||||
(String(localized: "자연"), [
|
||||
"sun.max.fill", "moon.fill", "moon.stars.fill", "sparkles",
|
||||
"cloud.fill", "cloud.rain.fill", "cloud.snow.fill",
|
||||
"cloud.sun.fill", "wind", "snowflake", "drop.fill",
|
||||
@ -132,7 +132,7 @@ enum SymbolCatalog {
|
||||
"camera.macro", "mountain.2.fill", "water.waves",
|
||||
"globe.asia.australia.fill", "star.fill", "rainbow",
|
||||
]),
|
||||
("기호", [
|
||||
(String(localized: "기호"), [
|
||||
"circle.fill", "square.fill", "triangle.fill", "diamond.fill",
|
||||
"seal.fill", "shield.fill", "flag.fill", "flag.checkered",
|
||||
"tag.fill", "bell.fill", "bookmark.fill", "pin.fill",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -121,7 +121,7 @@ struct ActionListView: View {
|
||||
action.isFavorite.toggle()
|
||||
} label: {
|
||||
Label(
|
||||
action.isFavorite ? "즐겨찾기 해제" : "즐겨찾기",
|
||||
action.isFavorite ? String(localized: "즐겨찾기 해제") : String(localized: "즐겨찾기"),
|
||||
systemImage: action.isFavorite ? "star.slash" : "star.fill"
|
||||
)
|
||||
}
|
||||
@ -295,13 +295,13 @@ struct ActionDetailView: View {
|
||||
let now = Date.now
|
||||
switch action.trackingType {
|
||||
case .time:
|
||||
statRow("오늘", Format.durationShort(agg.seconds(for: action, in: math.dayRange(containing: now))))
|
||||
statRow("이번 주", Format.durationShort(agg.seconds(for: action, in: math.weekRange(containing: now))))
|
||||
statRow("이번 달", Format.durationShort(agg.seconds(for: action, in: math.monthRange(containing: now))))
|
||||
statRow(String(localized: "오늘"), Format.durationShort(agg.seconds(for: action, in: math.dayRange(containing: now))))
|
||||
statRow(String(localized: "이번 주"), Format.durationShort(agg.seconds(for: action, in: math.weekRange(containing: now))))
|
||||
statRow(String(localized: "이번 달"), Format.durationShort(agg.seconds(for: action, in: math.monthRange(containing: now))))
|
||||
case .count:
|
||||
statRow("오늘", "\(agg.count(for: action, in: math.dayRange(containing: now)))회")
|
||||
statRow("이번 주", "\(agg.count(for: action, in: math.weekRange(containing: now)))회")
|
||||
statRow("이번 달", "\(agg.count(for: action, in: math.monthRange(containing: now)))회")
|
||||
statRow(String(localized: "오늘"), String(localized: "\(agg.count(for: action, in: math.dayRange(containing: now)))회"))
|
||||
statRow(String(localized: "이번 주"), String(localized: "\(agg.count(for: action, in: math.weekRange(containing: now)))회"))
|
||||
statRow(String(localized: "이번 달"), String(localized: "\(agg.count(for: action, in: math.monthRange(containing: now)))회"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,11 +408,11 @@ struct ActionEditorView: View {
|
||||
}
|
||||
} footer: {
|
||||
Text(trackingType == .time
|
||||
? "켜면 시간 측정을 종료할 때 메모 창이 떠요. 건너뛸 수 있어요."
|
||||
: "켜면 횟수를 추가할 때마다 메모 창이 떠요. 건너뛸 수 있어요.")
|
||||
? String(localized: "켜면 시간 측정을 종료할 때 메모 창이 떠요. 건너뛸 수 있어요.")
|
||||
: String(localized: "켜면 횟수를 추가할 때마다 메모 창이 떠요. 건너뛸 수 있어요."))
|
||||
}
|
||||
}
|
||||
.navigationTitle(action == nil ? "행동 추가" : "행동 수정")
|
||||
.navigationTitle(action == nil ? String(localized: "행동 추가") : String(localized: "행동 수정"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
|
||||
@ -108,7 +108,7 @@ struct GoalListView: View {
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
if activeGoals.count > 1 {
|
||||
Button(isReordering ? "완료" : "순서") {
|
||||
Button(isReordering ? String(localized: "완료") : String(localized: "순서")) {
|
||||
withAnimation {
|
||||
isReordering.toggle()
|
||||
editMode = isReordering ? .active : .inactive
|
||||
@ -260,17 +260,17 @@ struct GoalRow: View {
|
||||
if let end = goal.endDate {
|
||||
return "\(Format.shortDate(goal.startDate)) ~ \(Format.shortDate(end))"
|
||||
}
|
||||
return "\(Format.shortDate(goal.startDate)) 시작 · 종료일 없음"
|
||||
return String(localized: "\(Format.shortDate(goal.startDate)) 시작 · 종료일 없음")
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var statusBadge: some View {
|
||||
let (text, color): (String, Color) = {
|
||||
if goal.needsConfirmation() { return ("확인 필요", AppTheme.yellow) }
|
||||
if goal.needsConfirmation() { return (String(localized: "확인 필요"), AppTheme.yellow) }
|
||||
switch goal.status {
|
||||
case .inProgress: return ("진행 중", AppTheme.green)
|
||||
case .achieved: return ("달성", AppTheme.green)
|
||||
case .notAchieved: return ("미달성", Color.secondary)
|
||||
case .inProgress: return (String(localized: "진행 중"), AppTheme.green)
|
||||
case .achieved: return (String(localized: "달성"), AppTheme.green)
|
||||
case .notAchieved: return (String(localized: "미달성"), Color.secondary)
|
||||
}
|
||||
}()
|
||||
Text(text)
|
||||
@ -453,7 +453,7 @@ struct GoalDetailView: View {
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
if !goal.quests.isEmpty {
|
||||
Button(editMode == .active ? "완료" : "순서") {
|
||||
Button(editMode == .active ? String(localized: "완료") : String(localized: "순서")) {
|
||||
withAnimation {
|
||||
editMode = editMode == .active ? .inactive : .active
|
||||
}
|
||||
@ -566,7 +566,7 @@ struct GoalEditorView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(goal == nil ? "목표 추가" : "목표 수정")
|
||||
.navigationTitle(goal == nil ? String(localized: "목표 추가") : String(localized: "목표 수정"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
|
||||
@ -15,8 +15,8 @@ enum HistoryMode: String, CaseIterable, Identifiable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .list: return "목록"
|
||||
case .timetable: return "타임테이블"
|
||||
case .list: return String(localized: "목록")
|
||||
case .timetable: return String(localized: "타임테이블")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,7 +174,7 @@ struct HistoryView: View {
|
||||
|
||||
private var filterLabel: String {
|
||||
let names = allActions.filter(passesFilter).map(\.name)
|
||||
if names.isEmpty { return "표시할 행동 없음" }
|
||||
if names.isEmpty { return String(localized: "표시할 행동 없음") }
|
||||
return names.count <= 2
|
||||
? names.joined(separator: ", ")
|
||||
: "행동 \(names.count)/\(allActions.count)개 표시 중"
|
||||
|
||||
@ -67,7 +67,7 @@ struct MainView: View {
|
||||
.navigationTitle("모음")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button(isEditing ? "완료" : "배치 편집") {
|
||||
Button(isEditing ? String(localized: "완료") : String(localized: "배치 편집")) {
|
||||
withAnimation {
|
||||
isEditing.toggle()
|
||||
}
|
||||
@ -283,7 +283,7 @@ struct MainView: View {
|
||||
recordsAction = action
|
||||
} label: {
|
||||
Label(
|
||||
action.trackingType == .time ? "시작·종료 시각 수동 입력" : "횟수 직접 입력·수정",
|
||||
action.trackingType == .time ? String(localized: "시작·종료 시각 수동 입력") : String(localized: "횟수 직접 입력·수정"),
|
||||
systemImage: "square.and.pencil"
|
||||
)
|
||||
}
|
||||
@ -801,6 +801,8 @@ struct ActionButtonCell: View {
|
||||
.foregroundStyle(.white.opacity(0.85))
|
||||
}
|
||||
.lineLimit(1)
|
||||
// 영어 등 긴 단위 문구("times")도 셀 안에 들어가도록 축소 허용
|
||||
.minimumScaleFactor(0.6)
|
||||
.animation(.snappy(duration: 0.3), value: todayCount)
|
||||
}
|
||||
}
|
||||
@ -808,7 +810,9 @@ struct ActionButtonCell: View {
|
||||
private var todayLabel: String {
|
||||
let agg = Aggregator()
|
||||
let seconds = agg.seconds(for: action, in: agg.math.dayRange(containing: .now))
|
||||
return seconds > 0 ? "오늘 \(Format.durationShort(seconds))" : "오늘 0분"
|
||||
return seconds > 0
|
||||
? String(localized: "오늘 \(Format.durationShort(seconds))")
|
||||
: String(localized: "오늘 0분")
|
||||
}
|
||||
|
||||
private var todayCount: Int {
|
||||
|
||||
@ -21,7 +21,7 @@ struct QuestEditorView: View {
|
||||
enum TargetKind: String, CaseIterable, Identifiable {
|
||||
case action, tag
|
||||
var id: String { rawValue }
|
||||
var label: String { self == .action ? "행동" : "꼬리표" }
|
||||
var label: String { self == .action ? String(localized: "행동") : String(localized: "꼬리표") }
|
||||
}
|
||||
|
||||
// ① 대상
|
||||
@ -53,7 +53,7 @@ struct QuestEditorView: View {
|
||||
periodSection
|
||||
amountSection
|
||||
}
|
||||
.navigationTitle(quest == nil ? "다짐 추가" : "다짐 수정")
|
||||
.navigationTitle(quest == nil ? String(localized: "다짐 추가") : String(localized: "다짐 수정"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
|
||||
@ -37,7 +37,7 @@ struct ActionRecordsSheet: View {
|
||||
showingAdd = true
|
||||
} label: {
|
||||
Label(
|
||||
action.trackingType == .time ? "기록 직접 추가" : "횟수 직접 추가",
|
||||
action.trackingType == .time ? String(localized: "기록 직접 추가") : String(localized: "횟수 직접 추가"),
|
||||
systemImage: "plus.circle"
|
||||
)
|
||||
.foregroundStyle(AppTheme.green)
|
||||
|
||||
@ -43,7 +43,7 @@ struct SettingsView: View {
|
||||
var body: some View {
|
||||
ScrollViewReader { proxy in
|
||||
Form {
|
||||
Section("화면") {
|
||||
Section {
|
||||
Picker("테마", selection: $theme) {
|
||||
Text("라이트").tag("light")
|
||||
Text("다크").tag("dark")
|
||||
@ -53,6 +53,10 @@ struct SettingsView: View {
|
||||
Text(lang.label).tag(lang.rawValue)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("화면")
|
||||
} footer: {
|
||||
Text("언어를 바꾸면 앱을 완전히 종료했다가 다시 실행했을 때 적용돼요.")
|
||||
}
|
||||
Section {
|
||||
ForEach(AppTab.allCases) { tab in
|
||||
@ -126,7 +130,7 @@ struct SettingsView: View {
|
||||
Label("프리미엄 기능", systemImage: "crown.fill")
|
||||
.foregroundStyle(AppTheme.yellow)
|
||||
Spacer()
|
||||
Text(premium.isPremium ? "사용 중" : "무료 사용 중")
|
||||
Text(premium.isPremium ? String(localized: "사용 중") : String(localized: "무료 사용 중"))
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@ -148,6 +152,10 @@ struct SettingsView: View {
|
||||
// '앱 테마와 일치' 옵션의 위젯이 바뀐 테마를 바로 반영하도록 갱신
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
.onChange(of: language) {
|
||||
// 시스템의 앱별 언어 오버라이드 방식: 다음 실행부터 적용된다
|
||||
UserDefaults.standard.set([language], forKey: "AppleLanguages")
|
||||
}
|
||||
#if DEBUG
|
||||
// 검증용: -settingsScrollGoal YES → 모음 탭 목표 섹션, -settingsScrollPremium YES → 프리미엄 섹션까지 자동 스크롤
|
||||
.onAppear {
|
||||
|
||||
@ -184,9 +184,9 @@ struct StatsTabView: View {
|
||||
|
||||
private var currentPeriodLabel: String {
|
||||
switch span {
|
||||
case .day: return "오늘"
|
||||
case .week: return "이번 주"
|
||||
case .month: return "이번 달"
|
||||
case .day: return String(localized: "오늘")
|
||||
case .week: return String(localized: "이번 주")
|
||||
case .month: return String(localized: "이번 달")
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,7 +310,7 @@ struct StatsTabView: View {
|
||||
var colors: [String: Color] = [:]
|
||||
|
||||
func tagNames(for action: Action) -> [(String, Color)] {
|
||||
if action.tags.isEmpty { return [("꼬리표 없음", Color.gray)] }
|
||||
if action.tags.isEmpty { return [(String(localized: "꼬리표 없음"), Color.gray)] }
|
||||
return action.sortedTags.map { ($0.name, $0.color) }
|
||||
}
|
||||
|
||||
@ -533,7 +533,7 @@ struct StatsTabView: View {
|
||||
private func valueLabel(_ value: Double, type: TrackingType) -> String {
|
||||
switch type {
|
||||
case .time: return Format.durationShort(value)
|
||||
case .count: return "\(Int(value.rounded()))회"
|
||||
case .count: return String(localized: "\(Int(value.rounded()))회")
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,7 +551,7 @@ struct StatsTabView: View {
|
||||
Chart(dailyPoints(for: actions, type: type)) { point in
|
||||
LineMark(
|
||||
x: .value("날짜", point.dayKey, unit: .day),
|
||||
y: .value(type == .time ? "시간" : "횟수", point.value)
|
||||
y: .value(type == .time ? String(localized: "시간") : String(localized: "횟수"), point.value)
|
||||
)
|
||||
.foregroundStyle(by: .value("행동", point.actionName))
|
||||
.symbol(by: .value("행동", point.actionName))
|
||||
@ -575,7 +575,7 @@ struct StatsTabView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.chartYAxisLabel(type == .time ? "시간(h)" : "횟수")
|
||||
.chartYAxisLabel(type == .time ? String(localized: "시간(h)") : String(localized: "횟수"))
|
||||
.frame(height: 200)
|
||||
}
|
||||
}
|
||||
@ -593,14 +593,14 @@ struct StatsTabView: View {
|
||||
Chart(weeklyPoints(for: actions, type: type)) { point in
|
||||
LineMark(
|
||||
x: .value("주차", point.weekLabel),
|
||||
y: .value(type == .time ? "시간" : "횟수", point.value)
|
||||
y: .value(type == .time ? String(localized: "시간") : String(localized: "횟수"), point.value)
|
||||
)
|
||||
.foregroundStyle(by: .value("행동", point.actionName))
|
||||
.symbol(by: .value("행동", point.actionName))
|
||||
.interpolationMethod(.monotone)
|
||||
}
|
||||
.chartForegroundStyleScale(domain: names, range: colors)
|
||||
.chartYAxisLabel(type == .time ? "시간(h)" : "횟수")
|
||||
.chartYAxisLabel(type == .time ? String(localized: "시간(h)") : String(localized: "횟수"))
|
||||
.frame(height: 200)
|
||||
}
|
||||
}
|
||||
@ -617,7 +617,7 @@ struct StatsTabView: View {
|
||||
} else {
|
||||
Chart(items) { item in
|
||||
BarMark(
|
||||
x: .value(type == .time ? "시간" : "횟수", type == .time ? item.value / 3600 : item.value),
|
||||
x: .value(type == .time ? String(localized: "시간") : String(localized: "횟수"), type == .time ? item.value / 3600 : item.value),
|
||||
y: .value("행동", item.name)
|
||||
)
|
||||
.foregroundStyle(item.color)
|
||||
@ -628,7 +628,7 @@ struct StatsTabView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.chartXAxisLabel(type == .time ? "시간(h)" : "횟수")
|
||||
.chartXAxisLabel(type == .time ? String(localized: "시간(h)") : String(localized: "횟수"))
|
||||
.frame(height: CGFloat(items.count) * 40 + 30)
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ struct TagEditorView: View {
|
||||
Text("미리 보기")
|
||||
}
|
||||
}
|
||||
.navigationTitle(tag == nil ? "꼬리표 추가" : "꼬리표 수정")
|
||||
.navigationTitle(tag == nil ? String(localized: "꼬리표 추가") : String(localized: "꼬리표 수정"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
|
||||
@ -22,14 +22,24 @@ enum Format {
|
||||
let h = total / 3600
|
||||
let m = (total % 3600) / 60
|
||||
if h > 0 {
|
||||
return m > 0 ? "\(h)시간 \(m)분" : "\(h)시간"
|
||||
return m > 0
|
||||
? String(localized: "\(h)시간 \(m)분")
|
||||
: String(localized: "\(h)시간")
|
||||
}
|
||||
if m > 0 { return "\(m)분" }
|
||||
return "\(total)초"
|
||||
if m > 0 { return String(localized: "\(m)분") }
|
||||
return String(localized: "\(total)초")
|
||||
}
|
||||
|
||||
static func weekdayShort(_ weekday: Int) -> String {
|
||||
let names = ["일", "월", "화", "수", "목", "금", "토"]
|
||||
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)]
|
||||
}
|
||||
|
||||
@ -106,9 +106,9 @@ enum SpanOption: String, AppEnum {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .day: return "오늘"
|
||||
case .week: return "이번 주"
|
||||
case .month: return "이번 달"
|
||||
case .day: return String(localized: "오늘")
|
||||
case .week: return String(localized: "이번 주")
|
||||
case .month: return String(localized: "이번 달")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,7 +127,9 @@ struct ActionEntity: AppEntity {
|
||||
var displayRepresentation: DisplayRepresentation {
|
||||
DisplayRepresentation(
|
||||
title: "\(name)",
|
||||
subtitle: trackingTypeRaw == TrackingType.count.rawValue ? "횟수 기록" : "시간 측정"
|
||||
subtitle: trackingTypeRaw == TrackingType.count.rawValue
|
||||
? LocalizedStringResource("횟수 기록")
|
||||
: LocalizedStringResource("시간 측정")
|
||||
)
|
||||
}
|
||||
|
||||
@ -477,7 +479,7 @@ struct QuestProgressIntent: AppIntent {
|
||||
case .atLeast:
|
||||
detail = "\(Int(percent))%"
|
||||
case .atMost:
|
||||
detail = result.isAchieved ? "한도 안에서 잘 지키고 있어요" : "한도를 넘었어요"
|
||||
detail = result.isAchieved ? String(localized: "한도 안에서 잘 지키고 있어요") : String(localized: "한도를 넘었어요")
|
||||
}
|
||||
return .result(
|
||||
value: percent,
|
||||
|
||||
@ -19,8 +19,8 @@ enum TrackingType: String, CaseIterable, Identifiable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .time: return "시간 측정"
|
||||
case .count: return "횟수 기록"
|
||||
case .time: return String(localized: "시간 측정")
|
||||
case .count: return String(localized: "횟수 기록")
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,9 +209,9 @@ enum GoalStatus: String, CaseIterable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .inProgress: return "진행 중"
|
||||
case .achieved: return "달성 완료"
|
||||
case .notAchieved: return "미달성 종료"
|
||||
case .inProgress: return String(localized: "진행 중")
|
||||
case .achieved: return String(localized: "달성 완료")
|
||||
case .notAchieved: return String(localized: "미달성 종료")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -302,10 +302,10 @@ enum QuestPeriod: String, CaseIterable, Identifiable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .daily: return "하루 단위"
|
||||
case .weekly: return "일주일 단위"
|
||||
case .monthly: return "한 달 단위"
|
||||
case .custom: return "특정 기간"
|
||||
case .daily: return String(localized: "하루 단위")
|
||||
case .weekly: return String(localized: "일주일 단위")
|
||||
case .monthly: return String(localized: "한 달 단위")
|
||||
case .custom: return String(localized: "특정 기간")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,10 +321,10 @@ enum QuestScheduleMode: String, CaseIterable, Identifiable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .everyDay: return "매일"
|
||||
case .weekdays: return "요일 지정"
|
||||
case .monthDays: return "날짜 지정"
|
||||
case .ordinalWeekday: return "몇째 주 요일"
|
||||
case .everyDay: return String(localized: "매일")
|
||||
case .weekdays: return String(localized: "요일 지정")
|
||||
case .monthDays: return String(localized: "날짜 지정")
|
||||
case .ordinalWeekday: return String(localized: "몇째 주 요일")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -337,8 +337,8 @@ enum QuestDirection: String, CaseIterable, Identifiable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .atLeast: return "이상 달성"
|
||||
case .atMost: return "이하 유지"
|
||||
case .atLeast: return String(localized: "이상 달성")
|
||||
case .atMost: return String(localized: "이하 유지")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -410,7 +410,7 @@ extension Quest {
|
||||
var targetName: String {
|
||||
if let targetAction { return targetAction.name }
|
||||
if let targetTag { return "#\(targetTag.name)" }
|
||||
return "대상 없음"
|
||||
return String(localized: "대상 없음")
|
||||
}
|
||||
|
||||
var targetSymbol: String {
|
||||
@ -437,7 +437,7 @@ extension Quest {
|
||||
}
|
||||
|
||||
var targetValueLabel: String {
|
||||
measure == .time ? Format.durationShort(targetSeconds) : "\(targetCount)회"
|
||||
measure == .time ? Format.durationShort(targetSeconds) : String(localized: "\(targetCount)회")
|
||||
}
|
||||
|
||||
/// 주기 요약 문구 (예: "매주 월·수·금")
|
||||
@ -446,22 +446,22 @@ extension Quest {
|
||||
case .daily:
|
||||
switch scheduleMode {
|
||||
case .everyDay:
|
||||
return "매일"
|
||||
return String(localized: "매일")
|
||||
case .weekdays:
|
||||
let names = weekdays.sorted().map { Format.weekdayShort($0) }
|
||||
return "매주 " + names.joined(separator: "·")
|
||||
return String(localized: "매주 \(names.joined(separator: "·"))")
|
||||
case .monthDays:
|
||||
let names = monthDays.sorted().map { "\($0)일" }
|
||||
return "매달 " + names.joined(separator: ", ")
|
||||
let names = monthDays.sorted().map { String(localized: "\($0)일") }
|
||||
return String(localized: "매달 \(names.joined(separator: ", "))")
|
||||
case .ordinalWeekday:
|
||||
return "매달 \(ordinalWeek)째 주 \(Format.weekdayShort(ordinalWeekday))요일"
|
||||
return String(localized: "매달 \(ordinalWeek)째 주 \(Format.weekdayShort(ordinalWeekday))요일")
|
||||
}
|
||||
case .weekly:
|
||||
return "매주"
|
||||
return String(localized: "매주")
|
||||
case .monthly:
|
||||
return "매달"
|
||||
return String(localized: "매달")
|
||||
case .custom:
|
||||
guard let customStart, let customEnd else { return "특정 기간" }
|
||||
guard let customStart, let customEnd else { return String(localized: "특정 기간") }
|
||||
return "\(Format.shortDate(customStart)) ~ \(Format.shortDate(customEnd))"
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,9 +15,9 @@ enum StatSpan: String, CaseIterable, Identifiable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .day: return "하루"
|
||||
case .week: return "주간"
|
||||
case .month: return "월간"
|
||||
case .day: return String(localized: "하루")
|
||||
case .week: return String(localized: "주간")
|
||||
case .month: return String(localized: "월간")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,8 +40,8 @@ enum GoalCardStyle: String, CaseIterable, Identifiable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .perQuest: return "다짐별로 각각"
|
||||
case .combined: return "전체 다짐 합산"
|
||||
case .perQuest: return String(localized: "다짐별로 각각")
|
||||
case .combined: return String(localized: "전체 다짐 합산")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,12 +56,12 @@ enum TabBarConfig {
|
||||
/// 짧은 기록 무시 옵션 선택지 (초)
|
||||
enum MinSessionOption {
|
||||
static let choices: [(seconds: Int, label: String)] = [
|
||||
(0, "사용 안 함"),
|
||||
(3, "3초"),
|
||||
(5, "5초"),
|
||||
(10, "10초"),
|
||||
(30, "30초"),
|
||||
(60, "1분"),
|
||||
(0, String(localized: "사용 안 함")),
|
||||
(3, String(localized: "3초")),
|
||||
(5, String(localized: "5초")),
|
||||
(10, String(localized: "10초")),
|
||||
(30, String(localized: "30초")),
|
||||
(60, String(localized: "1분")),
|
||||
]
|
||||
}
|
||||
|
||||
@ -73,8 +73,8 @@ enum LiveActivityMode: String, CaseIterable, Identifiable {
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .latest: return "가장 나중에 시작한 행동"
|
||||
case .earliest: return "가장 먼저 시작한 행동"
|
||||
case .latest: return String(localized: "가장 나중에 시작한 행동")
|
||||
case .earliest: return String(localized: "가장 먼저 시작한 행동")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2863
myApp/HaruDanim/Widgets/Localizable.xcstrings
Normal file
2863
myApp/HaruDanim/Widgets/Localizable.xcstrings
Normal file
File diff suppressed because it is too large
Load Diff
@ -126,7 +126,9 @@ struct QuestRingActionCellView: View {
|
||||
|
||||
private var percentText: String {
|
||||
if cell.isAtMost {
|
||||
return cell.isAchieved ? "\(spanLabel) 한도 지킴" : "\(spanLabel) 한도 초과"
|
||||
return cell.isAchieved
|
||||
? String(localized: "\(spanLabel) 한도 지킴")
|
||||
: String(localized: "\(spanLabel) 한도 초과")
|
||||
}
|
||||
return "\(spanLabel) \(Format.percent(cell.displayRatio))"
|
||||
}
|
||||
|
||||
@ -27,9 +27,9 @@ enum StatsChartSpan: String, AppEnum {
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .week: return "일주일 통계"
|
||||
case .monthByDay: return "한 달 통계 (일별)"
|
||||
case .monthByWeek: return "한 달 통계 (주별)"
|
||||
case .week: return String(localized: "일주일 통계")
|
||||
case .monthByDay: return String(localized: "한 달 통계 (일별)")
|
||||
case .monthByWeek: return String(localized: "한 달 통계 (주별)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -203,14 +203,14 @@ struct StatsChartWidgetView: View {
|
||||
if let day = point.day {
|
||||
LineMark(
|
||||
x: .value("날짜", day, unit: .day),
|
||||
y: .value(entry.isTimeType ? "시간" : "값", point.value)
|
||||
y: .value(entry.isTimeType ? String(localized: "시간") : String(localized: "값"), point.value)
|
||||
)
|
||||
.foregroundStyle(by: .value("행동", point.actionName))
|
||||
.interpolationMethod(.monotone)
|
||||
} else if let weekLabel = point.weekLabel {
|
||||
LineMark(
|
||||
x: .value("주차", weekLabel),
|
||||
y: .value(entry.isTimeType ? "시간" : "값", point.value)
|
||||
y: .value(entry.isTimeType ? String(localized: "시간") : String(localized: "값"), point.value)
|
||||
)
|
||||
.foregroundStyle(by: .value("행동", point.actionName))
|
||||
.interpolationMethod(.monotone)
|
||||
@ -218,7 +218,7 @@ struct StatsChartWidgetView: View {
|
||||
}
|
||||
.chartForegroundStyleScale(domain: entry.names, range: colors)
|
||||
.chartLegend(family == .systemSmall ? .hidden : .visible)
|
||||
.chartYAxisLabel(family == .systemSmall ? "" : (entry.isTimeType ? "시간(h)" : "횟수"))
|
||||
.chartYAxisLabel(family == .systemSmall ? "" : (entry.isTimeType ? String(localized: "시간(h)") : String(localized: "횟수")))
|
||||
.chartXAxis {
|
||||
if entry.isWeekAxis {
|
||||
AxisMarks { _ in
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user