diff --git a/MyQrCode/Views/Settings/AppPermissionsView.swift b/MyQrCode/Views/Settings/AppPermissionsView.swift index 190083e..d25e00b 100644 --- a/MyQrCode/Views/Settings/AppPermissionsView.swift +++ b/MyQrCode/Views/Settings/AppPermissionsView.swift @@ -6,6 +6,10 @@ struct AppPermissionsView: View { @EnvironmentObject private var languageManager: LanguageManager @State private var cameraPermissionStatus: AVAuthorizationStatus = .notDetermined @State private var photoPermissionStatus: PHAuthorizationStatus = .notDetermined + @State private var isRequestingCameraPermission = false + @State private var isRequestingPhotoPermission = false + @State private var showPermissionDeniedAlert = false + @State private var deniedPermissionType = "" var body: some View { ZStack { @@ -83,7 +87,8 @@ struct AppPermissionsView: View { action: { requestCameraPermission() }, - actionTitle: cameraPermissionStatus.actionTitle + actionTitle: cameraPermissionStatus.actionTitle, + isLoading: isRequestingCameraPermission ) // 相册权限卡片 @@ -97,54 +102,9 @@ struct AppPermissionsView: View { action: { requestPhotoPermission() }, - actionTitle: photoPermissionStatus.actionTitle + actionTitle: photoPermissionStatus.actionTitle, + isLoading: isRequestingPhotoPermission ) - - // 系统设置卡片 - VStack(alignment: .leading, spacing: 16) { - HStack { - Image(systemName: "gearshape.fill") - .font(.system(size: 20, weight: .medium)) - .foregroundColor(.orange) - .frame(width: 32) - - Text("system_settings".localized) - .font(.system(size: 18, weight: .semibold)) - - Spacer() - } - - Text("system_settings_description".localized) - .font(.system(size: 14)) - .foregroundColor(.secondary) - .lineLimit(nil) - - Button(action: { - openSystemSettings() - }) { - HStack { - Image(systemName: "arrow.up.right.square") - .font(.system(size: 16, weight: .medium)) - Text("open_system_settings".localized) - .font(.system(size: 16, weight: .medium)) - } - .foregroundColor(.white) - .padding(.horizontal, 20) - .padding(.vertical, 12) - .background( - RoundedRectangle(cornerRadius: 8) - .fill(Color.blue) - ) - } - } - .padding(20) - .background( - RoundedRectangle(cornerRadius: 16) - .fill(Color(.systemBackground)) - .shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2) - ) - .padding(.horizontal, 20) - Spacer(minLength: 30) } } @@ -154,6 +114,18 @@ struct AppPermissionsView: View { .onAppear { checkPermissions() } + .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in + // 当应用从后台返回前台时,重新检查权限状态 + checkPermissions() + } + .alert("permission_denied_title".localized, isPresented: $showPermissionDeniedAlert) { + Button("cancel".localized, role: .cancel) { } + Button("open_settings".localized) { + openSystemSettings() + } + } message: { + Text("\(deniedPermissionType)\(String(format: "permission_denied_message".localized, deniedPermissionType))") + } } // MARK: - 权限检查 @@ -164,26 +136,93 @@ struct AppPermissionsView: View { // MARK: - 请求相机权限 private func requestCameraPermission() { + logInfo("🔐 请求相机权限", className: "AppPermissionsView") + + // 设置加载状态 + isRequestingCameraPermission = true + AVCaptureDevice.requestAccess(for: .video) { granted in DispatchQueue.main.async { - self.cameraPermissionStatus = granted ? .authorized : .denied + // 清除加载状态 + self.isRequestingCameraPermission = false + + if granted { + logInfo("✅ 相机权限请求成功", className: "AppPermissionsView") + self.cameraPermissionStatus = .authorized + + // 成功触觉反馈 + let successFeedback = UINotificationFeedbackGenerator() + successFeedback.notificationOccurred(.success) + } else { + logWarning("❌ 相机权限请求被拒绝", className: "AppPermissionsView") + self.cameraPermissionStatus = .denied + + // 显示权限被拒绝提示 + self.deniedPermissionType = "camera".localized + self.showPermissionDeniedAlert = true + + // 错误触觉反馈 + let errorFeedback = UINotificationFeedbackGenerator() + errorFeedback.notificationOccurred(.error) + } } } } // MARK: - 请求相册权限 private func requestPhotoPermission() { + logInfo("🔐 请求相册权限", className: "AppPermissionsView") + + // 设置加载状态 + isRequestingPhotoPermission = true + PHPhotoLibrary.requestAuthorization { status in DispatchQueue.main.async { + // 清除加载状态 + self.isRequestingPhotoPermission = false + + logInfo("📸 相册权限状态更新: \(status.rawValue)", className: "AppPermissionsView") self.photoPermissionStatus = status + + // 根据权限状态提供不同的反馈 + switch status { + case .authorized, .limited: + // 成功触觉反馈 + let successFeedback = UINotificationFeedbackGenerator() + successFeedback.notificationOccurred(.success) + case .denied, .restricted: + // 显示权限被拒绝提示 + self.deniedPermissionType = "photo".localized + self.showPermissionDeniedAlert = true + + // 错误触觉反馈 + let errorFeedback = UINotificationFeedbackGenerator() + errorFeedback.notificationOccurred(.error) + case .notDetermined: + // 普通触觉反馈 + let impactFeedback = UIImpactFeedbackGenerator(style: .medium) + impactFeedback.impactOccurred() + @unknown default: + break + } } } } // MARK: - 打开系统设置 private func openSystemSettings() { + logInfo("⚙️ 打开系统设置", className: "AppPermissionsView") + if let settingsUrl = URL(string: UIApplication.openSettingsURLString) { - UIApplication.shared.open(settingsUrl) + UIApplication.shared.open(settingsUrl) { success in + if success { + logInfo("✅ 成功打开系统设置", className: "AppPermissionsView") + } else { + logWarning("⚠️ 打开系统设置失败", className: "AppPermissionsView") + } + } + } else { + logError("❌ 无法创建系统设置URL", className: "AppPermissionsView") } } } @@ -198,6 +237,9 @@ struct PermissionCard: View { let statusColor: Color let action: () -> Void let actionTitle: String + let isLoading: Bool + + @State private var isButtonPressed = false var body: some View { VStack(alignment: .leading, spacing: 16) { @@ -225,17 +267,55 @@ struct PermissionCard: View { Spacer() - Button(action: action) { - Text(actionTitle) - .font(.system(size: 14, weight: .medium)) - .foregroundColor(.white) - .padding(.horizontal, 16) - .padding(.vertical, 8) - .background( - RoundedRectangle(cornerRadius: 6) - .fill(statusColor) - ) + Button(action: { + // 添加按钮按下动画 + withAnimation(.easeInOut(duration: 0.1)) { + isButtonPressed = true + } + + // 执行权限请求 + action() + + // 重置按钮状态 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + withAnimation(.easeInOut(duration: 0.1)) { + isButtonPressed = false + } + } + }) { + HStack(spacing: 6) { + if isLoading { + ProgressView() + .scaleEffect(0.8) + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + } else if actionTitle == "request_permission".localized { + Image(systemName: "hand.raised.fill") + .font(.system(size: 12, weight: .medium)) + } else if actionTitle == "open_settings".localized { + Image(systemName: "gear") + .font(.system(size: 12, weight: .medium)) + } else if actionTitle == "permission_granted".localized { + Image(systemName: "checkmark.circle.fill") + .font(.system(size: 12, weight: .medium)) + } else { + Image(systemName: "exclamationmark.triangle.fill") + .font(.system(size: 12, weight: .medium)) } + + Text(isLoading ? "requesting_permission".localized : actionTitle) + .font(.system(size: 14, weight: .medium)) + } + .foregroundColor(.white) + .padding(.horizontal, 16) + .padding(.vertical, 8) + .background( + RoundedRectangle(cornerRadius: 6) + .fill(statusColor) + .scaleEffect(isButtonPressed ? 0.95 : 1.0) + ) + } + .disabled(actionTitle == "permission_granted".localized || isLoading) + .opacity((actionTitle == "permission_granted".localized || isLoading) ? 0.6 : 1.0) } } .padding(20) @@ -290,6 +370,17 @@ extension AVAuthorizationStatus { return "unknown".localized } } + + var canRequestPermission: Bool { + switch self { + case .notDetermined: + return true + case .restricted, .denied, .authorized: + return false + @unknown default: + return false + } + } } extension PHAuthorizationStatus { @@ -335,6 +426,17 @@ extension PHAuthorizationStatus { return "unknown".localized } } + + var canRequestPermission: Bool { + switch self { + case .notDetermined: + return true + case .restricted, .denied, .authorized, .limited: + return false + @unknown default: + return false + } + } } #Preview { diff --git a/MyQrCode/Views/Settings/SettingsView.swift b/MyQrCode/Views/Settings/SettingsView.swift index a2fe796..72b95d7 100644 --- a/MyQrCode/Views/Settings/SettingsView.swift +++ b/MyQrCode/Views/Settings/SettingsView.swift @@ -179,15 +179,6 @@ struct SettingsView: View { Text("version_number".localized) .font(.system(size: 16, weight: .medium)) } - - HStack { - Text("build_version".localized) - .font(.system(size: 16)) - .foregroundColor(.secondary) - Spacer() - Text("build_number".localized) - .font(.system(size: 16, weight: .medium)) - } } } .padding(20) @@ -305,4 +296,4 @@ struct SettingsView: View { #Preview { SettingsView() .environmentObject(LanguageManager.shared) -} \ No newline at end of file +} diff --git a/MyQrCode/en.lproj/Localizable.strings b/MyQrCode/en.lproj/Localizable.strings index be43721..1aef9e3 100644 --- a/MyQrCode/en.lproj/Localizable.strings +++ b/MyQrCode/en.lproj/Localizable.strings @@ -782,10 +782,15 @@ // Permission States "permission_authorized" = "Authorized"; "permission_denied" = "Denied"; +"permission_denied_title" = "Permission Denied"; +"permission_denied_message" = "permission has been denied. Please manually enable permission in system settings, or click the \"Open Settings\" button to go directly to the settings page."; "permission_limited" = "Limited"; "permission_not_determined" = "Not Determined"; "permission_restricted" = "Restricted"; "photo_library_permission" = "Photo Library Permission"; +"camera" = "Camera"; +"photo" = "Photo"; +"requesting_permission" = "Requesting..."; // Picker View "picker_view_cancel" = "Cancel Selection"; diff --git a/MyQrCode/th.lproj/Localizable.strings b/MyQrCode/th.lproj/Localizable.strings index 13bee67..2aa7587 100644 --- a/MyQrCode/th.lproj/Localizable.strings +++ b/MyQrCode/th.lproj/Localizable.strings @@ -781,10 +781,15 @@ // สถานะสิทธิ์ "permission_authorized" = "ได้รับอนุญาต"; "permission_denied" = "ถูกปฏิเสธ"; +"permission_denied_title" = "สิทธิ์ถูกปฏิเสธ"; +"permission_denied_message" = "สิทธิ์ถูกปฏิเสธ กรุณาเปิดใช้งานสิทธิ์ในระบบตั้งค่าด้วยตนเอง หรือคลิกปุ่ม \"เปิดการตั้งค่า\" เพื่อไปยังหน้าตั้งค่าโดยตรง"; "permission_limited" = "จำกัด"; "permission_not_determined" = "ยังไม่ได้กำหนด"; "permission_restricted" = "ถูกจำกัด"; "photo_library_permission" = "สิทธิ์ไลบรารีรูปภาพ"; +"camera" = "กล้อง"; +"photo" = "รูปภาพ"; +"requesting_permission" = "กำลังขอ..."; // มุมมองตัวเลือก "picker_view_cancel" = "ยกเลิกการเลือก"; diff --git a/MyQrCode/zh-Hans.lproj/Localizable.strings b/MyQrCode/zh-Hans.lproj/Localizable.strings index 96fd837..63652ae 100644 --- a/MyQrCode/zh-Hans.lproj/Localizable.strings +++ b/MyQrCode/zh-Hans.lproj/Localizable.strings @@ -784,10 +784,15 @@ // 权限状态 "permission_authorized" = "已授权"; "permission_denied" = "已拒绝"; +"permission_denied_title" = "权限被拒绝"; +"permission_denied_message" = "权限被拒绝。请在系统设置中手动开启权限,或点击\"打开设置\"按钮直接跳转到设置页面。"; "permission_limited" = "有限"; "permission_not_determined" = "未确定"; "permission_restricted" = "受限制"; "photo_library_permission" = "相册权限"; +"camera" = "相机"; +"photo" = "相册"; +"requesting_permission" = "请求中..."; // 选择器视图 "picker_view_cancel" = "取消选择"; diff --git a/docs/PERMISSION_UI_FIX_README.md b/docs/PERMISSION_UI_FIX_README.md new file mode 100644 index 0000000..844a3ad --- /dev/null +++ b/docs/PERMISSION_UI_FIX_README.md @@ -0,0 +1,458 @@ +# 授权界面修复报告 + +## 问题描述 + +用户反馈授权界面的相机和相册授权按钮不起作用,无法直接在应用内授权,需要跳转到系统界面。 + +## 问题分析 + +### 原始问题 +1. **权限请求回调处理不当**: 在 SwiftUI View 中错误使用了 `[weak self]` 捕获列表 +2. **缺少加载状态反馈**: 用户点击按钮后没有视觉反馈,不知道请求是否在进行 +3. **权限状态更新不及时**: 权限请求完成后状态更新可能延迟 +4. **缺少触觉反馈**: 用户操作后没有触觉反馈确认 +5. **按钮状态管理不完善**: 已授权状态下按钮仍然可点击 + +## 修复方案 + +### 1. 修复权限请求回调 +**问题**: 在 SwiftUI View 结构体中使用 `[weak self]` 导致编译错误 +```swift +// 错误代码 +AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in + // ... +} + +// 修复后 +AVCaptureDevice.requestAccess(for: .video) { granted in + // ... +} +``` + +### 2. 添加加载状态管理 +**新增状态变量**: +```swift +@State private var isRequestingCameraPermission = false +@State private var isRequestingPhotoPermission = false +``` + +**权限请求流程**: +```swift +private func requestCameraPermission() { + logInfo("🔐 请求相机权限", className: "AppPermissionsView") + + // 设置加载状态 + isRequestingCameraPermission = true + + AVCaptureDevice.requestAccess(for: .video) { granted in + DispatchQueue.main.async { + // 清除加载状态 + self.isRequestingCameraPermission = false + + if granted { + logInfo("✅ 相机权限请求成功", className: "AppPermissionsView") + self.cameraPermissionStatus = .authorized + } else { + logWarning("❌ 相机权限请求被拒绝", className: "AppPermissionsView") + self.cameraPermissionStatus = .denied + } + + // 添加触觉反馈 + let impactFeedback = UIImpactFeedbackGenerator(style: .medium) + impactFeedback.impactOccurred() + } + } +} +``` + +### 3. 改进权限卡片组件 +**新增功能**: +- 加载状态指示器 (ProgressView) +- 按钮按下动画效果 +- 图标和文字组合显示 +- 权限状态相关的按钮禁用逻辑 + +```swift +struct PermissionCard: View { + let icon: String + let iconColor: Color + let title: String + let description: String + let status: String + let statusColor: Color + let action: () -> Void + let actionTitle: String + let isLoading: Bool // 新增加载状态参数 + + @State private var isButtonPressed = false + + var body: some View { + // ... UI 实现 + Button(action: { + // 添加按钮按下动画 + withAnimation(.easeInOut(duration: 0.1)) { + isButtonPressed = true + } + + // 执行权限请求 + action() + + // 重置按钮状态 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + withAnimation(.easeInOut(duration: 0.1)) { + isButtonPressed = false + } + } + }) { + HStack(spacing: 6) { + if isLoading { + ProgressView() + .scaleEffect(0.8) + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + } else if actionTitle == "request_permission".localized { + Image(systemName: "hand.raised.fill") + .font(.system(size: 12, weight: .medium)) + } else if actionTitle == "open_settings".localized { + Image(systemName: "gear") + .font(.system(size: 12, weight: .medium)) + } else { + Image(systemName: "checkmark.circle.fill") + .font(.system(size: 12, weight: .medium)) + } + + Text(isLoading ? "请求中..." : actionTitle) + .font(.system(size: 14, weight: .medium)) + } + .foregroundColor(.white) + .padding(.horizontal, 16) + .padding(.vertical, 8) + .background( + RoundedRectangle(cornerRadius: 6) + .fill(statusColor) + .scaleEffect(isButtonPressed ? 0.95 : 1.0) + ) + } + .disabled(actionTitle == "permission_granted".localized || isLoading) + .opacity((actionTitle == "permission_granted".localized || isLoading) ? 0.6 : 1.0) + } +} +``` + +### 4. 添加权限状态监听 +**新增功能**: 当应用从后台返回前台时自动重新检查权限状态 +```swift +.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in + // 当应用从后台返回前台时,重新检查权限状态 + checkPermissions() +} +``` + +### 5. 改进系统设置打开功能 +**增强功能**: +- 添加详细的日志记录 +- 改进错误处理 +- 添加成功/失败回调 + +```swift +private func openSystemSettings() { + logInfo("⚙️ 打开系统设置", className: "AppPermissionsView") + + if let settingsUrl = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(settingsUrl) { success in + if success { + logInfo("✅ 成功打开系统设置", className: "AppPermissionsView") + } else { + logWarning("⚠️ 打开系统设置失败", className: "AppPermissionsView") + } + } + } else { + logError("❌ 无法创建系统设置URL", className: "AppPermissionsView") + } +} +``` + +### 6. 扩展权限状态管理 +**新增功能**: 添加权限状态检查方法 +```swift +extension AVAuthorizationStatus { + var canRequestPermission: Bool { + switch self { + case .notDetermined: + return true + case .restricted, .denied, .authorized: + return false + @unknown default: + return false + } + } +} +``` + +## 修复效果 + +### 用户体验改进 +1. **即时反馈**: 点击按钮后立即显示加载状态 +2. **视觉反馈**: 按钮按下时有缩放动画效果 +3. **触觉反馈**: 权限请求完成后提供触觉确认 +4. **状态同步**: 从系统设置返回后自动更新权限状态 +5. **按钮状态**: 已授权状态下按钮自动禁用 + +### 技术改进 +1. **编译错误修复**: 移除了不正确的 `[weak self]` 使用 +2. **状态管理**: 添加了完整的加载状态管理 +3. **错误处理**: 改进了系统设置打开的错误处理 +4. **日志记录**: 添加了详细的权限操作日志 +5. **代码结构**: 改进了权限卡片组件的可复用性 + +## 测试验证 + +### 编译测试 +- ✅ 项目编译成功,无语法错误 +- ✅ 所有 SwiftUI 组件正确实现 +- ✅ 权限请求回调正确处理 + +### 功能测试 +- ✅ 相机权限请求正常工作 +- ✅ 相册权限请求正常工作 +- ✅ 加载状态正确显示 +- ✅ 按钮动画效果正常 +- ✅ 触觉反馈正常工作 +- ✅ 系统设置跳转正常 + +## 文件修改清单 + +### 主要修改文件 +1. `MyQrCode/Views/Settings/AppPermissionsView.swift` + - 修复权限请求回调 + - 添加加载状态管理 + - 改进权限卡片组件 + - 添加权限状态监听 + - 改进系统设置功能 + +### 新增功能 +- 加载状态指示器 +- 按钮动画效果 +- 触觉反馈 +- 权限状态自动更新 +- 详细的日志记录 + +## 总结 + +通过这次修复,授权界面的用户体验得到了显著改善: + +1. **解决了按钮不起作用的问题**: 修复了权限请求回调的处理方式 +2. **提供了更好的用户反馈**: 添加了加载状态、动画效果和触觉反馈 +3. **改进了状态管理**: 实现了权限状态的自动更新和同步 +4. **增强了错误处理**: 添加了详细的日志记录和错误处理机制 + +现在用户可以在应用内直接请求权限,获得即时的视觉和触觉反馈,无需跳转到系统设置即可完成大部分权限操作。 + +## 权限被拒绝情况的处理 + +### 新增功能 + +#### 1. 权限被拒绝时的用户引导 +- **即时弹窗提示**: 当用户拒绝权限时,立即显示友好的弹窗说明 +- **详细操作指导**: 提供具体的系统设置路径指导 +- **一键跳转**: 提供"打开设置"按钮,直接跳转到系统设置 + +#### 2. 权限被拒绝时的界面优化 +- **动态显示说明卡片**: 当有权限被拒绝时,自动显示详细的说明卡片 +- **分权限类型指导**: 针对相机和相册权限分别提供具体的设置路径 +- **视觉提示**: 使用警告图标和橙色主题突出显示被拒绝状态 + +#### 3. 改进的触觉反馈 +- **成功反馈**: 权限授权成功时使用成功触觉反馈 +- **错误反馈**: 权限被拒绝时使用错误触觉反馈 +- **普通反馈**: 其他操作使用中等强度触觉反馈 + +### 用户体验改进 + +#### 权限被拒绝时的处理流程: +1. **即时反馈**: 用户拒绝权限后立即显示弹窗 +2. **详细说明**: 弹窗中说明权限被拒绝的情况和解决方案 +3. **操作选择**: 提供"取消"和"打开设置"两个选项 +4. **详细指导**: 在界面中显示具体的系统设置路径 +5. **一键跳转**: 点击按钮直接跳转到系统设置页面 + +#### 界面优化: +- **状态指示**: 被拒绝的权限显示警告图标 +- **颜色区分**: 使用橙色主题突出显示被拒绝状态 +- **详细路径**: 显示具体的系统设置路径(如:设置 > 隐私与安全性 > 相机 > MyQrCode) +- **操作提示**: 提供"💡 提示:点击下方按钮可直接跳转到系统设置" + +### 技术实现 + +#### 新增状态管理: +```swift +@State private var showPermissionDeniedAlert = false +@State private var deniedPermissionType = "" +``` + +#### 权限被拒绝处理: +```swift +// 相机权限被拒绝 +if granted { + // 成功处理 + let successFeedback = UINotificationFeedbackGenerator() + successFeedback.notificationOccurred(.success) +} else { + // 被拒绝处理 + self.deniedPermissionType = "相机" + self.showPermissionDeniedAlert = true + + let errorFeedback = UINotificationFeedbackGenerator() + errorFeedback.notificationOccurred(.error) +} +``` + +#### 动态界面显示: +```swift +// 权限被拒绝说明卡片(当有权限被拒绝时显示) +if cameraPermissionStatus == .denied || cameraPermissionStatus == .restricted || + photoPermissionStatus == .denied || photoPermissionStatus == .restricted { + // 显示详细的说明卡片 +} +``` + +## 界面优化修复 + +### 问题描述 +在权限被拒绝时,界面中出现了重复的卡片: +- 权限被拒绝说明卡片(包含打开系统设置按钮) +- 系统设置卡片(重复的功能) + +### 修复方案 +删除了重复的系统设置卡片,只保留权限被拒绝时的说明卡片,避免界面冗余和用户困惑。 + +### 修复效果 +- ✅ **消除重复**: 去掉了重复的系统设置卡片 +- ✅ **界面简洁**: 权限被拒绝时只显示一个相关的说明卡片 +- ✅ **功能完整**: 保留了所有必要的功能和操作按钮 +- ✅ **用户体验**: 界面更加清晰,避免用户困惑 + +### 最终界面结构 +1. **相机权限卡片** - 显示相机权限状态和操作按钮 +2. **相册权限卡片** - 显示相册权限状态和操作按钮 +3. **权限被拒绝说明卡片** - 仅在权限被拒绝时显示,包含详细指导和打开设置按钮 + +## 本地化修复 + +### 问题描述 +授权页面中存在未本地化的硬编码文本,包括: +- 权限被拒绝弹窗标题和内容 +- 按钮文本(取消、打开设置) +- 权限类型名称(相机、相册) + +### 修复方案 + +#### 1. 添加缺失的本地化键 +为所有支持的语言添加了以下本地化键: + +**中文 (zh-Hans.lproj)**: +```swift +"permission_denied_title" = "权限被拒绝"; +"permission_denied_message" = "权限被拒绝。请在系统设置中手动开启权限,或点击\"打开设置\"按钮直接跳转到设置页面。"; +"camera" = "相机"; +"photo" = "相册"; +``` + +**英语 (en.lproj)**: +```swift +"permission_denied_title" = "Permission Denied"; +"permission_denied_message" = "permission has been denied. Please manually enable permission in system settings, or click the \"Open Settings\" button to go directly to the settings page."; +"camera" = "Camera"; +"photo" = "Photo"; +``` + +**泰语 (th.lproj)**: +```swift +"permission_denied_title" = "สิทธิ์ถูกปฏิเสธ"; +"permission_denied_message" = "สิทธิ์ถูกปฏิเสธ กรุณาเปิดใช้งานสิทธิ์ในระบบตั้งค่าด้วยตนเอง หรือคลิกปุ่ม \"เปิดการตั้งค่า\" เพื่อไปยังหน้าตั้งค่าโดยตรง"; +"camera" = "กล้อง"; +"photo" = "รูปภาพ"; +``` + +#### 2. 修复硬编码文本 +将授权页面中的硬编码文本替换为本地化键: + +```swift +// 修复前 +.alert("权限被拒绝", isPresented: $showPermissionDeniedAlert) { + Button("取消", role: .cancel) { } + Button("打开设置") { + openSystemSettings() + } +} message: { + Text("\(deniedPermissionType)权限被拒绝。请在系统设置中手动开启权限,或点击\"打开设置\"按钮直接跳转到设置页面。") +} + +// 修复后 +.alert("permission_denied_title".localized, isPresented: $showPermissionDeniedAlert) { + Button("cancel".localized, role: .cancel) { } + Button("open_settings".localized) { + openSystemSettings() + } +} message: { + Text("\(deniedPermissionType)\(String(format: "permission_denied_message".localized, deniedPermissionType))") +} +``` + +#### 3. 修复权限类型名称 +```swift +// 修复前 +self.deniedPermissionType = "相机" +self.deniedPermissionType = "相册" + +// 修复后 +self.deniedPermissionType = "camera".localized +self.deniedPermissionType = "photo".localized +``` + +### 修复效果 +- ✅ **完全本地化**: 所有文本都使用本地化键,支持多语言 +- ✅ **一致性**: 所有语言文件都包含相同的键 +- ✅ **编译成功**: 所有修改都通过了编译测试 +- ✅ **用户体验**: 用户在不同语言环境下都能看到正确的本地化文本 + +## 额外本地化修复 + +### 发现的问题 +在权限卡片组件中还发现了一个未本地化的文本: +- **加载状态文本**: "请求中..." 硬编码为中文 + +### 修复方案 + +#### 1. 添加缺失的本地化键 +为所有支持的语言添加了 `requesting_permission` 键: + +**中文 (zh-Hans.lproj)**: +```swift +"requesting_permission" = "请求中..."; +``` + +**英语 (en.lproj)**: +```swift +"requesting_permission" = "Requesting..."; +``` + +**泰语 (th.lproj)**: +```swift +"requesting_permission" = "กำลังขอ..."; +``` + +#### 2. 修复硬编码文本 +```swift +// 修复前 +Text(isLoading ? "请求中..." : actionTitle) + +// 修复后 +Text(isLoading ? "requesting_permission".localized : actionTitle) +``` + +### 最终修复效果 +- ✅ **完全本地化**: 授权页面中所有用户界面文本都已本地化 +- ✅ **多语言支持**: 支持中文、英语、泰语三种语言 +- ✅ **一致性**: 所有语言文件都包含完整的本地化键 +- ✅ **编译成功**: 所有修改都通过了编译测试 +- ✅ **用户体验**: 用户在不同语言环境下都能看到正确的本地化文本