feat: SettingsView 完善国际化支持

- 所有文本改用 String(localized:)
- 添加应用内语言切换 Picker
- 支持简体中文、繁体中文、英文

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
empty
2026-01-03 22:23:43 +08:00
parent bcf0dd71a7
commit 6e60bea509

View File

@@ -10,7 +10,7 @@ import Photos
struct SettingsView: View {
@State private var photoLibraryStatus: PHAuthorizationStatus = .notDetermined
@State private var cacheSize: String = "计算中..."
@State private var cacheSize: String = String(localized: "common.calculating")
@State private var showingClearCacheAlert = false
@State private var showingClearRecentWorksAlert = false
@State private var feedbackPackageURL: URL?
@@ -21,7 +21,7 @@ struct SettingsView: View {
//
Section {
HStack {
Label("相册权限", systemImage: "photo.on.rectangle")
Label(String(localized: "settings.photoPermission"), systemImage: "photo.on.rectangle")
Spacer()
permissionStatusView
}
@@ -30,19 +30,37 @@ struct SettingsView: View {
Button {
openSettings()
} label: {
Label("前往设置授权", systemImage: "gear")
Label(String(localized: "settings.goToSettings"), systemImage: "gear")
}
}
} header: {
Text("权限")
Text(String(localized: "settings.permission"))
} footer: {
Text("需要相册权限才能保存 Live Photo")
Text(String(localized: "settings.permissionFooter"))
}
//
Section {
Picker(selection: Binding(
get: { LanguageManager.shared.current },
set: { LanguageManager.shared.current = $0 }
)) {
ForEach(LanguageManager.Language.allCases) { language in
Text(language.displayName).tag(language)
}
} label: {
Label(String(localized: "settings.appLanguage"), systemImage: "globe")
}
} header: {
Text(String(localized: "settings.language"))
} footer: {
Text(String(localized: "settings.languageChangeHint"))
}
//
Section {
HStack {
Label("缓存大小", systemImage: "internaldrive")
Label(String(localized: "settings.cacheSize"), systemImage: "internaldrive")
Spacer()
Text(cacheSize)
.foregroundStyle(.secondary)
@@ -51,18 +69,18 @@ struct SettingsView: View {
Button(role: .destructive) {
showingClearCacheAlert = true
} label: {
Label("清理缓存", systemImage: "trash")
Label(String(localized: "settings.clearCache"), systemImage: "trash")
}
Button(role: .destructive) {
showingClearRecentWorksAlert = true
} label: {
Label("清空最近作品记录", systemImage: "clock.arrow.circlepath")
Label(String(localized: "settings.clearRecentWorks"), systemImage: "clock.arrow.circlepath")
}
} header: {
Text("存储")
Text(String(localized: "settings.storage"))
} footer: {
Text("清理缓存不会影响已保存到相册的 Live Photo")
Text(String(localized: "settings.storageFooter"))
}
//
@@ -70,27 +88,27 @@ struct SettingsView: View {
Button {
exportFeedbackPackage()
} label: {
Label("导出诊断报告", systemImage: "doc.text")
Label(String(localized: "settings.exportDiagnostics"), systemImage: "doc.text")
}
Link(destination: URL(string: "mailto:support@let5see.xyz")!) {
Label("反馈问题", systemImage: "envelope")
Label(String(localized: "settings.contactUs"), systemImage: "envelope")
}
// TODO: App Store App ID
Link(destination: URL(string: "https://apps.apple.com/app/id000000000")!) {
Label("App Store 评分", systemImage: "star")
Label(String(localized: "settings.rateApp"), systemImage: "star")
}
} header: {
Text("反馈")
Text(String(localized: "settings.feedback"))
} footer: {
Text("诊断报告仅包含日志和参数,不含媒体内容")
Text(String(localized: "settings.feedbackFooter"))
}
//
Section {
HStack {
Label("版本", systemImage: "info.circle")
Label(String(localized: "settings.version"), systemImage: "info.circle")
Spacer()
Text(appVersion)
.foregroundStyle(.secondary)
@@ -99,39 +117,39 @@ struct SettingsView: View {
NavigationLink {
PrivacyPolicyView()
} label: {
Label("隐私政策", systemImage: "hand.raised")
Label(String(localized: "settings.privacyPolicy"), systemImage: "hand.raised")
}
NavigationLink {
TermsOfServiceView()
} label: {
Label("使用条款", systemImage: "doc.text")
Label(String(localized: "settings.termsOfService"), systemImage: "doc.text")
}
} header: {
Text("关于")
Text(String(localized: "settings.about"))
}
}
.navigationTitle("设置")
.navigationTitle(String(localized: "settings.title"))
.navigationBarTitleDisplayMode(.inline)
.onAppear {
checkPermissionStatus()
calculateCacheSize()
}
.alert("清理缓存", isPresented: $showingClearCacheAlert) {
Button("取消", role: .cancel) {}
Button("清理", role: .destructive) {
.alert(String(localized: "settings.clearCache"), isPresented: $showingClearCacheAlert) {
Button(String(localized: "common.cancel"), role: .cancel) {}
Button(String(localized: "settings.clear"), role: .destructive) {
clearCache()
}
} message: {
Text("确定要清理所有缓存文件吗?")
Text(String(localized: "settings.clearCacheConfirm"))
}
.alert("清空记录", isPresented: $showingClearRecentWorksAlert) {
Button("取消", role: .cancel) {}
Button("清空", role: .destructive) {
.alert(String(localized: "settings.clearRecordsTitle"), isPresented: $showingClearRecentWorksAlert) {
Button(String(localized: "common.cancel"), role: .cancel) {}
Button(String(localized: "settings.clear"), role: .destructive) {
clearRecentWorks()
}
} message: {
Text("确定要清空最近作品记录吗?这不会删除相册中的 Live Photo。")
Text(String(localized: "settings.clearRecordsConfirm"))
}
.sheet(isPresented: $showingShareSheet) {
if let url = feedbackPackageURL {
@@ -144,19 +162,19 @@ struct SettingsView: View {
private var permissionStatusView: some View {
switch photoLibraryStatus {
case .authorized:
Label("已授权", systemImage: "checkmark.circle.fill")
Label(String(localized: "settings.authorized"), systemImage: "checkmark.circle.fill")
.foregroundStyle(.green)
.labelStyle(.iconOnly)
case .limited:
Label("部分授权", systemImage: "exclamationmark.circle.fill")
Label(String(localized: "settings.limited"), systemImage: "exclamationmark.circle.fill")
.foregroundStyle(.orange)
.labelStyle(.iconOnly)
case .denied, .restricted:
Label("未授权", systemImage: "xmark.circle.fill")
Label(String(localized: "settings.denied"), systemImage: "xmark.circle.fill")
.foregroundStyle(.red)
.labelStyle(.iconOnly)
case .notDetermined:
Label("未确定", systemImage: "questionmark.circle.fill")
Label(String(localized: "settings.notDetermined"), systemImage: "questionmark.circle.fill")
.foregroundStyle(.secondary)
.labelStyle(.iconOnly)
@unknown default: