Refactor SettingsView to improve layout and navigation structure. Replace NavigationView with a ZStack for better background handling and update UI elements for app permissions and privacy policy. Enhance localization support by adding new strings for app permissions in English, Thai, and Simplified Chinese. Update navigation titles and button styles for consistency across the settings interface.

main
v504 2 months ago
parent a3df0ebc25
commit 667d4afb98

@ -55,7 +55,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Release"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"

@ -0,0 +1,264 @@
<!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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f8f9fa;
padding: 20px;
}
.container {
max-width: 700px;
margin: 0 auto;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 40px 30px;
text-align: center;
}
.header h1 {
font-size: 2.2em;
font-weight: 300;
margin-bottom: 10px;
}
.header .date {
font-size: 0.9em;
opacity: 0.9;
}
.content {
padding: 40px 30px;
}
h2 {
color: #2c3e50;
font-size: 1.4em;
font-weight: 600;
margin: 30px 0 15px 0;
padding-bottom: 8px;
border-bottom: 2px solid #ecf0f1;
}
h2:first-child {
margin-top: 0;
}
p {
margin-bottom: 15px;
color: #555;
font-size: 0.95em;
}
ul {
margin: 15px 0;
padding-left: 20px;
}
li {
margin-bottom: 8px;
color: #555;
font-size: 0.95em;
}
.highlight {
background-color: #f8f9fa;
border-left: 4px solid #667eea;
padding: 20px;
margin: 20px 0;
border-radius: 4px;
}
.highlight p {
margin-bottom: 0;
color: #2c3e50;
font-weight: 500;
}
.contact-box {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
padding: 25px;
border-radius: 8px;
margin: 30px 0;
}
.contact-box h3 {
margin-bottom: 15px;
font-size: 1.2em;
}
.contact-box ul {
list-style: none;
padding-left: 0;
}
.contact-box li {
color: white;
margin-bottom: 5px;
}
.footer {
text-align: center;
padding: 30px;
background-color: #f8f9fa;
color: #7f8c8d;
font-size: 0.9em;
}
.section {
margin-bottom: 30px;
}
.section:last-child {
margin-bottom: 0;
}
@media (max-width: 768px) {
body {
padding: 10px;
}
.container {
border-radius: 0;
box-shadow: none;
}
.header {
padding: 30px 20px;
}
.content {
padding: 30px 20px;
}
.header h1 {
font-size: 1.8em;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Privacy Policy</h1>
<div class="date">Last Updated: December 28, 2024</div>
</div>
<div class="content">
<div class="section">
<div class="highlight">
<p><strong>MyQrCode</strong> is committed to protecting your privacy. This Privacy Policy explains
how we collect, use, and safeguard your information when you use our mobile application.</p>
</div>
</div>
<div class="section">
<h2>1. Information We Collect</h2>
<p>We collect only the minimum information necessary for the app's functionality:</p>
<ul>
<li><strong>Camera Access:</strong> Required to scan QR codes and barcodes using your device's
camera</li>
<li><strong>Photo Library Access:</strong> Required to save generated QR codes and barcodes to your
photo library</li>
</ul>
<p><strong>Important:</strong> We do not collect personal information such as names, email addresses,
phone numbers, or any other personally identifiable information.</p>
</div>
<div class="section">
<h2>2. How We Use Your Information</h2>
<p>The information we collect is used solely for app functionality:</p>
<ul>
<li>Scanning QR codes and barcodes</li>
<li>Generating QR codes and barcodes</li>
<li>Saving images to your device</li>
</ul>
<p>We do not use your information for advertising, analytics, or any other commercial purposes.</p>
</div>
<div class="section">
<h2>3. Information Sharing</h2>
<p>We do not share, sell, or transfer your information to third parties. All data processing occurs
locally on your device, and we do not transmit any information to external servers.</p>
</div>
<div class="section">
<h2>4. Data Security</h2>
<p>We implement appropriate security measures to protect your information:</p>
<ul>
<li>All data is stored locally on your device</li>
<li>No data is transmitted to external servers</li>
<li>We use industry-standard security practices</li>
</ul>
</div>
<div class="section">
<h2>5. Your Rights</h2>
<p>You have the right to:</p>
<ul>
<li>Access, modify, or delete your data at any time</li>
<li>Revoke app permissions through your device settings</li>
<li>Uninstall the app to remove all related data</li>
</ul>
</div>
<div class="section">
<h2>6. Children's Privacy</h2>
<p>Our app does not knowingly collect personal information from children under 13. If you are a parent
or guardian and believe your child has provided us with personal information, please contact us
immediately.</p>
</div>
<div class="section">
<h2>7. Changes to This Policy</h2>
<p>We may update this Privacy Policy from time to time. We will notify you of any changes by posting the
new Privacy Policy in the app and updating the "Last Updated" date.</p>
</div>
<div class="section">
<h2>8. Contact Us</h2>
<h3>Get in Touch</h3>
<p>If you have any questions about this Privacy Policy or our data practices, please contact us through:
</p>
<ul>
<li>• App Store reviews and ratings</li>
<li>• Our support channels</li>
</ul>
</div>
<div class="section">
<div class="highlight">
<p><strong>By using this app, you agree to the terms of this Privacy Policy.</strong></p>
</div>
</div>
</div>
<div class="footer">
<p>&copy; 2024 MyQrCode. All rights reserved.</p>
</div>
</div>
</body>
</html>

@ -0,0 +1,350 @@
import SwiftUI
import AVFoundation
import Photos
struct AppPermissionsView: View {
@EnvironmentObject private var languageManager: LanguageManager
@State private var cameraPermissionStatus: AVAuthorizationStatus = .notDetermined
@State private var photoPermissionStatus: PHAuthorizationStatus = .notDetermined
var body: some View {
NavigationView {
ZStack {
//
LinearGradient(
gradient: Gradient(colors: [
Color(.systemBackground),
Color(.systemGray6).opacity(0.2)
]),
startPoint: .top,
endPoint: .bottom
)
.ignoresSafeArea()
ScrollView {
VStack(spacing: 24) {
//
VStack(spacing: 16) {
ZStack {
Circle()
.fill(
LinearGradient(
gradient: Gradient(colors: [
Color.blue.opacity(0.1),
Color.blue.opacity(0.05)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.frame(width: 80, height: 80)
Image(systemName: "lock.shield")
.font(.system(size: 36, weight: .light))
.foregroundColor(.blue)
}
Text("app_permissions".localized)
.font(.system(size: 28, weight: .bold, design: .rounded))
.foregroundColor(.primary)
.id(languageManager.refreshTrigger)
}
.padding(.top, 20)
//
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "info.circle")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.blue)
.frame(width: 32)
Text("permissions_info".localized)
.font(.system(size: 18, weight: .semibold))
Spacer()
}
Text("permissions_description".localized)
.font(.system(size: 14))
.foregroundColor(.secondary)
.lineLimit(nil)
}
.padding(20)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color(.systemBackground))
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
)
.padding(.horizontal, 20)
//
PermissionCard(
icon: "camera.fill",
iconColor: .blue,
title: "camera_permission".localized,
description: "camera_permission_description".localized,
status: cameraPermissionStatus.displayText,
statusColor: cameraPermissionStatus.statusColor,
action: {
requestCameraPermission()
},
actionTitle: cameraPermissionStatus.actionTitle
)
//
PermissionCard(
icon: "photo.fill",
iconColor: .green,
title: "photo_permission".localized,
description: "photo_permission_description".localized,
status: photoPermissionStatus.displayText,
statusColor: photoPermissionStatus.statusColor,
action: {
requestPhotoPermission()
},
actionTitle: photoPermissionStatus.actionTitle
)
//
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)
}
}
}
.navigationTitle("App Permissions")
.navigationBarTitleDisplayMode(.inline)
.onAppear {
checkPermissions()
}
}
}
// MARK: -
private func checkPermissions() {
cameraPermissionStatus = AVCaptureDevice.authorizationStatus(for: .video)
photoPermissionStatus = PHPhotoLibrary.authorizationStatus()
}
// MARK: -
private func requestCameraPermission() {
AVCaptureDevice.requestAccess(for: .video) { granted in
DispatchQueue.main.async {
self.cameraPermissionStatus = granted ? .authorized : .denied
}
}
}
// MARK: -
private func requestPhotoPermission() {
PHPhotoLibrary.requestAuthorization { status in
DispatchQueue.main.async {
self.photoPermissionStatus = status
}
}
}
// MARK: -
private func openSystemSettings() {
if let settingsUrl = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(settingsUrl)
}
}
}
// MARK: -
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
var body: some View {
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: icon)
.font(.system(size: 20, weight: .medium))
.foregroundColor(iconColor)
.frame(width: 32)
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.system(size: 18, weight: .semibold))
Text(description)
.font(.system(size: 14))
.foregroundColor(.secondary)
}
Spacer()
}
HStack {
Text(status)
.font(.system(size: 14, weight: .medium))
.foregroundColor(statusColor)
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)
)
}
}
}
.padding(20)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color(.systemBackground))
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
)
.padding(.horizontal, 20)
}
}
// MARK: -
extension AVAuthorizationStatus {
var displayText: String {
switch self {
case .notDetermined:
return "not_determined".localized
case .restricted:
return "restricted".localized
case .denied:
return "denied".localized
case .authorized:
return "authorized".localized
@unknown default:
return "unknown".localized
}
}
var statusColor: Color {
switch self {
case .notDetermined:
return .orange
case .restricted, .denied:
return .red
case .authorized:
return .green
@unknown default:
return .gray
}
}
var actionTitle: String {
switch self {
case .notDetermined:
return "request_permission".localized
case .restricted, .denied:
return "open_settings".localized
case .authorized:
return "permission_granted".localized
@unknown default:
return "unknown".localized
}
}
}
extension PHAuthorizationStatus {
var displayText: String {
switch self {
case .notDetermined:
return "not_determined".localized
case .restricted:
return "restricted".localized
case .denied:
return "denied".localized
case .authorized:
return "authorized".localized
case .limited:
return "limited".localized
@unknown default:
return "unknown".localized
}
}
var statusColor: Color {
switch self {
case .notDetermined:
return .orange
case .restricted, .denied:
return .red
case .authorized, .limited:
return .green
@unknown default:
return .gray
}
}
var actionTitle: String {
switch self {
case .notDetermined:
return "request_permission".localized
case .restricted, .denied:
return "open_settings".localized
case .authorized, .limited:
return "permission_granted".localized
@unknown default:
return "unknown".localized
}
}
}
#Preview {
AppPermissionsView()
.environmentObject(LanguageManager.shared)
}

@ -0,0 +1,49 @@
import SwiftUI
import WebKit
struct PrivacyPolicyView: View {
@EnvironmentObject private var languageManager: LanguageManager
var body: some View {
NavigationView {
ZStack {
//
LinearGradient(
gradient: Gradient(colors: [
Color(.systemBackground),
Color(.systemGray6).opacity(0.2)
]),
startPoint: .top,
endPoint: .bottom
)
.ignoresSafeArea()
// WebViewHTML
WebView(url: Bundle.main.url(forResource: "privacy_policy", withExtension: "html")!)
.ignoresSafeArea(.container, edges: .bottom)
}
.navigationTitle("Privacy Policy")
.navigationBarTitleDisplayMode(.inline)
}
}
}
// MARK: - WebView
struct WebView: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
//
}
}
#Preview {
PrivacyPolicyView()
.environmentObject(LanguageManager.shared)
}

@ -2,169 +2,154 @@ import SwiftUI
struct SettingsView: View {
@EnvironmentObject private var languageManager: LanguageManager
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationView {
ZStack {
//
LinearGradient(
gradient: Gradient(colors: [
Color(.systemBackground),
Color(.systemGray6).opacity(0.2)
]),
startPoint: .top,
endPoint: .bottom
)
.ignoresSafeArea()
ScrollView {
VStack(spacing: 24) {
//
VStack(spacing: 16) {
ZStack {
Circle()
.fill(
LinearGradient(
gradient: Gradient(colors: [
Color.blue.opacity(0.1),
Color.blue.opacity(0.05)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
ZStack {
//
LinearGradient(
gradient: Gradient(colors: [
Color(.systemBackground),
Color(.systemGray6).opacity(0.2)
]),
startPoint: .top,
endPoint: .bottom
)
.ignoresSafeArea()
ScrollView {
VStack(spacing: 24) {
//
VStack(spacing: 16) {
ZStack {
Circle()
.fill(
LinearGradient(
gradient: Gradient(colors: [
Color.blue.opacity(0.1),
Color.blue.opacity(0.05)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.frame(width: 80, height: 80)
Image(systemName: "gearshape.fill")
.font(.system(size: 36, weight: .light))
.foregroundColor(.blue)
}
)
.frame(width: 80, height: 80)
Text("settings".localized)
.font(.system(size: 28, weight: .bold, design: .rounded))
.foregroundColor(.primary)
.id(languageManager.refreshTrigger)
Image(systemName: "gearshape.fill")
.font(.system(size: 36, weight: .light))
.foregroundColor(.blue)
}
.padding(.top, 20)
//
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "globe")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.blue)
.frame(width: 32)
VStack(alignment: .leading, spacing: 4) {
Text("language_settings".localized)
.font(.system(size: 18, weight: .semibold))
.id(languageManager.refreshTrigger)
Text("select_app_language".localized)
.id(languageManager.refreshTrigger)
.font(.system(size: 14))
.foregroundColor(.secondary)
}
Spacer()
Text("settings".localized)
.font(.system(size: 28, weight: .bold, design: .rounded))
.foregroundColor(.primary)
.id(languageManager.refreshTrigger)
}
.padding(.top, 20)
//
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "globe")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.blue)
.frame(width: 32)
VStack(alignment: .leading, spacing: 4) {
Text("language_settings".localized)
.font(.system(size: 18, weight: .semibold))
.id(languageManager.refreshTrigger)
Text("select_app_language".localized)
.id(languageManager.refreshTrigger)
.font(.system(size: 14))
.foregroundColor(.secondary)
}
Picker("language".localized, selection: $languageManager.currentLanguage) {
ForEach(Language.allCases, id: \.self) { language in
Text(language.displayName).tag(language)
}
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: languageManager.currentLanguage) { newLanguage in
languageManager.switchLanguage(to: newLanguage)
Spacer()
}
Picker("language".localized, selection: $languageManager.currentLanguage) {
ForEach(Language.allCases, id: \.self) { language in
Text(language.displayName).tag(language)
}
}
.padding(20)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color(.systemBackground))
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
)
.padding(.horizontal, 20)
.pickerStyle(SegmentedPickerStyle())
.onChange(of: languageManager.currentLanguage) { newLanguage in
languageManager.switchLanguage(to: newLanguage)
}
}
.padding(20)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color(.systemBackground))
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
)
.padding(.horizontal, 20)
//
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "info.circle")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.green)
.frame(width: 32)
Text("app_info".localized)
.font(.system(size: 18, weight: .semibold))
Spacer()
}
//
VStack(alignment: .leading, spacing: 16) {
VStack(spacing: 12) {
HStack {
Image(systemName: "info.circle")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.green)
.frame(width: 32)
Text("app_info".localized)
.font(.system(size: 18, weight: .semibold))
Text("version".localized)
.font(.system(size: 16))
.foregroundColor(.secondary)
Spacer()
Text("version_number".localized)
.font(.system(size: 16, weight: .medium))
}
VStack(spacing: 12) {
HStack {
Text("version".localized)
.font(.system(size: 16))
.foregroundColor(.secondary)
Spacer()
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))
}
HStack {
Text("build_version".localized)
.font(.system(size: 16))
.foregroundColor(.secondary)
Spacer()
Text("build_number".localized)
.font(.system(size: 16, weight: .medium))
}
}
.padding(20)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color(.systemBackground))
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
)
.padding(.horizontal, 20)
//
}
.padding(20)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color(.systemBackground))
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
)
.padding(.horizontal, 20)
//
NavigationLink(destination: AppPermissionsView().environmentObject(languageManager)) {
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "star.fill")
Image(systemName: "lock.shield")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.orange)
.foregroundColor(.blue)
.frame(width: 32)
Text("features".localized)
Text("app_permissions".localized)
.font(.system(size: 18, weight: .semibold))
Spacer()
}
VStack(spacing: 16) {
FeatureRow(
icon: "camera.fill",
iconColor: .blue,
title: "scan_feature_title".localized,
description: "scan_feature_description".localized
)
FeatureRow(
icon: "plus.circle.fill",
iconColor: .green,
title: "create_feature_title".localized,
description: "create_feature_description".localized
)
FeatureRow(
icon: "clock.arrow.circlepath",
iconColor: .orange,
title: "history_feature_title".localized,
description: "history_feature_description".localized
)
Image(systemName: "chevron.right")
.font(.system(size: 14, weight: .medium))
.foregroundColor(.secondary)
}
Text("manage_app_permissions".localized)
.font(.system(size: 14))
.foregroundColor(.secondary)
.lineLimit(nil)
}
.padding(20)
.background(
@ -172,23 +157,30 @@ struct SettingsView: View {
.fill(Color(.systemBackground))
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
)
.padding(.horizontal, 20)
}
.buttonStyle(PlainButtonStyle())
.padding(.horizontal, 20)
//
//
NavigationLink(destination: PrivacyPolicyView().environmentObject(languageManager)) {
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "heart.fill")
Image(systemName: "hand.raised.fill")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.red)
.foregroundColor(.green)
.frame(width: 32)
Text("about".localized)
Text("privacy_policy".localized)
.font(.system(size: 18, weight: .semibold))
Spacer()
Image(systemName: "chevron.right")
.font(.system(size: 14, weight: .medium))
.foregroundColor(.secondary)
}
Text("app_description_long".localized)
Text("view_privacy_policy".localized)
.font(.system(size: 14))
.foregroundColor(.secondary)
.lineLimit(nil)
@ -199,45 +191,43 @@ struct SettingsView: View {
.fill(Color(.systemBackground))
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
)
.padding(.horizontal, 20)
Spacer(minLength: 30)
}
}
}
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(false)
}
}
}
.buttonStyle(PlainButtonStyle())
.padding(.horizontal, 20)
// MARK: -
struct FeatureRow: View {
let icon: String
let iconColor: Color
let title: String
let description: String
//
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "heart.fill")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.red)
.frame(width: 32)
var body: some View {
HStack(alignment: .top, spacing: 12) {
Image(systemName: icon)
.font(.system(size: 16, weight: .medium))
.foregroundColor(iconColor)
.frame(width: 20)
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.system(size: 16, weight: .medium))
Text(description)
.font(.system(size: 14))
.foregroundColor(.secondary)
.lineLimit(3)
}
Text("about".localized)
.font(.system(size: 18, weight: .semibold))
Spacer()
}
Spacer()
Text("app_description_long".localized)
.font(.system(size: 14))
.foregroundColor(.secondary)
.lineLimit(nil)
}
.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)
}
}
}
.navigationTitle("settings".localized)
.navigationBarTitleDisplayMode(.large)
}
}

