From 9896a536a08e7b43d0cb6bbcf0cf88d569b4b92e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 28 Jun 2026 07:52:20 +0000 Subject: [PATCH 1/3] Add Punjabi (pa) localization for SweepAlert iOS app - Add Localizable.xcstrings with 117 Gurmukhi translations - Register pa locale in Xcode project and InfoPlist.strings - Localize programmatic strings via String(localized:) in views and services Co-authored-by: Anam Hira --- .../SweepAlertSwift.xcodeproj/project.pbxproj | 15 + .../AuthenticationService.swift | 10 +- .../SweepAlertSwift/ContentView.swift | 88 +- .../SweepAlertSwift/ConvexService.swift | 4 +- .../SweepAlertSwift/Localizable.xcstrings | 1176 +++++++++++++++++ .../PushNotificationService.swift | 2 +- .../pa.lproj/InfoPlist.strings | 3 + .../pa.lproj/InfoPlist.strings | 2 + 8 files changed, 1251 insertions(+), 49 deletions(-) create mode 100644 apps/ios-sweep-alert/SweepAlertSwift/Localizable.xcstrings create mode 100644 apps/ios-sweep-alert/SweepAlertSwift/pa.lproj/InfoPlist.strings create mode 100644 apps/ios-sweep-alert/SweepAlertWidget/pa.lproj/InfoPlist.strings diff --git a/apps/ios-sweep-alert/SweepAlertSwift.xcodeproj/project.pbxproj b/apps/ios-sweep-alert/SweepAlertSwift.xcodeproj/project.pbxproj index 5318976..c65c458 100644 --- a/apps/ios-sweep-alert/SweepAlertSwift.xcodeproj/project.pbxproj +++ b/apps/ios-sweep-alert/SweepAlertSwift.xcodeproj/project.pbxproj @@ -19,6 +19,10 @@ A1000010 /* PushNotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000011 /* PushNotificationService.swift */; }; B1000001 /* SweepAlertWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2000001 /* SweepAlertWidget.swift */; }; B1000002 /* SweepAlertWidget.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = B2000003 /* SweepAlertWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + C1000001 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = C2000001 /* Localizable.xcstrings */; }; + C1000002 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = C2000001 /* Localizable.xcstrings */; }; + C1000003 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C2000002 /* InfoPlist.strings */; }; + C1000004 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C2000003 /* InfoPlist.strings */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -61,6 +65,9 @@ B2000001 /* SweepAlertWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SweepAlertWidget.swift; sourceTree = ""; }; B2000002 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B2000003 /* SweepAlertWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SweepAlertWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + C2000001 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + C2000002 /* InfoPlist.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = pa.lproj/InfoPlist.strings; sourceTree = ""; }; + C2000003 /* InfoPlist.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = pa.lproj/InfoPlist.strings; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -105,6 +112,8 @@ A2000006 /* Auth0.plist */, A2000005 /* sweeping.json */, A2000003 /* Assets.xcassets */, + C2000001 /* Localizable.xcstrings */, + C2000002 /* InfoPlist.strings */, ); path = SweepAlertSwift; sourceTree = ""; @@ -123,6 +132,7 @@ children = ( B2000001 /* SweepAlertWidget.swift */, B2000002 /* Info.plist */, + C2000003 /* InfoPlist.strings */, ); path = SweepAlertWidget; sourceTree = ""; @@ -196,6 +206,7 @@ knownRegions = ( en, Base, + pa, ); mainGroup = A4000001; packageReferences = ( @@ -219,6 +230,8 @@ A1000003 /* Assets.xcassets in Resources */, A1000004 /* sweeping.json in Resources */, A1000005 /* Auth0.plist in Resources */, + C1000001 /* Localizable.xcstrings in Resources */, + C1000003 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -226,6 +239,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + C1000002 /* Localizable.xcstrings in Resources */, + C1000004 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/apps/ios-sweep-alert/SweepAlertSwift/AuthenticationService.swift b/apps/ios-sweep-alert/SweepAlertSwift/AuthenticationService.swift index aaf38ce..b7f2bb3 100644 --- a/apps/ios-sweep-alert/SweepAlertSwift/AuthenticationService.swift +++ b/apps/ios-sweep-alert/SweepAlertSwift/AuthenticationService.swift @@ -54,7 +54,7 @@ final class AuthenticationService: ObservableObject { .start() guard credentialsManager.store(credentials: credentials) else { - errorMessage = "Could not store Auth0 credentials." + errorMessage = String(localized: "Could not store Auth0 credentials.") return } @@ -66,7 +66,7 @@ final class AuthenticationService: ObservableObject { } catch { isAuthenticated = false convexSynced = false - errorMessage = "Login failed: \(error.localizedDescription)" + errorMessage = String(localized: "Login failed: \(error.localizedDescription)") } } @@ -80,7 +80,7 @@ final class AuthenticationService: ObservableObject { .webAuth(clientId: AppConfiguration.auth0ClientID, domain: AppConfiguration.auth0Domain) .clearSession() } catch { - errorMessage = "Logout session clear failed: \(error.localizedDescription)" + errorMessage = String(localized: "Logout session clear failed: \(error.localizedDescription)") } _ = credentialsManager.clear() @@ -223,7 +223,7 @@ final class AuthenticationService: ObservableObject { convexSynced = true } catch { convexSynced = false - errorMessage = "Convex sync failed: \(error.localizedDescription)" + errorMessage = String(localized: "Convex sync failed: \(error.localizedDescription)") } } @@ -243,7 +243,7 @@ final class AuthenticationService: ObservableObject { let token = try await pushNotifications.requestDeviceToken() try await convexService.registerDevice(pushToken: token, idToken: credentials.idToken) } catch { - errorMessage = "Push registration failed: \(error.localizedDescription)" + errorMessage = String(localized: "Push registration failed: \(error.localizedDescription)") } } } diff --git a/apps/ios-sweep-alert/SweepAlertSwift/ContentView.swift b/apps/ios-sweep-alert/SweepAlertSwift/ContentView.swift index 684f8ee..112d483 100644 --- a/apps/ios-sweep-alert/SweepAlertSwift/ContentView.swift +++ b/apps/ios-sweep-alert/SweepAlertSwift/ContentView.swift @@ -555,13 +555,13 @@ struct ContentView: View { private func dropPin(at coordinate: CLLocationCoordinate2D) { guard authentication.isAuthenticated else { - statusMessage = "Sign in to create or join a car crew before setting a parking spot." + statusMessage = String(localized: "Sign in to create or join a car crew before setting a parking spot.") settingsPresented = true return } guard let car = selectedCar else { - statusMessage = "Add a car before setting a parking spot." + statusMessage = String(localized: "Add a car before setting a parking spot.") settingsPresented = true return } @@ -626,7 +626,7 @@ struct ContentView: View { isReparking = true rulesSheetExpanded = false } catch { - authentication.errorMessage = "Move claim sync failed: \(error.localizedDescription)" + authentication.errorMessage = String(localized: "Move claim sync failed: \(error.localizedDescription)") } } } @@ -716,14 +716,14 @@ struct ContentView: View { let remoteCrew = try await authentication.ensureDefaultCrew() applyRemoteCrew(remoteCrew, preferredCarId: selectedCarId) } catch { - authentication.errorMessage = "Crew sync failed: \(error.localizedDescription)" + authentication.errorMessage = String(localized: "Crew sync failed: \(error.localizedDescription)") } } private func prepareInviteAndPresent() { guard authentication.isAuthenticated else { settingsPresented = true - statusMessage = "Sign in before creating a car crew invite link." + statusMessage = String(localized: "Sign in before creating a car crew invite link.") return } @@ -732,7 +732,7 @@ struct ContentView: View { inviteURL = try await authentication.createInviteURL() invitePresented = true } catch { - statusMessage = "Could not create invite: \(error.localizedDescription)" + statusMessage = String(localized: "Could not create invite: \(error.localizedDescription)") } } } @@ -749,7 +749,7 @@ struct ContentView: View { } } else { settingsPresented = true - statusMessage = "Sign in to accept this car crew invite." + statusMessage = String(localized: "Sign in to accept this car crew invite.") } } @@ -762,9 +762,9 @@ struct ContentView: View { let remoteCrew = try await authentication.acceptInvite(token: token) applyRemoteCrew(remoteCrew, preferredCarId: remoteCrew.cars.first?.id) pendingInviteToken = nil - statusMessage = "Joined \(remoteCrew.name)." + statusMessage = String(localized: "Joined \(remoteCrew.name).") } catch { - statusMessage = "Could not accept invite: \(error.localizedDescription)" + statusMessage = String(localized: "Could not accept invite: \(error.localizedDescription)") } } @@ -812,7 +812,7 @@ struct ContentView: View { activeParkingSessionId = selectedCarSession?.id activeParkingSessionIsRemote = true } catch { - authentication.errorMessage = "Parking sync failed: \(error.localizedDescription)" + authentication.errorMessage = String(localized: "Parking sync failed: \(error.localizedDescription)") } } } @@ -899,7 +899,7 @@ struct ContentView: View { private func renameCrew(_ name: String) { guard authentication.isAuthenticated else { - statusMessage = "Sign in before renaming your crew." + statusMessage = String(localized: "Sign in before renaming your crew.") return } Task { @@ -907,14 +907,14 @@ struct ContentView: View { let remoteCrew = try await authentication.renameCrew(name: name) applyRemoteCrew(remoteCrew, preferredCarId: selectedCarId) } catch { - statusMessage = "Could not rename crew: \(error.localizedDescription)" + statusMessage = String(localized: "Could not rename crew: \(error.localizedDescription)") } } } private func addCar(_ name: String) { guard authentication.isAuthenticated else { - statusMessage = "Sign in before adding another car." + statusMessage = String(localized: "Sign in before adding another car.") return } Task { @@ -923,14 +923,14 @@ struct ContentView: View { let remoteCrew = try await authentication.createCar(name: name, color: color) applyRemoteCrew(remoteCrew, preferredCarId: remoteCrew.cars.last?.id) } catch { - statusMessage = "Could not add car: \(error.localizedDescription)" + statusMessage = String(localized: "Could not add car: \(error.localizedDescription)") } } } private func renameCar(_ carId: String, _ name: String) { guard authentication.isAuthenticated else { - statusMessage = "Sign in before renaming cars." + statusMessage = String(localized: "Sign in before renaming cars.") return } Task { @@ -938,7 +938,7 @@ struct ContentView: View { let remoteCrew = try await authentication.renameCar(carId: carId, name: name) applyRemoteCrew(remoteCrew, preferredCarId: carId) } catch { - statusMessage = "Could not rename car: \(error.localizedDescription)" + statusMessage = String(localized: "Could not rename car: \(error.localizedDescription)") } } } @@ -1243,7 +1243,9 @@ struct RulesSheet: View { Spacer() - Text("\(visibleRules.count) match\(visibleRules.count == 1 ? "" : "es") within \(Int(searchRadiusMeters))m") + Text(visibleRules.count == 1 + ? String(localized: "\(visibleRules.count) match within \(Int(searchRadiusMeters))m") + : String(localized: "\(visibleRules.count) matches within \(Int(searchRadiusMeters))m")) .font(.system(size: 12, weight: .semibold, design: .rounded)) .foregroundStyle(sweepMuted) } @@ -1487,15 +1489,15 @@ struct RulesSheet: View { private var crewStatusText: String { if isReparking { - return "\(carCrew.currentMemberName) is setting the new spot" + return String(localized: "\(carCrew.currentMemberName) is setting the new spot") } if let moveClaim { - return "\(moveClaim.memberName) claimed this move" + return String(localized: "\(moveClaim.memberName) claimed this move") } if let lastMoveActivity { - return "\(lastMoveActivity.memberName) moved it \(relativeMoveTime(lastMoveActivity.movedAt))" + return String(localized: "\(lastMoveActivity.memberName) moved it \(relativeMoveTime(lastMoveActivity.movedAt))") } - return "\(carCrew.members.count) people get alerts" + return String(localized: "\(carCrew.members.count) people get alerts") } private func sideSelector(_ sides: [String], activeSide: String?) -> some View { @@ -2009,14 +2011,15 @@ struct SettingsScreen: View { private var accountSubtitle: String { guard authentication.isAuthenticated else { - return "Use Auth0 to join crews and receive shared car updates." + return String(localized: "Use Auth0 to join crews and receive shared car updates.") } if authentication.convexSynced { - return "\(authentication.userName ?? authentication.userEmail ?? "Your account") is connected to \(carCrew.name)." + let accountName = authentication.userName ?? authentication.userEmail ?? String(localized: "Your account") + return String(localized: "\(accountName) is connected to \(carCrew.name).") } - return "Auth0 is connected. Waiting to sync with Convex." + return String(localized: "Auth0 is connected. Waiting to sync with Convex.") } private var notificationSection: some View { @@ -2231,7 +2234,7 @@ struct SettingsScreen: View { } } - private func settingsGroup(title: String, @ViewBuilder content: () -> Content) -> some View { + private func settingsGroup(title: LocalizedStringKey, @ViewBuilder content: () -> Content) -> some View { VStack(alignment: .leading, spacing: 12) { Text(title) .font(.system(size: 13, weight: .bold, design: .rounded)) @@ -2275,7 +2278,7 @@ struct SettingsScreen: View { do { try await authentication.updateNotificationPreferences(preferences) } catch { - authentication.errorMessage = "Notification settings sync failed: \(error.localizedDescription)" + authentication.errorMessage = String(localized: "Notification settings sync failed: \(error.localizedDescription)") } } } @@ -2668,9 +2671,9 @@ private func formatTimeWindow(fromHour: Int, toHour: Int) -> String { private func formatDistance(_ meters: CLLocationDistance) -> String { let feet = meters * 3.28084 if feet < 10 { - return "at pin" + return String(localized: "at pin") } - return "\(Int(feet.rounded())) ft" + return String(localized: "\(Int(feet.rounded())) ft") } private func ruleSummary(_ rule: SweepRule) -> String { @@ -2680,13 +2683,13 @@ private func ruleSummary(_ rule: SweepRule) -> String { private func formatSide(_ side: String) -> String { switch side { case "NorthEast": - return "Northeast" + return String(localized: "Northeast") case "NorthWest": - return "Northwest" + return String(localized: "Northwest") case "SouthEast": - return "Southeast" + return String(localized: "Southeast") case "SouthWest": - return "Southwest" + return String(localized: "Southwest") default: return side } @@ -2731,21 +2734,21 @@ private func formatReminderLeadTime(_ hours: Double) -> String { private func relativeMoveTime(_ date: Date, now: Date = Date()) -> String { let seconds = max(0, Int(now.timeIntervalSince(date))) if seconds < 60 { - return "just now" + return String(localized: "just now") } let minutes = seconds / 60 if minutes < 60 { - return "\(minutes)m ago" + return String(localized: "\(minutes)m ago") } let hours = minutes / 60 if hours < 24 { - return "\(hours)h ago" + return String(localized: "\(hours)h ago") } let days = hours / 24 - return "\(days)d ago" + return String(localized: "\(days)d ago") } private func formatHour(_ hour: Int) -> String { @@ -2773,12 +2776,14 @@ private func urgencyColor(_ hoursUntil: Double) -> Color { private func urgencyLabel(_ target: Date, from now: Date = Date()) -> String { let remainingHours = hoursUntil(target, from: now) - if remainingHours < 1 { return "Move your car NOW" } - if remainingHours < 24 { return "Street cleaning in \(Int(remainingHours.rounded()))h" } + if remainingHours < 1 { return String(localized: "Move your car NOW") } + if remainingHours < 24 { + return String(localized: "Street cleaning in \(Int(remainingHours.rounded()))h") + } let calendar = Calendar.current if calendar.isDateInTomorrow(target) { - return "Street cleaning tomorrow" + return String(localized: "Street cleaning tomorrow") } let today = calendar.startOfDay(for: now) @@ -2786,11 +2791,12 @@ private func urgencyLabel(_ target: Date, from now: Date = Date()) -> String { let dayDifference = calendar.dateComponents([.day], from: today, to: targetDay).day ?? 0 if dayDifference > 1 && dayDifference < 7 { let formatter = DateFormatter() + formatter.locale = Locale.current formatter.dateFormat = "EEEE" - return "Street cleaning \(formatter.string(from: target))" + return String(localized: "Street cleaning \(formatter.string(from: target))") } - return "Street cleaning \(formatDate(target))" + return String(localized: "Street cleaning \(formatDate(target))") } private func formatDate(_ date: Date) -> String { diff --git a/apps/ios-sweep-alert/SweepAlertSwift/ConvexService.swift b/apps/ios-sweep-alert/SweepAlertSwift/ConvexService.swift index 3ce7d77..b6534fc 100644 --- a/apps/ios-sweep-alert/SweepAlertSwift/ConvexService.swift +++ b/apps/ios-sweep-alert/SweepAlertSwift/ConvexService.swift @@ -48,9 +48,9 @@ struct ConvexService { var errorDescription: String? { switch self { case .invalidResponse: - return "Convex returned an invalid response." + return String(localized: "Convex returned an invalid response.") case let .httpStatus(status, message): - return "Convex request failed with HTTP \(status): \(message)" + return String(localized: "Convex request failed with HTTP \(status): \(message)") case let .functionError(message): return message } diff --git a/apps/ios-sweep-alert/SweepAlertSwift/Localizable.xcstrings b/apps/ios-sweep-alert/SweepAlertSwift/Localizable.xcstrings new file mode 100644 index 0000000..209c55a --- /dev/null +++ b/apps/ios-sweep-alert/SweepAlertSwift/Localizable.xcstrings @@ -0,0 +1,1176 @@ +{ + "sourceLanguage": "en", + "strings": { + "SweepAlert": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸਵੀਪਅਲਰਟ" + } + } + } + }, + "Create your car crew": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਆਪਣੀ ਕਾਰ ਕ੍ਰਿਊ ਬਣਾਓ" + } + } + } + }, + "Sign in to save cars, share invite links, and sync alerts.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰਾਂ ਸੇਵ ਕਰਨ, ਸੱਦਾ ਲਿੰਕ ਸਾਂਝੇ ਕਰਨ ਅਤੇ ਅਲਰਟ ਸਿੰਕ ਕਰਨ ਲਈ ਸਾਈਨ ਇਨ ਕਰੋ।" + } + } + } + }, + "Sign in": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸਾਈਨ ਇਨ" + } + } + } + }, + "Checking account": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਖਾਤਾ ਜਾਂਚਿਆ ਜਾ ਰਿਹਾ ਹੈ" + } + } + } + }, + "Open settings": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ" + } + } + } + }, + "You are here": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਤੁਸੀਂ ਇੱਥੇ ਹੋ" + } + } + } + }, + "San Francisco parking map": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੈਨ ਫਰਾਂਸਿਸਕੋ ਪਾਰਕਿੰਗ ਨਕਸ਼ਾ" + } + } + } + }, + "Sign in to create or join a car crew before setting a parking spot.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਾਰਕਿੰਗ ਸਪਾਟ ਸੈੱਟ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਕਾਰ ਕ੍ਰਿਊ ਬਣਾਉਣ ਜਾਂ ਸ਼ਾਮਿਲ ਹੋਣ ਲਈ ਸਾਈਨ ਇਨ ਕਰੋ।" + } + } + } + }, + "OK": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਠੀਕ ਹੈ" + } + } + } + }, + "Park Here": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਇੱਥੇ ਪਾਰਕ ਕਰੋ" + } + } + } + }, + "Parked here": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਇੱਥੇ ਪਾਰਕ ਕੀਤਾ" + } + } + } + }, + "Settings": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੈਟਿੰਗਾਂ" + } + } + } + }, + "Manage your crew, cars, shared alerts, and who can move them.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਆਪਣੀ ਕ੍ਰਿਊ, ਕਾਰਾਂ, ਸਾਂਝੇ ਅਲਰਟ ਅਤੇ ਉਹਨਾਂ ਨੂੰ ਮੈਨੇਜ ਕਰੋ ਜੋ ਉਹਨਾਂ ਨੂੰ ਮੋਵ ਕਰ ਸਕਦੇ ਹਨ।" + } + } + } + }, + "Account": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਖਾਤਾ" + } + } + } + }, + "Sign in to sync alerts": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਅਲਰਟ ਸਿੰਕ ਕਰਨ ਲਈ ਸਾਈਨ ਇਨ ਕਰੋ" + } + } + } + }, + "Use Auth0 to join crews and receive shared car updates.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕ੍ਰਿਊ ਵਿੱਚ ਸ਼ਾਮਿਲ ਹੋਣ ਅਤੇ ਸਾਂਝੇ ਕਾਰ ਅਪਡੇਟ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ Auth0 ਵਰਤੋ।" + } + } + } + }, + "Log in": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਲੌਗ ਇਨ" + } + } + } + }, + "Log out": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਲੌਗ ਆਉਟ" + } + } + } + }, + "Signed in with Auth0": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "Auth0 ਨਾਲ ਸਾਈਨ ਇਨ" + } + } + } + }, + "Crew": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕ੍ਰਿਊ" + } + } + } + }, + "Sign in to manage cars": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰਾਂ ਮੈਨੇਜ ਕਰਨ ਲਈ ਸਾਈਨ ਇਨ ਕਰੋ" + } + } + } + }, + "Crew names, invite links, cars, and push alerts are stored after login.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਲੌਗਇਨ ਤੋਂ ਬਾਅਦ ਕ੍ਰਿਊ ਨਾਮ, ਸੱਦਾ ਲਿੰਕ, ਕਾਰਾਂ ਅਤੇ ਪੁਸ਼ ਅਲਰਟ ਸਟੋਰ ਹੁੰਦੇ ਹਨ।" + } + } + } + }, + "Back": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਿੱਛੇ" + } + } + } + }, + "Share invite link": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੱਦਾ ਲਿੰਕ ਸਾਂਝਾ ਕਰੋ" + } + } + } + }, + "Tap the map to drop your parking pin": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਾਰਕਿੰਗ ਪਿੰਨ ਲਗਾਉਣ ਲਈ ਨਕਸ਼ੇ 'ਤੇ ਟੈਪ ਕਰੋ" + } + } + } + }, + "Expand parking details": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਾਰਕਿੰਗ ਵੇਰਵੇ ਵਿਸਤਾਰ ਕਰੋ" + } + } + } + }, + "Collapse parking details": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਾਰਕਿੰਗ ਵੇਰਵੇ ਸਮੇਟੋ" + } + } + } + }, + "Reading curb rules": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਰਬ ਨਿਯਮ ਪੜ੍ਹੇ ਜਾ ਰਹੇ ਹਨ" + } + } + } + }, + "Checking nearby SF sweeping schedules...": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਨੇੜਲੇ SF ਸਫਾਈ ਸਮਾਂ-ਸਾਰਣੀਆਂ ਦੀ ਜਾਂਚ..." + } + } + } + }, + "No street cleaning found": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕੋਈ ਗਲੀ ਸਫਾਈ ਨਹੀਂ ਮਿਲੀ" + } + } + } + }, + "No sweeping schedule found near this spot. Double-check the pin is on the street.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਇਸ ਸਥਾਨ ਦੇ ਨੇੜੇ ਕੋਈ ਸਫਾਈ ਸਮਾਂ-ਸਾਰਣੀ ਨਹੀਂ ਮਿਲੀ। ਜਾਂਚ ਕਰੋ ਕਿ ਪਿੰਨ ਗਲੀ 'ਤੇ ਹੈ।" + } + } + } + }, + "Clear Pin": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਿੰਨ ਹਟਾਓ" + } + } + } + }, + "Nearby rules": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਨੇੜਲੇ ਨਿਯਮ" + } + } + } + }, + "%lld match within %lldm": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਮਿਲਾਨ %lldm ਦੇ ਅੰਦਰ" + } + } + } + }, + "%lld matches within %lldm": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਮਿਲਾਨ %lldm ਦੇ ਅੰਦਰ" + } + } + } + }, + "%lld car": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਕਾਰ" + } + } + } + }, + "%lld cars": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਕਾਰਾਂ" + } + } + } + }, + "Where is it parked now?": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਹੁਣ ਇਹ ਕਿੱਥੇ ਪਾਰਕ ਹੈ?" + } + } + } + }, + "Tap the map to place a new pin, or use your current location.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਨਵਾਂ ਪਿੰਨ ਲਗਾਉਣ ਲਈ ਨਕਸ਼ੇ 'ਤੇ ਟੈਪ ਕਰੋ, ਜਾਂ ਆਪਣਾ ਮੌਜੂਦਾ ਸਥਾਨ ਵਰਤੋ।" + } + } + } + }, + "Use current": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਮੌਜੂਦਾ ਵਰਤੋ" + } + } + } + }, + "Cancel": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਰੱਦ ਕਰੋ" + } + } + } + }, + "Choose spot": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸਥਾਨ ਚੁਣੋ" + } + } + } + }, + "Street side": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਗਲੀ ਪਾਸਾ" + } + } + } + }, + "Tap the map or use current location": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਨਕਸ਼ੇ 'ਤੇ ਟੈਪ ਕਰੋ ਜਾਂ ਮੌਜੂਦਾ ਸਥਾਨ ਵਰਤੋ" + } + } + } + }, + "Use current location": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਮੌਜੂਦਾ ਸਥਾਨ ਵਰਤੋ" + } + } + } + }, + "Cancel move": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਮੋਵ ਰੱਦ ਕਰੋ" + } + } + } + }, + "Add a car in settings to start tracking parking spots.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਾਰਕਿੰਗ ਸਪਾਟ ਟ੍ਰੈਕ ਕਰਨ ਲਈ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਕਾਰ ਜੋੜੋ।" + } + } + } + }, + "Add a car": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰ ਜੋੜੋ" + } + } + } + }, + "%@ parking marker": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਪਾਰਕਿੰਗ ਮਾਰਕਰ" + } + } + } + }, + "Notifications": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੂਚਨਾਵਾਂ" + } + } + } + }, + "Street cleaning alerts": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਗਲੀ ਸਫਾਈ ਅਲਰਟ" + } + } + } + }, + "Crew claim updates": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕ੍ਰਿਊ ਕਲੇਮ ਅਪਡੇਟ" + } + } + } + }, + "First reminder": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਹਿਲੀ ਯਾਦ-ਦਹਾਨੀ" + } + } + } + }, + "Group name": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਗਰੁੱਪ ਨਾਮ" + } + } + } + }, + "Crew name": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕ੍ਰਿਊ ਨਾਮ" + } + } + } + }, + "Save crew name": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕ੍ਰਿਊ ਨਾਮ ਸੇਵ ਕਰੋ" + } + } + } + }, + "Cars": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰਾਂ" + } + } + } + }, + "Save %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਸੇਵ ਕਰੋ" + } + } + } + }, + "Members": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਮੈਂਬਰ" + } + } + } + }, + "%lld members can receive alerts": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਮੈਂਬਰ ਅਲਰਟ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹਨ" + } + } + } + }, + "Invite": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੱਦਾ" + } + } + } + }, + "Invite to %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਨੂੰ ਸੱਦਾ" + } + } + } + }, + "Anyone with the app can join this crew and help manage every car.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਐਪ ਵਾਲਾ ਕੋਈ ਵੀ ਇਸ ਕ੍ਰਿਊ ਵਿੱਚ ਸ਼ਾਮਿਲ ਹੋ ਸਕਦਾ ਹੈ ਅਤੇ ਹਰ ਕਾਰ ਮੈਨੇਜ ਕਰਨ ਵਿੱਚ ਮਦਦ ਕਰ ਸਕਦਾ ਹੈ।" + } + } + } + }, + "Add a car before setting a parking spot.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਾਰਕਿੰਗ ਸਪਾਟ ਸੈੱਟ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਕਾਰ ਜੋੜੋ।" + } + } + } + }, + "Sign in before creating a car crew invite link.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰ ਕ੍ਰਿਊ ਸੱਦਾ ਲਿੰਕ ਬਣਾਉਣ ਤੋਂ ਪਹਿਲਾਂ ਸਾਈਨ ਇਨ ਕਰੋ।" + } + } + } + }, + "Could not create invite: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੱਦਾ ਨਹੀਂ ਬਣਾਇਆ ਜਾ ਸਕਿਆ: %@" + } + } + } + }, + "Sign in to accept this car crew invite.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰ ਕ੍ਰਿਊ ਸੱਦਾ ਸਵੀਕਾਰ ਕਰਨ ਲਈ ਸਾਈਨ ਇਨ ਕਰੋ।" + } + } + } + }, + "Joined %@.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਵਿੱਚ ਸ਼ਾਮਿਲ ਹੋਏ।" + } + } + } + }, + "Could not accept invite: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੱਦਾ ਸਵੀਕਾਰ ਨਹੀਂ ਹੋ ਸਕਿਆ: %@" + } + } + } + }, + "Sign in before renaming your crew.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕ੍ਰਿਊ ਦਾ ਨਾਮ ਬਦਲਣ ਤੋਂ ਪਹਿਲਾਂ ਸਾਈਨ ਇਨ ਕਰੋ।" + } + } + } + }, + "Could not rename crew: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕ੍ਰਿਊ ਦਾ ਨਾਮ ਨਹੀਂ ਬਦਲਿਆ ਜਾ ਸਕਿਆ: %@" + } + } + } + }, + "Sign in before adding another car.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਹੋਰ ਕਾਰ ਜੋੜਨ ਤੋਂ ਪਹਿਲਾਂ ਸਾਈਨ ਇਨ ਕਰੋ।" + } + } + } + }, + "Could not add car: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰ ਨਹੀਂ ਜੋੜੀ ਜਾ ਸਕੀ: %@" + } + } + } + }, + "Sign in before renaming cars.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰਾਂ ਦਾ ਨਾਮ ਬਦਲਣ ਤੋਂ ਪਹਿਲਾਂ ਸਾਈਨ ਇਨ ਕਰੋ।" + } + } + } + }, + "Could not rename car: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰ ਦਾ ਨਾਮ ਨਹੀਂ ਬਦਲਿਆ ਜਾ ਸਕਿਆ: %@" + } + } + } + }, + "Could not store Auth0 credentials.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "Auth0 ਕ੍ਰੈਡੈਂਸ਼ਲ ਸਟੋਰ ਨਹੀਂ ਹੋ ਸਕੇ।" + } + } + } + }, + "Login failed: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਲੌਗਇਨ ਅਸਫਲ: %@" + } + } + } + }, + "Logout session clear failed: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਲੌਗਆਉਟ ਸੈਸ਼ਨ ਕਲੀਅਰ ਅਸਫਲ: %@" + } + } + } + }, + "Convex sync failed: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "Convex ਸਿੰਕ ਅਸਫਲ: %@" + } + } + } + }, + "Push registration failed: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪੁਸ਼ ਰਜਿਸਟ੍ਰੇਸ਼ਨ ਅਸਫਲ: %@" + } + } + } + }, + "Move claim sync failed: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਮੋਵ ਕਲੇਮ ਸਿੰਕ ਅਸਫਲ: %@" + } + } + } + }, + "Crew sync failed: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕ੍ਰਿਊ ਸਿੰਕ ਅਸਫਲ: %@" + } + } + } + }, + "Parking sync failed: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਾਰਕਿੰਗ ਸਿੰਕ ਅਸਫਲ: %@" + } + } + } + }, + "Notification settings sync failed: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੂਚਨਾ ਸੈਟਿੰਗਾਂ ਸਿੰਕ ਅਸਫਲ: %@" + } + } + } + }, + "Convex returned an invalid response.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "Convex ਨੇ ਅਵੈਧ ਜਵਾਬ ਦਿੱਤਾ।" + } + } + } + }, + "Convex request failed with HTTP %lld: %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "Convex ਬੇਨਤੀ HTTP %lld ਨਾਲ ਅਸਫਲ: %@" + } + } + } + }, + "Notification permission was not granted.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਸੂਚਨਾ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ।" + } + } + } + }, + "%@ is setting the new spot": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਨਵਾਂ ਸਪਾਟ ਸੈੱਟ ਕਰ ਰਿਹਾ/ਰਹੀ ਹੈ" + } + } + } + }, + "%@ claimed this move": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਨੇ ਇਹ ਮੋਵ ਕਲੇਮ ਕੀਤਾ" + } + } + } + }, + "%@ moved it %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਨੇ ਇਸਨੂੰ %@ ਮੋਵ ਕੀਤਾ" + } + } + } + }, + "%lld people get alerts": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਲੋਕਾਂ ਨੂੰ ਅਲਰਟ ਮਿਲਦੇ ਹਨ" + } + } + } + }, + "%@ is moving it": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਇਸਨੂੰ ਮੋਵ ਕਰ ਰਿਹਾ/ਰਹੀ ਹੈ" + } + } + } + }, + "Set %@'s new spot": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਦਾ ਨਵਾਂ ਸਪਾਟ ਸੈੱਟ ਕਰੋ" + } + } + } + }, + "Your account": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਤੁਹਾਡਾ ਖਾਤਾ" + } + } + } + }, + "%@ is connected to %@.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ %@ ਨਾਲ ਜੁੜਿਆ ਹੈ।" + } + } + } + }, + "Auth0 is connected. Waiting to sync with Convex.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "Auth0 ਜੁੜਿਆ ਹੈ। Convex ਨਾਲ ਸਿੰਕ ਦੀ ਉਡੀਕ।" + } + } + } + }, + "at pin": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਪਿੰਨ 'ਤੇ" + } + } + } + }, + "%lld ft": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਫੁੱਟ" + } + } + } + }, + "Northeast": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਉੱਤਰ-ਪੂਰਬ" + } + } + } + }, + "Northwest": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਉੱਤਰ-ਪੱਛਮ" + } + } + } + }, + "Southeast": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਦੱਖਣ-ਪੂਰਬ" + } + } + } + }, + "Southwest": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਦੱਖਣ-ਪੱਛਮ" + } + } + } + }, + "just now": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਹੁਣੇ" + } + } + } + }, + "%lldm ago": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਮਿੰਟ ਪਹਿਲਾਂ" + } + } + } + }, + "%lldh ago": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਘੰਟੇ ਪਹਿਲਾਂ" + } + } + } + }, + "%lldd ago": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%lld ਦਿਨ ਪਹਿਲਾਂ" + } + } + } + }, + "Move your car NOW": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਆਪਣੀ ਕਾਰ ਹੁਣੇ ਮੋਵ ਕਰੋ" + } + } + } + }, + "Street cleaning in %lldh": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਗਲੀ ਸਫਾਈ %lld ਘੰਟਿਆਂ ਵਿੱਚ" + } + } + } + }, + "Street cleaning tomorrow": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕੱਲ੍ਹ ਗਲੀ ਸਫਾਈ" + } + } + } + }, + "Street cleaning %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਗਲੀ ਸਫਾਈ %@" + } + } + } + }, + "See the next street cleaning window for your shared car.": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਆਪਣੀ ਸਾਂਝੀ ਕਾਰ ਲਈ ਅਗਲੀ ਗਲੀ ਸਫਾਈ ਵਿੰਡੋ ਦੇਖੋ।" + } + } + } + }, + "%@ side - %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ ਪਾਸਾ - %@" + } + } + } + }, + "%@ - %@ side - %@": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "%@ - %@ ਪਾਸਾ - %@" + } + } + } + }, + "Auto-selected": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਆਟੋ-ਚੁਣਿਆ" + } + } + } + }, + "Changed": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਬਦਲਿਆ" + } + } + } + }, + "Add car": { + "localizations": { + "pa": { + "stringUnit": { + "state": "translated", + "value": "ਕਾਰ ਜੋੜੋ" + } + } + } + } + }, + "version": "1.0" +} diff --git a/apps/ios-sweep-alert/SweepAlertSwift/PushNotificationService.swift b/apps/ios-sweep-alert/SweepAlertSwift/PushNotificationService.swift index 7d0948d..4c18b35 100644 --- a/apps/ios-sweep-alert/SweepAlertSwift/PushNotificationService.swift +++ b/apps/ios-sweep-alert/SweepAlertSwift/PushNotificationService.swift @@ -9,7 +9,7 @@ enum PushNotificationError: LocalizedError { var errorDescription: String? { switch self { case .permissionDenied: - return "Notification permission was not granted." + return String(localized: "Notification permission was not granted.") case let .registrationFailed(message): return message } diff --git a/apps/ios-sweep-alert/SweepAlertSwift/pa.lproj/InfoPlist.strings b/apps/ios-sweep-alert/SweepAlertSwift/pa.lproj/InfoPlist.strings new file mode 100644 index 0000000..cac54d3 --- /dev/null +++ b/apps/ios-sweep-alert/SweepAlertSwift/pa.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +"CFBundleDisplayName" = "ਸਵੀਪਅਲਰਟ"; +"CFBundleName" = "ਸਵੀਪਅਲਰਟ"; +"NSLocationWhenInUseUsageDescription" = "ਸਵੀਪਅਲਰਟ ਨੇੜਲੇ ਪਾਰਕਿੰਗ ਨਿਯਮ ਦਿਖਾਉਣ ਲਈ ਤੁਹਾਡੇ ਸਥਾਨ ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ।"; diff --git a/apps/ios-sweep-alert/SweepAlertWidget/pa.lproj/InfoPlist.strings b/apps/ios-sweep-alert/SweepAlertWidget/pa.lproj/InfoPlist.strings new file mode 100644 index 0000000..867f107 --- /dev/null +++ b/apps/ios-sweep-alert/SweepAlertWidget/pa.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +"CFBundleDisplayName" = "ਸਵੀਪਅਲਰਟ ਵਿਜੇਟ"; +"CFBundleName" = "ਸਵੀਪਅਲਰਟ ਵਿਜੇਟ"; From 5176e6a6f087283e1f8d05e7967dad4c4f4fd9fe Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 28 Jun 2026 08:09:32 +0000 Subject: [PATCH 2/3] Add Revyl project config for remote iOS builds Configure app_id, Xcode scheme, and build output path so cloud agents can run revyl dev without interactive app selection. Co-authored-by: Anam Hira --- apps/ios-sweep-alert/.revyl/.gitignore | 11 +++++++++++ apps/ios-sweep-alert/.revyl/config.yaml | 15 +++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 apps/ios-sweep-alert/.revyl/.gitignore create mode 100644 apps/ios-sweep-alert/.revyl/config.yaml diff --git a/apps/ios-sweep-alert/.revyl/.gitignore b/apps/ios-sweep-alert/.revyl/.gitignore new file mode 100644 index 0000000..bf3b832 --- /dev/null +++ b/apps/ios-sweep-alert/.revyl/.gitignore @@ -0,0 +1,11 @@ +# Most files in .revyl are local runtime state. +# Only the shared project config and test definitions belong in git. +* + +# Keep this file itself. +!.gitignore + +# Shared Revyl project files. +!config.yaml +!tests/ +!tests/** diff --git a/apps/ios-sweep-alert/.revyl/config.yaml b/apps/ios-sweep-alert/.revyl/config.yaml new file mode 100644 index 0000000..e35540a --- /dev/null +++ b/apps/ios-sweep-alert/.revyl/config.yaml @@ -0,0 +1,15 @@ +project: + name: sweep-alert-ios + +build: + system: Xcode + platforms: + ios: + app_id: ee303738-1c98-4c7b-b471-ed70d8ea5245 + command: xcodebuild -project SweepAlertSwift.xcodeproj -scheme SweepAlertSwift -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build + output: build/Build/Products/Debug-iphonesimulator/SweepAlertSwift.app + scheme: SweepAlertSwift + +defaults: + open_browser: false + timeout: 1800 From 5316df1f5c4954d7642a8de96d8c4412cbb173e2 Mon Sep 17 00:00:00 2001 From: Kyle Rich Date: Sun, 23 Aug 2026 23:03:09 -0700 Subject: [PATCH 3/3] test: refresh SweepAlert accent color --- apps/ios-sweep-alert/SweepAlertSwift/ContentView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ios-sweep-alert/SweepAlertSwift/ContentView.swift b/apps/ios-sweep-alert/SweepAlertSwift/ContentView.swift index 112d483..52bda47 100644 --- a/apps/ios-sweep-alert/SweepAlertSwift/ContentView.swift +++ b/apps/ios-sweep-alert/SweepAlertSwift/ContentView.swift @@ -192,7 +192,7 @@ final class LocationStore: NSObject, ObservableObject, CLLocationManagerDelegate private let sfCoordinate = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194) private let searchRadiusMeters: CLLocationDistance = 60 -private let sweepBlue = Color(red: 0.05, green: 0.39, blue: 0.90) +private let sweepBlue = Color(red: 0.12, green: 0.48, blue: 0.96) private let sweepInk = Color(red: 0.08, green: 0.10, blue: 0.14) private let sweepMuted = Color(red: 0.38, green: 0.43, blue: 0.50)