Enhance LanguageManager to support system language detection and user selection. Introduce a new 'system' language option that automatically follows the device's language settings. Update SettingsView to implement a collapsible language selection interface with visual feedback. Add localized strings for the new system language option in English, Simplified Chinese, and Thai. Improve UI responsiveness and maintainability in language management.

main
v504 2 months ago
parent 667d4afb98
commit 7e3e592a42

@ -4,12 +4,15 @@ import Combine
//
enum Language: String, CaseIterable {
case system = "system"
case english = "en"
case chinese = "zh-Hans"
case thai = "th"
var displayName: String {
switch self {
case .system:
return "system_language".localized
case .english:
return "English"
case .chinese:
@ -21,6 +24,8 @@ enum Language: String, CaseIterable {
var flag: String {
switch self {
case .system:
return "🌐"
case .english:
return "🇺🇸"
case .chinese:
@ -39,40 +44,118 @@ class LanguageManager: ObservableObject {
@Published var refreshTrigger = UUID() // UI
private let languageKey = "selected_language"
private let systemLanguageKey = "system_language"
private init() {
loadLanguage()
}
//
private func detectSystemLanguage() -> Language {
// iOS 15+
let systemLanguage = Locale.current.languageCode ?? "en"
//
if systemLanguage.hasPrefix("zh") {
return .chinese
}
//
if systemLanguage == "th" {
return .thai
}
// 使
return .english
}
//
private func loadLanguage() {
if let savedLanguage = UserDefaults.standard.string(forKey: languageKey),
let language = Language(rawValue: savedLanguage) {
currentLanguage = language
//
let hasManualLanguage = UserDefaults.standard.object(forKey: languageKey) != nil
if hasManualLanguage {
// 使
if let savedLanguage = UserDefaults.standard.string(forKey: languageKey),
let language = Language(rawValue: savedLanguage) {
if language == .system {
//
let systemLanguage = detectSystemLanguage()
currentLanguage = systemLanguage
} else {
currentLanguage = language
}
} else {
currentLanguage = .english
}
} else {
// 使
currentLanguage = .english
// 使
currentLanguage = .system
let systemLanguage = detectSystemLanguage()
//
UserDefaults.standard.set("system", forKey: languageKey)
UserDefaults.standard.set(true, forKey: systemLanguageKey)
//
switchLanguage(to: .system)
}
}
//
func switchLanguage(to language: Language) {
currentLanguage = language
UserDefaults.standard.set(language.rawValue, forKey: languageKey)
if language == .system {
//
resetToSystemLanguage()
} else {
//
UserDefaults.standard.set(language.rawValue, forKey: languageKey)
UserDefaults.standard.set(false, forKey: systemLanguageKey)
// UI
refreshTrigger = UUID()
//
NotificationCenter.default.post(name: .languageChanged, object: language)
}
}
//
func resetToSystemLanguage() {
let systemLanguage = detectSystemLanguage()
currentLanguage = systemLanguage
UserDefaults.standard.set(systemLanguage.rawValue, forKey: languageKey)
UserDefaults.standard.set(true, forKey: systemLanguageKey)
// UI
refreshTrigger = UUID()
//
NotificationCenter.default.post(name: .languageChanged, object: language)
NotificationCenter.default.post(name: .languageChanged, object: systemLanguage)
}
// 使
var isUsingSystemLanguage: Bool {
return UserDefaults.standard.bool(forKey: systemLanguageKey)
}
//
func localizedString(for key: String) -> String {
let bundle = Bundle.main
let language = currentLanguage.rawValue
if let path = bundle.path(forResource: language, ofType: "lproj"),
// 使
let languageToUse: String
if currentLanguage == .system {
//
let systemLanguage = detectSystemLanguage()
languageToUse = systemLanguage.rawValue
} else {
languageToUse = currentLanguage.rawValue
}
if let path = bundle.path(forResource: languageToUse, ofType: "lproj"),
let languageBundle = Bundle(path: path) {
return languageBundle.localizedString(forKey: key, value: key, table: nil)
}

@ -2,6 +2,7 @@ import SwiftUI
struct SettingsView: View {
@EnvironmentObject private var languageManager: LanguageManager
@State private var showLanguageOptions = false
var body: some View {
ZStack {
@ -67,14 +68,84 @@ struct SettingsView: View {
Spacer()
}
Picker("language".localized, selection: $languageManager.currentLanguage) {
ForEach(Language.allCases, id: \.self) { language in
Text(language.displayName).tag(language)
VStack(spacing: 0) {
//
Button(action: {
withAnimation(.easeInOut(duration: 0.2)) {
showLanguageOptions.toggle()
}
}) {
HStack {
Text(languageManager.currentLanguage.flag)
.font(.system(size: 16))
Text(languageManager.currentLanguage.displayName)
.font(.system(size: 16))
.foregroundColor(.primary)
Spacer()
Image(systemName: showLanguageOptions ? "chevron.up" : "chevron.down")
.font(.system(size: 12, weight: .medium))
.foregroundColor(.secondary)
}
.padding(.vertical, 12)
.padding(.horizontal, 16)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color(.systemGray6))
)
}
.buttonStyle(PlainButtonStyle())
//
if showLanguageOptions {
VStack(spacing: 0) {
ForEach(Language.allCases, id: \.self) { language in
Button(action: {
languageManager.switchLanguage(to: language)
withAnimation(.easeInOut(duration: 0.2)) {
showLanguageOptions = false
}
}) {
HStack {
Text(language.flag)
.font(.system(size: 16))
Text(language.displayName)
.font(.system(size: 16))
.foregroundColor(.primary)
Spacer()
if languageManager.currentLanguage == language {
Image(systemName: "checkmark")
.font(.system(size: 14, weight: .medium))
.foregroundColor(.blue)
}
}
.padding(.vertical, 12)
.padding(.horizontal, 16)
.background(
RoundedRectangle(cornerRadius: 0)
.fill(languageManager.currentLanguage == language ? Color.blue.opacity(0.1) : Color(.systemBackground))
)
}
.buttonStyle(PlainButtonStyle())
if language != Language.allCases.last {
Divider()
.padding(.leading, 44)
}
}
}
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color(.systemBackground))
.shadow(color: .black.opacity(0.1), radius: 4, x: 0, y: 2)
)
.transition(.opacity.combined(with: .scale(scale: 0.95, anchor: .top)))
}
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: languageManager.currentLanguage) { newLanguage in
languageManager.switchLanguage(to: newLanguage)
}
}
.padding(20)

@ -661,6 +661,7 @@
"birthday_format" = "%@-%@-%@";
// Language Manager
"chinese_language" = "Chinese";
"system_language" = "System";
// Input Component Factory
"input_any_text_content" = "Input any text content...";
"input_phone_number" = "Input phone number...";

@ -669,6 +669,7 @@
"birthday_format" = "%@-%@-%@";
// Language Manager
"chinese_language" = "จีน";
"system_language" = "ตามระบบ";
// Input Component Factory
"input_any_text_content" = "ป้อนเนื้อหาข้อความใดๆ...";
"input_phone_number" = "ป้อนหมายเลขโทรศัพท์...";

@ -668,6 +668,7 @@
"birthday_format" = "%@年%@月%@日";
// Language Manager
"chinese_language" = "中文";
"system_language" = "跟随系统";
// Input Component Factory
"input_any_text_content" = "输入任意文本内容...";
"input_phone_number" = "输入电话号码...";

@ -41,7 +41,7 @@ private func requestCameraPermission() {
#### 核心功能:
- **HTML格式显示**使用WKWebView加载本地HTML文件
- **简约风格设计**:现代化的界面设计
- **简约风格设计**:现代化的界面布局和样式
- **完整内容**:包含所有必要的隐私政策章节
#### 技术实现:
@ -57,10 +57,11 @@ struct WebView: UIViewRepresentable {
}
```
#### HTML文件特点
#### 界面特点:
- **渐变头部**:蓝紫色渐变背景的标题区域
- **卡片式布局**:清爽的白色容器,圆角设计
- **响应式设计**:适配不同屏幕尺寸
- **现代化样式**使用CSS3和现代设计元素
- **清晰结构**8个主要章节内容完整
### 3. 设置界面集成 (`SettingsView.swift`)
@ -98,42 +99,138 @@ NavigationLink(destination: PrivacyPolicyView().environmentObject(languageManage
}
```
#### 导航优化:
- **移除双重导航栏**解决了ContentView和SettingsView都使用NavigationView导致的导航栏重复问题
- **直接导航**使用NavigationLink替代sheet模态展示提供更流畅的导航体验
- **简化代码**移除了dismiss相关的复杂逻辑
### 4. 隐私政策HTML文件 (`privacy_policy.html`)
#### 设计特点:
- **简约风格**:使用渐变头部和现代化的布局
- **完整内容**包含8个主要章节
- **响应式设计**:适配不同设备屏幕
- **专业外观**:符合现代应用设计标准
#### 内容结构:
1. 信息收集
2. 信息使用
3. 信息共享
4. 数据安全
5. 用户权利
6. 儿童隐私
7. 政策变更
8. 联系我们
## 🌐 本地化支持
### 支持语言:
- **英文** (en.lproj)
- **简体中文** (zh-Hans.lproj)
- **泰文** (th.lproj)
### 新增本地化键:
### 4. 语言本地化系统 (`LanguageManager.swift`)
#### 核心功能:
- **系统语言跟随**:默认跟随系统语言设置
- **智能语言检测**:自动检测系统语言并选择对应支持的语言
- **手动语言选择**:用户可手动选择特定语言
- **语言回退机制**:如果没有对应的语言文件,自动使用英语
#### 技术实现:
```swift
// 检测系统语言并返回对应的支持语言
private func detectSystemLanguage() -> Language {
let systemLanguage = Locale.current.languageCode ?? "en"
// 检查是否是简体中文
if systemLanguage.hasPrefix("zh") {
return .chinese
}
// 检查是否是泰文
if systemLanguage == "th" {
return .thai
}
// 其他语言默认使用英文
return .english
}
// 加载保存的语言设置
private func loadLanguage() {
let hasManualLanguage = UserDefaults.standard.object(forKey: languageKey) != nil
if hasManualLanguage {
// 如果用户手动设置过语言,使用保存的设置
if let savedLanguage = UserDefaults.standard.string(forKey: languageKey),
let language = Language(rawValue: savedLanguage) {
if language == .system {
// 如果保存的是系统语言,检测当前系统语言
let systemLanguage = detectSystemLanguage()
currentLanguage = systemLanguage
} else {
currentLanguage = language
}
}
} else {
// 首次启动,默认使用系统语言
currentLanguage = .system
let systemLanguage = detectSystemLanguage()
// 保存系统语言设置
UserDefaults.standard.set("system", forKey: languageKey)
UserDefaults.standard.set(true, forKey: systemLanguageKey)
// 设置实际语言
switchLanguage(to: .system)
}
}
```
#### 支持的语言选项:
- **🌐 跟随系统**:自动跟随系统语言设置
- **🇺🇸 English**:英语
- **🇨🇳 中文**:简体中文
- **🇹🇭 ไทย**:泰文
#### 语言选择界面:
- **折叠式选择器**:默认显示当前选中的语言,点击后展开所有选项
- **视觉反馈**:选中状态有蓝色背景和勾选图标
- **国旗图标**:每个语言选项都有对应的国旗图标
- **动画效果**:展开/收起有平滑的动画过渡
- **空间优化**:避免语言选项过多时占用过多空间
## 📄 隐私政策内容
### HTML文件结构
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Privacy Policy - MyQrCode</title>
<style>
/* 简约风格CSS样式 */
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Privacy Policy</h1>
<div class="last-updated">
<strong>Last Updated:</strong> December 28, 2024
</div>
</div>
<!-- 8个主要章节 -->
<h2>1. Information We Collect</h2>
<h2>2. How We Use Your Information</h2>
<h2>3. Information Sharing</h2>
<h2>4. Data Security</h2>
<h2>5. Your Rights</h2>
<h2>6. Children's Privacy</h2>
<h2>7. Changes to This Policy</h2>
<h2>8. Contact Us</h2>
</div>
</body>
</html>
```
### 隐私政策特点:
- **英文版本**:统一使用英文,确保法律效力
- **完整内容**:包含所有必要的隐私政策章节
- **简约设计**:现代化的界面布局和样式
- **响应式布局**:适配不同屏幕尺寸
## 🌍 本地化支持
### 支持的语言:
1. **英语 (en)**:默认语言,作为回退语言
2. **简体中文 (zh-Hans)**:完整的中文本地化
3. **泰文 (th)**:完整的泰文本地化
### 本地化文件:
- `MyQrCode/en.lproj/Localizable.strings`:英文本地化
- `MyQrCode/zh-Hans.lproj/Localizable.strings`:简体中文本地化
- `MyQrCode/th.lproj/Localizable.strings`:泰文本地化
### 新增本地化字符串:
```strings
"app_permissions" = "App Permissions";
"manage_app_permissions" = "Manage camera and photo library permissions for this app.";
"privacy_policy" = "Privacy Policy";
"view_privacy_policy" = "View our privacy policy and data handling practices.";
"system_language" = "System"; // 英文
"system_language" = "跟随系统"; // 中文
"system_language" = "ตามระบบ"; // 泰文
```
## 📱 用户体验
@ -152,25 +249,31 @@ NavigationLink(destination: PrivacyPolicyView().environmentObject(languageManage
4. 查看简约风格的HTML格式隐私政策
5. 了解应用的数据处理方式
### 语言设置:
1. 用户进入设置界面
2. 在语言设置区域点击当前语言显示
3. 展开语言选项列表
4. 可选择"跟随系统"自动跟随系统语言
5. 或手动选择特定语言(英语、中文、泰文)
6. 选择后自动收起选项列表并立即应用新的语言设置
## 🔧 技术实现细节
### 权限管理:
- 使用`AVFoundation`框架检测相机权限
- 使用`Photos`框架检测相册权限
- 实时更新权限状态显示
- 提供权限请求和系统设置跳转功能
### 系统语言检测
- 使用`Locale.current.languageCode`获取系统语言代码
- 支持中文前缀检测zh-Hans, zh-CN等
- 支持泰文检测th
- 其他语言默认使用英语
### 隐私政策:
- 使用`WKWebView`加载本地HTML文件
- HTML文件包含完整的隐私政策内容
- 响应式设计适配不同屏幕尺寸
- 现代化的CSS样式设计
### 语言状态管理:
- 使用UserDefaults保存语言设置
- 区分手动设置和系统跟随状态
- 支持动态语言切换和UI刷新
### 导航优化:
- 移除SettingsView中的NavigationView包装
- 使用NavigationLink替代sheet模态展示
- 简化导航逻辑,提供更流畅的用户体验
- 删除功能信息卡片,简化设置界面
- 移除双重NavigationView问题
- 使用NavigationLink提供直接导航
- 简化子视图的导航标题设置
## 📝 总结
@ -179,14 +282,16 @@ NavigationLink(destination: PrivacyPolicyView().environmentObject(languageManage
1. **完整的权限管理界面**,支持实时状态显示和权限操作
2. **简约风格的隐私政策界面**使用HTML格式提供现代化的英文版隐私政策
3. **无缝的设置界面集成**使用NavigationLink提供直接的导航体验
4. **全面的本地化支持**,支持英文、中文和泰文三种语言
5. **现代化的界面设计**,提供优秀的用户体验
4. **智能的语言本地化系统**,支持系统语言跟随和手动语言选择
5. **全面的本地化支持**,支持英文、中文和泰文三种语言
6. **现代化的界面设计**,提供优秀的用户体验
### 主要改进:
- **隐私政策HTML**:改为简约风格设计,使用渐变头部和现代化的布局
- **导航方式**从sheet模态展示改为NavigationLink直接导航提供更流畅的用户体验
- **语言系统**:添加系统语言跟随功能,智能检测系统语言并自动选择对应支持的语言
- **语言选择器**:改为折叠式设计,默认收起,点击展开,避免占用过多空间
- **界面优化**移除了不必要的dismiss相关代码简化了导航逻辑
- **导航栏修复**:解决了双重导航栏问题,提供更清晰的导航体验
- **功能简化**:删除了功能信息卡片,使设置界面更加简洁
- **用户体验**:解决双重导航栏问题,提供更清晰的导航层次
所有功能都已通过编译验证,可以立即投入使用。这些功能将帮助应用更好地管理用户权限,提供透明的隐私政策,并符合现代应用的法律和用户体验要求。
所有功能都已通过编译验证,可以立即投入使用。这些功能将帮助应用更好地管理用户权限,提供透明的隐私政策,智能地适应不同语言环境,并符合现代应用的法律和用户体验要求。

Loading…
Cancel
Save