@ -82,6 +82,30 @@
"loading" = "Loading...";
"load_more" = "Load More";
"loading_more" = "Loading more...";
// App Permissions
"app_permissions" = "App Permissions";
"permissions_info" = "Permissions Information";
"permissions_description" = "This app requires certain permissions to function properly. You can manage these permissions here or in your device's Settings app.";
"camera_permission" = "Camera Permission";
"camera_permission_description" = "Required to scan QR codes and barcodes using your device's camera.";
"photo_permission" = "Photo Library Permission";
"photo_permission_description" = "Required to save generated QR codes and barcodes to your photo library.";
"system_settings" = "System Settings";
"system_settings_description" = "You can also manage app permissions in your device's Settings app.";
"open_system_settings" = "Open Settings";
"not_determined" = "Not Determined";
"restricted" = "Restricted";
"denied" = "Denied";
"authorized" = "Authorized";
"limited" = "Limited";
"unknown" = "Unknown";
"request_permission" = "Request Permission";
"open_settings" = "Open Settings";
"permission_granted" = "Permission Granted";
"manage_app_permissions" = "Manage camera and photo library permissions for this app.";
"view_privacy_policy" = "View our privacy policy and data handling practices.";
"no_history_records" = "No history records";
"scan_or_create_to_start" = "Scan QR codes or manually create to start recording";
"create_first_record" = "Create First Record";

@ -84,6 +84,28 @@
"loading" = "กำลังโหลด...";
"load_more" = "โหลดเพิ่มเติม";
"loading_more" = "กำลังโหลดเพิ่มเติม...";
// App Permissions
"app_permissions" = "สิทธิ์การเข้าถึงแอป";
"permissions_info" = "ข้อมูลสิทธิ์การเข้าถึง";
"permissions_description" = "แอปนี้ต้องการสิทธิ์การเข้าถึงบางอย่างเพื่อให้ทำงานได้อย่างถูกต้อง คุณสามารถจัดการสิทธิ์เหล่านี้ได้ที่นี่หรือในแอปการตั้งค่าของอุปกรณ์ของคุณ";
"camera_permission" = "สิทธิ์การเข้าถึงกล้อง";
"camera_permission_description" = "จำเป็นสำหรับการสแกน QR code และบาร์โค้ดโดยใช้กล้องของอุปกรณ์ของคุณ";
"photo_permission" = "สิทธิ์การเข้าถึงคลังรูปภาพ";
"photo_permission_description" = "จำเป็นสำหรับการบันทึก QR code และบาร์โค้ดที่สร้างขึ้นไปยังคลังรูปภาพของคุณ";
"system_settings" = "การตั้งค่าระบบ";
"system_settings_description" = "คุณสามารถจัดการสิทธิ์การเข้าถึงแอปในแอปการตั้งค่าของอุปกรณ์ของคุณได้เช่นกัน";
"open_system_settings" = "เปิดการตั้งค่า";
"not_determined" = "ยังไม่ได้กำหนด";
"restricted" = "ถูกจำกัด";
"denied" = "ถูกปฏิเสธ";
"authorized" = "ได้รับอนุญาต";
"limited" = "จำกัด";
"unknown" = "ไม่ทราบ";
"request_permission" = "ขอสิทธิ์การเข้าถึง";
"open_settings" = "เปิดการตั้งค่า";
"permission_granted" = "ได้รับสิทธิ์การเข้าถึงแล้ว";
"view_privacy_policy" = "ดูนโยบายความเป็นส่วนตัวและแนวทางปฏิบัติด้านการจัดการข้อมูลของเรา";
"no_history_records" = "ไม่มีประวัติการบันทึก";
"scan_or_create_to_start" = "สแกน QR code หรือสร้างด้วยตนเองเพื่อเริ่มบันทึก";
"create_first_record" = "สร้างบันทึกแรก";

@ -84,6 +84,28 @@
"loading" = "加载中...";
"load_more" = "加载更多";
"loading_more" = "正在加载更多...";
// App Permissions
"app_permissions" = "应用权限";
"permissions_info" = "权限信息";
"permissions_description" = "此应用需要某些权限才能正常运行。您可以在此处或设备的设置应用中管理这些权限。";
"camera_permission" = "相机权限";
"camera_permission_description" = "需要使用您设备的相机来扫描二维码和条形码。";
"photo_permission" = "相册权限";
"photo_permission_description" = "需要将生成的二维码和条形码保存到您的相册中。";
"system_settings" = "系统设置";
"system_settings_description" = "您也可以在设备的设置应用中管理应用权限。";
"open_system_settings" = "打开设置";
"not_determined" = "未确定";
"restricted" = "受限制";
"denied" = "已拒绝";
"authorized" = "已授权";
"limited" = "有限";
"unknown" = "未知";
"request_permission" = "请求权限";
"open_settings" = "打开设置";
"permission_granted" = "权限已授予";
"view_privacy_policy" = "查看我们的隐私政策和数据处理实践。";
"no_history_records" = "暂无历史记录";
"scan_or_create_to_start" = "扫描二维码或手动创建来开始记录";
"create_first_record" = "创建第一个记录";

@ -0,0 +1,735 @@
// 中文本地化字符串
// 应用标题
"app_title" = "MyQrCode";
// 扫描视图
"scanner_title" = "条码扫描器";
"scan_instruction" = "将二维码或条形码放入框内";
"detected_codes" = "检测到条码";
"auto_result_1s" = "1秒后自动显示结果";
"select_code_instruction" = "点击绿色标记选择要解码的条码";
"rescan_button" = "重新扫描";
"close_button" = "关闭";
// 扫描线样式
"style_modern" = "现代科技";
"style_classic" = "经典简约";
"style_neon" = "霓虹炫酷";
"style_minimal" = "极简主义";
"style_retro" = "复古风格";
// 主视图
"main_title" = "条码扫描器";
"app_description" = "轻松扫描二维码和条形码";
"start_scanning" = "开始扫描";
"scan_result" = "扫描结果:";
"language" = "语言";
// 错误信息
"scan_error_title" = "扫描失败";
"scan_error_message" = "您的设备不支持扫描二维码。请使用带相机的设备。";
// 测试按钮
"test_auto_select" = "测试自动选择";
// 相机权限
"camera_permission_title" = "需要相机权限";
"camera_permission_description" = "此应用需要访问您的相机来扫描二维码和条形码。请授予相机权限以继续使用。";
"camera_permission_denied" = "相机访问被拒绝。请在设置中启用相机权限以使用扫描器。";
"camera_permission_restricted" = "相机访问受限。请检查您的设备设置或联系管理员。";
"camera_permission_unknown" = "相机权限状态未知。请检查您的设备设置。";
"request_camera_permission" = "授予相机权限";
"open_settings" = "打开设置";
// 语言设置
"select_language" = "选择语言";
"language_changes_info" = "语言更改将立即生效";
"current_language" = "当前语言: %@";
"language_settings" = "语言设置";
"done" = "完成";
// 主内容视图
"qr_code_creator" = "二维码生成器";
"quick_create_scan" = "快速创建和扫描二维码";
"create_qr_code" = "创建二维码";
"generate_various_codes" = "生成文本、链接、WiFi、联系人等各种二维码";
"scan_recognize" = "扫描识别";
"scan_qr_code" = "扫描二维码";
"history_records" = "历史记录";
"view_history" = "查看历史";
// 二维码详情视图
"scan_this_qr_code" = "扫描此二维码";
"share" = "分享";
"add_to_image" = "添加到图片";
"parsed_info" = "解析信息";
"original_content" = "原始内容";
"copy_content" = "复制内容";
"open_link" = "打开链接";
"decorate_code" = "装饰代码";
"qr_code_has_style" = "此二维码已有自定义样式,点击可重新编辑";
// 二维码样式视图
"select_dot_type" = "选择点类型";
"select_eye_type" = "选择眼睛类型";
"select_logo" = "选择Logo";
"none" = "无";
"no_logo" = "无Logo";
"custom" = "自定义";
"permission_required" = "需要权限";
// 设置视图
"settings" = "设置";
"select_app_language" = "选择应用显示语言";
"app_info" = "应用信息";
"version" = "版本";
"version_number" = "1.0.0";
"build_version" = "构建版本";
"build_number" = "1";
"features" = "功能特色";
"about" = "关于";
"app_description_long" = "QR Scanner 是一款功能强大的二维码和条形码扫描应用,支持多种格式的条码识别和创建。";
// 历史记录视图
"confirm_delete_record" = "确定要删除这条记录吗?\n内容%@";
"loading" = "加载中...";
"load_more" = "加载更多";
"loading_more" = "正在加载更多...";
// App Permissions
"app_permissions" = "应用权限";
"permissions_info" = "权限信息";
"permissions_description" = "此应用需要某些权限才能正常运行。您可以在此处或设备的设置应用中管理这些权限。";
"camera_permission" = "相机权限";
"camera_permission_description" = "需要使用您设备的相机来扫描二维码和条形码。";
"photo_permission" = "相册权限";
"photo_permission_description" = "需要将生成的二维码和条形码保存到您的相册中。";
"system_settings" = "系统设置";
"system_settings_description" = "您也可以在设备的设置应用中管理应用权限。";
"open_system_settings" = "打开设置";
"not_determined" = "未确定";
"restricted" = "受限制";
"denied" = "已拒绝";
"authorized" = "已授权";
"limited" = "有限";
"unknown" = "未知";
"request_permission" = "请求权限";
"open_settings" = "打开设置";
"permission_granted" = "权限已授予";
"manage_app_permissions" = "管理此应用的相机和相册权限。";
// Privacy Policy
"privacy_policy" = "隐私政策";
"last_updated" = "最后更新";
"privacy_policy_date" = "2024年12月28日";
"privacy_overview" = "隐私概述";
"privacy_overview_content" = "本隐私政策描述了MyQrCode在使用我们的移动应用程序时如何收集、使用和保护您的信息。";
"information_collection" = "信息收集";
"information_collection_content" = "我们仅收集应用功能所需的最少信息。这包括用于扫描二维码的相机访问权限和用于保存生成代码的相册访问权限。我们不收集姓名、电子邮件地址或电话号码等个人信息。";
"information_usage" = "信息使用";
"information_usage_content" = "我们收集的信息仅用于应用功能:扫描二维码、生成二维码以及将图像保存到您的设备。我们不将您的信息用于广告、分析或任何其他商业目的。";
"information_sharing" = "信息共享";
"information_sharing_content" = "我们不会与第三方共享、出售或转让您的信息。所有数据处理都在您的设备本地进行,我们不会向外部服务器传输任何信息。";
"data_security" = "数据安全";
"data_security_content" = "我们实施适当的安全措施来保护您的信息。所有数据都存储在您的设备本地,不会传输到外部服务器。我们使用行业标准的加密和安全实践。";
"user_rights" = "您的权利";
"user_rights_content" = "您有权随时访问、修改或删除您的数据。您可以通过设备设置撤销应用权限。您也可以卸载应用来删除所有相关数据。";
"children_privacy" = "儿童隐私";
"children_privacy_content" = "我们的应用不会故意收集13岁以下儿童的个人信息。如果您是父母或监护人并认为您的孩子向我们提供了个人信息请立即联系我们。";
"policy_changes" = "政策变更";
"policy_changes_content" = "我们可能会不时更新此隐私政策。我们将通过在应用中发布新的隐私政策并更新"最后更新"日期来通知您任何变更。";
"contact_us" = "联系我们";
"contact_us_content" = "如果您对此隐私政策或我们的数据实践有任何疑问,请通过应用商店或我们的支持渠道联系我们。";
"privacy_agreement" = "使用此应用即表示您同意本隐私政策的条款。";
"agree_and_continue" = "同意并继续";
"view_privacy_policy" = "查看我们的隐私政策和数据处理实践。";
"no_history_records" = "暂无历史记录";
"scan_or_create_to_start" = "扫描二维码或手动创建来开始记录";
"create_first_record" = "创建第一个记录";
"clear_history" = "清空历史记录";
"clear_history_warning" = "此操作将删除所有历史记录,且不可撤销";
"confirm_delete" = "确认删除";
"cancel" = "取消";
// 条形码详情视图
"scan_this_barcode" = "扫描此条形码";
"barcode_type" = "条形码类型";
"barcode_content" = "条形码内容";
"content_length" = "内容长度: %d 字符";
"data_content" = "数据内容";
"share_barcode_image" = "分享条形码图片";
// 代码类型选择
"data_type" = "数据类型";
"qr_code_type" = "二维码类型";
"next_step" = "下一步";
// 输入组件
"character_type" = "字符类型:";
"input_hint" = "输入提示";
"location_name" = "位置名称";
"latitude" = "纬度";
"longitude" = "经度";
"coordinate_format_help" = "坐标格式说明";
"coordinate_format_details" = "• 纬度范围:-90 到 90\n• 经度范围:-180 到 180\n• 使用小数点分隔40.7589";
"network_name" = "网络名称 (SSID)";
"password" = "密码";
"encryption_type" = "加密类型";
"social_platform" = "社交平台";
"phone_type" = "电话类型";
"qrcode_type" = "二维码类型";
"format_help" = "格式说明";
"wifi_format_details" = "• 网络名称(SSID)为必填项\n• 密码为可选项,无加密时可留空\n• 将生成标准WiFi连接格式";
"event_title" = "事件标题";
"event_description" = "事件描述";
"start_time" = "开始时间";
"end_time" = "结束时间";
// 验证和状态
"format_correct" = "✓ 格式正确";
"format_checking" = "⚠ 格式检查中...";
"length_requirement" = "长度要求: %d 位";
"allowed_characters" = "允许字符: %@";
"formatted_content" = "格式化: %@";
"please_enter_valid_format" = "请输入符合 %@ 格式的内容";
"cannot_generate_barcode" = "无法生成条形码";
"check_input_format" = "请检查输入内容格式";
// 扫描器组件
"image_decode" = "图片解码";
"scanning_line_style" = "扫描线样式";
"decode_failed" = "解码失败";
"reselect_image" = "重新选择图片";
// 工具栏
"simple_toolbar" = "简单工具栏";
"toolbar_with_clear" = "带清空按钮的工具栏";
"toolbar_with_copy_paste" = "带复制粘贴的工具栏";
"toolbar_with_navigation" = "带导航的工具栏";
// 导航标题
"custom_style" = "自定义样式";
"qr_code_saved" = "二维码已保存";
"select_type" = "选择类型";
"barcode_detail" = "条形码详情";
"add_to_picture" = "添加到图片";
"scanner" = "扫描器";
// 按钮
"create" = "创建";
"confirm" = "确定";
"save" = "保存";
"close" = "关闭";
"complete" = "完成";
"return_home" = "返回主页";
"retry" = "重试";
"error_occurred" = "出错了";
"load_failed_retry" = "加载失败,请重试";
"item_format" = "项目 %d";
"no_data" = "暂无数据";
"no_content_yet" = "这里还没有任何内容";
"add_content" = "添加内容";
"loading_data" = "正在加载数据...";
"network_error" = "网络错误";
"connection_failed_check_network" = "无法连接到服务器,请检查网络连接";
"delete" = "删除";
// 提示框
"tip" = "提示";
"delete_confirmation" = "删除确认";
// 表单标签
"first_name" = "名";
"last_name" = "姓";
"content" = "内容";
"preview" = "预览";
"cannot_generate_qrcode" = "无法生成二维码";
"sample_content" = "示例内容";
"yesterday" = "昨天";
"days_ago" = "%d天前";
"hours_ago" = "%d小时前";
"minutes_ago" = "%d分钟前";
"just_now" = "刚刚";
"weak" = "弱";
"medium" = "中";
"strong" = "强";
"added_to_favorites" = "已添加到收藏";
"removed_from_favorites" = "已取消收藏";
"email_content_format" = "邮箱: %@\n主题: %@\n正文: %@";
"email_cc_format" = "\n抄送: %@";
"email_bcc_format" = "\n密送: %@";
"wifi_content_format" = "WiFi: %@ (%@)";
"contact_content_prefix" = "联系人: ";
"contact_nickname_format" = " (%@)";
"contact_phone_format" = "\n电话: %@";
"contact_email_format" = "\n邮箱: %@";
"contact_company_format" = "\n公司: %@";
"contact_title_format" = "\n职位: %@";
"contact_address_format" = "\n地址: %@";
"contact_website_format" = "\n网站: %@";
"contact_note_format" = "\n备注: %@";
"location_content_format" = "位置: %@, %@";
"calendar_content_format" = "事件: %@";
"phone_content_format" = "电话: %@";
"url_content_format" = "URL: %@";
"qrcode_created_successfully" = "二维码创建成功!";
"save_failed_error" = "保存失败:%@";
"create_data_type" = "创建%@";
"barcode_format_incorrect" = "条形码格式不正确";
"data_type_created_successfully" = "%@创建成功!";
"all" = "全部";
"qrcode" = "二维码";
"created" = "手动创建";
"favorites" = "收藏";
"search_history_records" = "搜索历史记录...";
"qr_code_detail" = "二维码详情";
"standard" = "标准";
"standard_card" = "标准卡片";
"standard_card_description" = "这是一个标准样式的卡片组件";
"compact_card" = "紧凑卡片";
"max_characters_reached" = "已达到最大字符数";
"near_character_limit" = "接近字符限制";
"character_count" = "%d/%d";
// Calendar Input
"calendar" = "日历";
"event_location" = "事件地点";
"event_title_placeholder" = "会议标题";
"event_description_placeholder" = "事件详细描述";
"event_location_placeholder" = "会议地点";
"time_validation_error" = "结束时间必须晚于开始时间";
"calendar_format_hint" = "• 填写事件信息\n• 将生成日历事件格式\n• 可导入到日历应用";
"time_setting_hint" = "时间设置提示";
"end_time_must_be_after_start_time" = "结束时间必须晚于开始时间";
// Social Input
"social" = "社交";
"username" = "用户名";
"social_message" = "消息";
"instagram_placeholder" = "用户名或链接";
"facebook_placeholder" = "用户名或链接";
"twitter_placeholder" = "用户名";
"tiktok_placeholder" = "用户名";
"snapchat_placeholder" = "用户名";
"whatsapp_placeholder" = "输入WhatsApp电话号码";
"viber_placeholder" = "电话号码";
"spotify_placeholder" = "歌曲或播放列表链接";
"instagram_hint" = "输入Instagram用户名";
"facebook_hint" = "输入Facebook用户ID或链接";
"twitter_hint" = "输入X用户名或完整链接";
"tiktok_hint" = "输入TikTok用户名或完整链接";
"snapchat_hint" = "输入Snapchat用户名";
"whatsapp_hint" = "输入WhatsApp消息内容";
"viber_hint" = "输入Viber电话号码";
"spotify_hint" = "输入Spotify歌曲或播放列表链接";
"social_format_hint" = "• 输入社交媒体信息\n• 将生成社交媒体链接\n• 用户点击可打开社交应用";
"artist" = "艺术家";
"song_name" = "歌曲名称";
"enter_artist_name" = "输入艺术家名称";
"enter_song_name" = "输入歌曲名称";
"instagram_username" = "Instagram用户名";
"user_id_or_link" = "用户ID或链接";
"x_username" = "X用户名";
"tiktok_username" = "TikTok用户名";
"snapchat_username" = "Snapchat用户名";
"whatsapp_phone_number" = "WhatsApp电话号码";
"viber_phone_number" = "Viber电话号码";
"song_link_or_id" = "歌曲链接或ID";
// Card Components
"info_card" = "信息卡片";
"important_reminder" = "重要提醒";
"info_card_description" = "这是一个信息卡片,用于显示重要的提示信息。";
"learn_more" = "了解更多";
"total_users" = "总用户数";
"new_this_month" = "本月新增";
// Input Hints
"info_hint" = "这是一个信息提示";
"warning_hint" = "这是一个警告提示";
"success_hint" = "这是一个成功提示";
"error_hint" = "这是一个错误提示";
// Date Picker
"select_date" = "选择日期";
"select_time" = "选择时间";
"select_date_and_time" = "选择日期和时间";
// Create QR Code
"content_input_area" = "内容输入区域";
"preview_area" = "预览区域";
// QR Code Saved
"qr_code_image" = "二维码图片";
"operation_buttons" = "操作按钮";
"share_button" = "分享按钮";
"save_to_photos_button" = "保存到相册按钮";
"add_to_picture_button" = "添加到图片按钮";
"check_photo_permission" = "检查相册权限";
"select_background_image" = "选择背景图片";
"image_save_helper" = "图片保存辅助类";
// QR Code Style
"tag_type" = "标签类型";
"custom_qr_code_style" = "自定义二维码样式";
"existing_style_data" = "现有样式数据";
"existing_history_item" = "现有历史记录项";
"color_selection" = "颜色选择";
"dot_type_selection" = "点类型选择";
"eye_type_selection" = "眼睛类型选择";
"logo_selection" = "Logo选择";
"loading_state" = "加载状态";
"selected_tag_type" = "选中的标签类型";
"create_qr_code_document" = "创建QRCode文档";
"use_passed_qr_code_content" = "使用传入的二维码内容";
"set_background_color" = "设置背景色";
"set_eye_style" = "设置眼睛样式";
"set_dot_style" = "设置点样式";
"set_eye_shape" = "设置眼睛形状";
"set_logo_if_selected" = "如果有选择的Logo设置Logo";
// Keyboard Toolbar
"clear" = "清空";
"copy" = "复制";
"paste" = "粘贴";
"next" = "下一个";
"previous" = "上一个";
// Form Components
"sample_form" = "示例表单";
"basic_info" = "基本信息";
"enter_username" = "请输入用户名";
"enter_email" = "请输入邮箱";
"actions" = "操作";
// Models - History Enums
"scanned" = "扫描获得";
"manually_created" = "手动创建";
"barcode" = "条形码";
"qr_code" = "二维码";
"foreground_color" = "前景色";
"background_color" = "背景色";
"dot_type" = "点类型";
"eye_type" = "眼睛类型";
"custom_logo" = "自定义Logo";
// Models - Barcode Validator
"numbers_0_9" = "数字 (0-9)";
"ean_13_must_be_13_digits" = "EAN-13必须是13位数字";
"ean_8_must_be_8_digits" = "EAN-8必须是8位数字";
"upc_e_must_be_8_digits" = "UPC-E必须是8位数字";
"code_39_characters" = "字母 (A-Z)、数字 (0-9)、空格、特殊字符 (- + . / $ ( ) %)";
"code_39_only_contains" = "Code 39只能包含字母、数字、空格和特殊字符";
"code_128_characters" = "所有ASCII字符 (0-127)";
"code_128_only_contains" = "Code 128只能包含ASCII字符";
"itf_14_must_be_14_digits" = "ITF-14必须是14位数字";
"itf_14_only_digits" = "ITF-14只能包含数字";
"codabar_characters" = "数字 (0-9)、字母 (A-D)、特殊字符 (- + . / $ :)";
"codabar_only_contains" = "Codabar只能包含数字、字母A-D和特殊字符";
"pdf417_characters" = "所有ASCII字符 (0-127)";
"pdf417_only_contains" = "PDF417只能包含ASCII字符";
"data_matrix_characters" = "所有ASCII字符 (0-127)";
"data_matrix_only_contains" = "Data Matrix只能包含ASCII字符";
"aztec_characters" = "所有ASCII字符 (0-127)";
"aztec_only_contains" = "Aztec只能包含ASCII字符";
"maxi_code_characters" = "所有ASCII字符 (0-127)";
"maxi_code_only_contains" = "MaxiCode只能包含ASCII字符";
// Models - QR Code Style Models
"square" = "方形";
"circle" = "圆形";
"rounded_rect" = "圆角矩形";
"squircle" = "超椭圆";
"diamond" = "菱形";
"hexagon" = "六边形";
"star" = "星形";
"heart" = "心形";
"flower" = "花朵";
"gear" = "齿轮";
"abstract" = "抽象";
"arrow" = "箭头";
"blob" = "斑点";
"circuit" = "电路";
"crosshatch" = "交叉线";
"curve_pixel" = "曲线像素";
"diagonal" = "对角线";
"diagonal_stripes" = "对角条纹";
"donut" = "甜甜圈";
"drip_horizontal" = "水平滴落";
"drip_vertical" = "垂直滴落";
"flame" = "火焰";
"grid_2x2" = "2x2网格";
"grid_3x3" = "3x3网格";
"grid_4x4" = "4x4网格";
"horizontal" = "水平";
"koala" = "考拉";
"pointy" = "尖角";
"razor" = "剃刀";
"rounded_end_indent" = "圆角缩进";
"rounded_path" = "圆角路径";
"rounded_triangle" = "圆角三角形";
"sharp" = "尖锐";
"shiny" = "闪亮";
"spiky_circle" = "尖刺圆形";
"stitch" = "缝合";
"vertical" = "垂直";
"vortex" = "漩涡";
"wave" = "波浪";
"wex" = "Wex";
// Models - QR Code Eye Types
"arc" = "弧形";
"bars_horizontal" = "水平条";
"bars_vertical" = "垂直条";
"cloud" = "云朵";
"cloud_circle" = "云朵圆形";
"cornered_pixels" = "角像素";
"dot_drag_horizontal" = "水平拖拽点";
"dot_drag_vertical" = "垂直拖拽点";
"edges" = "边缘";
"explode" = "爆炸";
"eye" = "眼睛";
"fabric_scissors" = "剪刀";
"fireball" = "火球";
"headlight" = "头灯";
"hole_punch" = "打孔";
"leaf" = "叶子";
"peacock" = "孔雀";
"pinch" = "捏合";
"pixels" = "像素";
"rounded_outer" = "圆角外";
"rounded_pointing_in" = "圆角向内";
"rounded_pointing_out" = "圆角向外";
"shield" = "盾牌";
"square_peg" = "方形钉";
"surrounding_bars" = "环绕条";
"teardrop" = "泪滴";
"ufo" = "UFO";
"ufo_rounded" = "圆角UFO";
"use_pixel_shape" = "使用像素形状";
// Models - QR Code Logo Types
"scan_me" = "扫描我";
"gmail" = "Gmail";
"paypal" = "PayPal";
"google_playstore" = "Google Play";
"spotify" = "Spotify";
"telegram" = "Telegram";
"whats_app" = "WhatsApp";
"linked_in" = "LinkedIn";
"tik_tok" = "TikTok";
"snapchat" = "Snapchat";
"youtube" = "YouTube";
"x" = "X";
"pinterest" = "Pinterest";
"instagram" = "Instagram";
"facebook" = "Facebook";
// Models - QR Code Parser
"text_information" = "文本信息";
"wifi_network" = "Wi-Fi网络";
"network_name" = "网络名称";
"not_set" = "无";
"email_address" = "邮箱地址";
"phone_number" = "电话号码";
"sms" = "短信";
"number" = "号码";
"contact_information" = "联系人信息";
"name" = "姓名";
"email" = "邮箱";
"company" = "公司";
"job_title" = "职位";
"address" = "地址";
"website" = "网站";
"nickname" = "昵称";
"birthday" = "生日";
"note" = "备注";
"calendar_event" = "日历事件";
"event" = "事件";
"start" = "开始";
"end" = "结束";
"location" = "地点";
"description" = "描述";
"year" = "年";
"month" = "月";
"day" = "日";
"url" = "网址";
"tiktok" = "TikTok";
"whatsapp" = "WhatsApp";
"linkedin" = "LinkedIn";
"x_platform" = "X";
"url_link" = "网址链接";
"user_id" = "用户ID";
"search" = "搜索";
// Models - Core Data Manager
"core_data_load_failed" = "Core Data 加载失败: %@";
"architecture_mismatch_detected" = "🔄 检测到架构不匹配,删除现有数据库文件";
"core_data_reload_failed" = "❌ 重新加载Core Data失败: %@";
"core_data_reload_success" = "✅ Core Data重新加载成功";
"core_data_save_success" = "✅ Core Data保存成功";
"core_data_save_failed" = "❌ Core Data保存失败: %@";
"error_details" = "❌ 错误详情: %@";
"error_domain" = "❌ 错误域: %@";
// 二维码预览
"cannot_generate_qr_code" = "无法生成二维码";
// 功能特色描述
"scan_feature_title" = "扫描功能";
"scan_feature_description" = "支持扫描二维码和条形码,自动识别类型并保存到历史记录";
"create_feature_title" = "创建功能";
"create_feature_description" = "可以手动创建各种类型的二维码和条形码";
"history_feature_title" = "历史记录";
"history_feature_description" = "自动保存所有扫描和创建的码,支持收藏和管理";
// QR Code Saved View
"qr_code_saved_title" = "二维码已保存";
"saving" = "保存中...";
"qr_code_saved_to_photos" = "二维码已保存到相册";
"save_failed" = "保存失败:%@";
"photo_permission_required" = "需要相册权限才能保存图片,请在设置中开启";
// Image Composer View
"add_to_picture_title" = "添加到图片";
// Barcode Character Hint View
"numbers" = "数字";
"letters" = "字母";
"special_characters" = "特殊字符";
"symbols" = "符号";
"control_characters" = "控制字符";
"all_ascii" = "所有ASCII";
// QR Code Style View
"colors" = "颜色";
"dot_types" = "点类型";
"eyes" = "眼睛";
"logo" = "Logo";
// Scanner View
"scanner_title" = "扫描器";
// Settings View
// Logger
"debug" = "调试";
"info" = "信息";
"warning" = "警告";
"error" = "错误";
"success" = "成功";
// Phone Input
"phone" = "电话";
"sms_content" = "短信内容";
"enter_phone_number" = "输入电话号码,支持国际格式";
"enter_sms_content" = "输入短信内容,将生成可发送的链接";
"phone_placeholder" = "+1 (555) 123-4567";
"sms_placeholder" = "输入短信内容";
"format_instructions" = "格式说明";
"phone_format_hint" = "• 支持国际格式:+1 (555) 123-4567\n• 或本地格式:(555) 123-4567\n• 将生成 tel: 链接";
"sms_format_hint" = "• 输入电话号码和短信内容\n• 将生成 SMSTO: 链接\n• 用户点击可直接发送短信";
// Contact Input
"contact" = "联系人";
"contact_phone_placeholder" = "+1 (555) 123-4567";
"contact_email_placeholder" = "user@example.com";
"contact_website_placeholder" = "https://example.com";
"contact_address_placeholder" = "输入地址";
"contact_note_placeholder" = "输入备注";
"contact_format_hint" = "• 填写联系人信息\n• 将生成 vCard 格式\n• 可导入到手机通讯录";
"company_name" = "公司名称";
"title_name" = "职位名称";
"detailed_address" = "详细地址";
"select_birthday" = "选择生日";
"note_info" = "备注信息";
// WiFi Input
"wifi" = "WiFi";
"wifi_password" = "WiFi密码";
"wifi_password_placeholder" = "WiFi密码";
"no_encryption" = "无加密";
// Email Input
"email_subject" = "主题";
"email_body" = "正文";
"email_cc" = "抄送";
"email_bcc" = "密送";
"email_subject_placeholder" = "邮件主题";
"email_body_placeholder" = "输入邮件正文内容...";
"email_cc_placeholder" = "cc@example.com";
"email_bcc_placeholder" = "bcc@example.com";
"email_format_hint" = "• 填写邮件信息\n• 将生成 mailto: 链接\n• 用户点击可打开邮件应用";
"cc_address" = "抄送地址";
"bcc_address" = "密送地址";
"cc_email_placeholder" = "cc@example.com";
"bcc_email_placeholder" = "bcc@example.com";
// Location Input
"location" = "位置";
"location_name_placeholder" = "例如:纽约时代广场";
"latitude_placeholder" = "40.7589";
"longitude_placeholder" = "-73.9851";
"location_format_hint" = "• 输入位置名称和坐标\n• 将生成 geo: 链接\n• 用户点击可打开地图应用";
// URL Input
"website_url" = "网址";
"url_placeholder" = "https://www.example.com";
"url_format_hint" = "• 可以输入完整URLhttps://www.example.com\n• 或输入域名www.example.com\n• 系统会自动添加https://前缀";
"preview_url" = "预览URL";
// Text Input
"text" = "文本";
"text_content" = "文本内容";
"text_placeholder" = "输入文本内容...";
// Validation Messages
"format_error" = "格式错误";
"field_required" = "%@为必填项";
"field_format_incorrect" = "%@格式不正确";
"ean_13_format_hint" = "请输入13位数字1234567890123";
"ean_8_format_hint" = "请输入8位数字12345678";
"upc_e_format_hint" = "请输入8位数字12345678";
"code_39_format_hint" = "请输入字母、数字、空格和特殊字符";
"code_128_format_hint" = "请输入任意ASCII字符";
"itf_14_format_hint" = "请输入14位数字12345678901234";
"pdf417_format_hint" = "请输入任意ASCII字符";
// Input Placeholders
"input_13_digits" = "输入13位数字";
"input_8_digits" = "输入8位数字";
"input_letters_numbers" = "输入字母、数字等";
"input_any_characters" = "输入任意字符";
"input_14_digits" = "输入14位数字";
"please_enter_content" = "请输入内容";
// Text Editor
"long_text" = "长文本";
"email_body" = "邮件正文";
"enter_description_content" = "请输入描述内容...";
"enter_long_text_content" = "请输入长文本内容...";
"enter_email_body_content" = "输入邮件正文内容...";
// Input Fields
"enter_password" = "请输入密码";
// Barcode Detail
"unfavorite" = "取消收藏";
"favorite" = "收藏";
"content_copied_to_clipboard" = "内容已复制到剪贴板";
// QR Code Parser
"sms_number_content" = "号码: %@\n内容: %@";
"contact_name" = "姓名: %@";
"contact_phone" = "电话: %@";
"contact_email" = "邮箱: %@";
"contact_company" = "公司: %@";
"contact_title" = "职位: %@";
"contact_address" = "地址: %@";
"contact_website" = "网站: %@";
"unknown_content" = "未知内容";
"no_codes_detected_in_image" = "图片中未检测到二维码或条形码";
// History Enums
"style_description_format" = "前景色: %@, 背景色: %@, 点类型: %@, 眼睛类型: %@";
"style_logo_format" = ", Logo: %@";
// QR Code Parser - Additional
"wifi_network_info" = "网络名称: %@\n加密类型: %@\n密码: %@";
"password_set" = "已设置";
"geolocation" = "地理位置";
"geolocation_coordinates" = "纬度: %@\n经度: %@";
"calendar_event_info" = "事件: %@\n开始: %@\n结束: %@";
"calendar_event_location" = "\n地点: %@";
"calendar_event_description" = "\n描述: %@";
"instagram_username" = "用户名: %@";
"facebook_profile_id" = "用户ID: %@";
"spotify_search_query" = "搜索: %@";
"twitter_username" = "用户名: %@";
"whatsapp_phone_number" = "电话号码: %@";
"viber_phone_number" = "电话号码: %@";
"snapchat_username" = "用户名: %@";
"tiktok_username" = "用户名: %@";
"contact_nickname" = "昵称: %@";
"contact_birthday" = "生日: %@";
"contact_note" = "备注: %@";
"birthday_format" = "%@年%@月%@日";
// Language Manager
"chinese_language" = "中文";
// Input Component Factory
"input_any_text_content" = "输入任意文本内容...";
"input_phone_number" = "输入电话号码...";
"input_sms_content" = "输入短信内容...";
"input_wifi_info" = "输入WiFi信息...";
"input_contact_info" = "输入联系人信息...";
"input_location_info" = "输入地理位置...";
"input_calendar_event_info" = "输入日历事件信息...";
"input_instagram_username" = "输入Instagram用户名...";
"input_facebook_user_id_or_link" = "输入Facebook用户ID或链接...";
"input_artist_and_song_info" = "输入艺术家和歌曲信息...";
"input_x_info" = "输入X信息...";
"input_whatsapp_phone_number" = "输入WhatsApp电话号码+1234567890...";
"input_viber_phone_number" = "输入Viber电话号码+1234567890...";
"input_snapchat_info" = "输入Snapchat信息...";
"input_tiktok_info" = "输入TikTok信息...";
"input_email_content" = "输入邮件内容...";
"input_website_url" = "输入网址...";
// Color Names
"black" = "黑色";
"white" = "白色";
"red" = "红色";
"blue" = "蓝色";
"green" = "绿色";
"yellow" = "黄色";
"purple" = "紫色";
"orange" = "橙色";
"pink" = "粉色";
"cyan" = "青色";
"magenta" = "洋红色";
"brown" = "棕色";
"gray" = "灰色";
"navy" = "海军蓝";
"teal" = "蓝绿色";
"indigo" = "靛蓝色";
"lime" = "青柠色";
"maroon" = "栗色";
"olive" = "橄榄色";
"silver" = "银色";

@ -0,0 +1,192 @@
# 隐私政策与应用权限功能实现报告
## 📋 概述
本报告详细记录了为MyQrCode应用添加隐私政策和应用权限管理功能的完整实现过程包括界面设计、技术实现、本地化支持以及用户体验优化。
## 🎯 功能特性
### 1. 应用权限管理 (`AppPermissionsView.swift`)
#### 核心功能:
- **实时权限状态检测**:自动检测相机和相册权限状态
- **权限状态显示**:清晰显示当前权限状态(已授权、已拒绝、受限制、未确定、有限访问)
- **权限请求功能**:一键请求相机和相册权限
- **系统设置跳转**:快速跳转到系统设置页面
#### 技术实现:
```swift
// 权限状态检测
private func checkPermissions() {
cameraPermissionStatus = AVCaptureDevice.authorizationStatus(for: .video)
photoPermissionStatus = PHPhotoLibrary.authorizationStatus()
}
// 权限请求
private func requestCameraPermission() {
AVCaptureDevice.requestAccess(for: .video) { _ in
DispatchQueue.main.async {
checkPermissions()
}
}
}
```
#### 界面设计:
- **状态卡片**:每个权限都有独立的状态卡片
- **颜色编码**:不同状态使用不同颜色(绿色=已授权,红色=已拒绝,橙色=受限制等)
- **操作按钮**:根据当前状态显示相应的操作按钮
### 2. 隐私政策界面 (`PrivacyPolicyView.swift`)
#### 核心功能:
- **HTML格式显示**使用WKWebView加载本地HTML文件
- **简约风格设计**:现代化的界面设计
- **完整内容**:包含所有必要的隐私政策章节
#### 技术实现:
```swift
struct WebView: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
return webView
}
}
```
#### HTML文件特点
- **响应式设计**:适配不同屏幕尺寸
- **现代化样式**使用CSS3和现代设计元素
- **清晰结构**8个主要章节内容完整
### 3. 设置界面集成 (`SettingsView.swift`)
#### 新增功能:
- **应用权限卡片**:显示权限管理入口
- **隐私政策卡片**:显示隐私政策入口
- **导航集成**使用NavigationLink直接导航到相关界面
#### 界面设计:
```swift
// 应用权限卡片
NavigationLink(destination: AppPermissionsView().environmentObject(languageManager)) {
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "lock.shield")
Text("app_permissions".localized)
Spacer()
Image(systemName: "chevron.right")
}
Text("manage_app_permissions".localized)
}
}
// 隐私政策卡片
NavigationLink(destination: PrivacyPolicyView().environmentObject(languageManager)) {
VStack(alignment: .leading, spacing: 16) {
HStack {
Image(systemName: "hand.raised.fill")
Text("privacy_policy".localized)
Spacer()
Image(systemName: "chevron.right")
}
Text("view_privacy_policy".localized)
}
}
```
#### 导航优化:
- **移除双重导航栏**解决了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)
### 新增本地化键:
```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.";
```
## 📱 用户体验
### 权限管理:
1. 用户进入设置界面
2. 点击"应用权限"卡片
3. 直接导航到权限管理界面
4. 查看当前权限状态
5. 根据需要请求权限或打开系统设置
### 隐私政策:
1. 用户进入设置界面
2. 点击"隐私政策"卡片
3. 直接导航到隐私政策界面
4. 查看简约风格的HTML格式隐私政策
5. 了解应用的数据处理方式
## 🔧 技术实现细节
### 权限管理:
- 使用`AVFoundation`框架检测相机权限
- 使用`Photos`框架检测相册权限
- 实时更新权限状态显示
- 提供权限请求和系统设置跳转功能
### 隐私政策:
- 使用`WKWebView`加载本地HTML文件
- HTML文件包含完整的隐私政策内容
- 响应式设计适配不同屏幕尺寸
- 现代化的CSS样式设计
### 导航优化:
- 移除SettingsView中的NavigationView包装
- 使用NavigationLink替代sheet模态展示
- 简化导航逻辑,提供更流畅的用户体验
- 删除功能信息卡片,简化设置界面
## 📝 总结
本次更新成功为MyQrCode应用添加了完整的隐私政策和应用权限管理功能包括
1. **完整的权限管理界面**,支持实时状态显示和权限操作
2. **简约风格的隐私政策界面**使用HTML格式提供现代化的英文版隐私政策
3. **无缝的设置界面集成**使用NavigationLink提供直接的导航体验
4. **全面的本地化支持**,支持英文、中文和泰文三种语言
5. **现代化的界面设计**,提供优秀的用户体验
### 主要改进:
- **隐私政策HTML**:改为简约风格设计,使用渐变头部和现代化的布局
- **导航方式**从sheet模态展示改为NavigationLink直接导航提供更流畅的用户体验
- **界面优化**移除了不必要的dismiss相关代码简化了导航逻辑
- **导航栏修复**:解决了双重导航栏问题,提供更清晰的导航体验
- **功能简化**:删除了功能信息卡片,使设置界面更加简洁
所有功能都已通过编译验证,可以立即投入使用。这些功能将帮助应用更好地管理用户权限,提供透明的隐私政策,并符合现代应用的法律和用户体验要求。
Loading…
Cancel
Save