From 16325a47a91c13609db097a16ef8243d989583d6 Mon Sep 17 00:00:00 2001 From: Damien Glancy Date: Sat, 20 Jun 2026 13:46:37 +0100 Subject: [PATCH] #304 refining on-boarding --- Driveline/AppLifecycle/Localizable.xcstrings | 2476 +++++++++++++---- Driveline/UI/Home/HomePresenter.swift | 4 +- Driveline/UI/Home/HomeView.swift | 4 + .../OnboardingAutomationDetailView.swift | 28 +- .../UI/Onboarding/OnboardingPresenter.swift | 14 +- Driveline/UI/Tips/RecordButtonTip.swift | 4 + .../UITests/EditDriveTipTests.swift | 7 + .../UITests/RecordButtonTipTests.swift | 33 +- .../UITests/StatsPanelTipTests.swift | 7 + DrivelineUITests/TipUITests.swift | 41 +- 10 files changed, 2045 insertions(+), 573 deletions(-) diff --git a/Driveline/AppLifecycle/Localizable.xcstrings b/Driveline/AppLifecycle/Localizable.xcstrings index c42fe4d..f725bdb 100644 --- a/Driveline/AppLifecycle/Localizable.xcstrings +++ b/Driveline/AppLifecycle/Localizable.xcstrings @@ -471,7 +471,33 @@ } }, "A Shortcut fires the moment you join CarPlay or your car's Bluetooth." : { - "comment" : "Onboarding welcome feature row 2 body" + "comment" : "Onboarding welcome feature row 2 body", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ein Kurzbefehl wird ausgelöst, sobald du dich mit CarPlay oder dem Bluetooth deines Autos verbindest." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "A Shortcut fires the moment you join CarPlay or your car's Bluetooth." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un raccourci se déclenche dès que vous vous connectez à CarPlay ou au Bluetooth de votre voiture." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Een opdracht wordt geactiveerd zodra je verbinding maakt met CarPlay of de bluetooth van je auto." + } + } + } }, "active" : { "comment" : "Label for the active driving duration stat", @@ -590,13 +616,91 @@ } }, "Allow location “Always”" : { - "comment" : "Onboarding Always-location primer title" + "comment" : "Onboarding Always-location primer title", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standort „Immer“ erlauben" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allow location “Always”" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autoriser la localisation « Toujours »" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie “Altijd” toestaan" + } + } + } }, "Allow Location Access" : { - "comment" : "Onboarding location primer button — not yet granted" + "comment" : "Onboarding location primer button — not yet granted", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standortzugriff erlauben" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allow Location Access" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autoriser l'accès à la localisation" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatietoegang toestaan" + } + } + } }, "Always Allow enabled" : { - "comment" : "Onboarding confirmation that Always location access is granted" + "comment" : "Onboarding confirmation that Always location access is granted", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "„Immer erlauben“ aktiviert" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Allow enabled" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "« Toujours autoriser » activé" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "“Altijd toestaan” ingeschakeld" + } + } + } }, "Arrival" : { "comment" : "Endpoint row subtitle", @@ -686,7 +790,33 @@ } }, "Automatic drive tracking — connect, drive, done." : { - "comment" : "Onboarding welcome screen subtitle" + "comment" : "Onboarding welcome screen subtitle", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatische Fahrtaufzeichnung — verbinden, fahren, fertig." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatic drive tracking — connect, drive, done." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suivi automatique des trajets — connectez, conduisez, c'est fait." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatische ritregistratie — verbinden, rijden, klaar." + } + } + } }, "Automatically" : { "comment" : "Recording trigger: started automatically by automation", @@ -776,7 +906,33 @@ } }, "Background location activates around your trips, not all day." : { - "comment" : "Onboarding Always-location info row 2 body" + "comment" : "Onboarding Always-location info row 2 body", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Hintergrundstandort wird rund um deine Fahrten aktiviert, nicht den ganzen Tag." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Background location activates around your trips, not all day." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La localisation en arrière-plan s'active autour de vos trajets, pas toute la journée." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie op de achtergrond is actief rond je ritten, niet de hele dag." + } + } + } }, "Cancel" : { "comment" : "Cancel or dismiss", @@ -836,12 +992,6 @@ } } }, - "CarPlay connects" : { - "comment" : "Onboarding automations intro row 1 title" - }, - "CarPlay disconnects" : { - "comment" : "Onboarding automations intro row 2 title" - }, "Category" : { "comment" : "Metadata row", "localizations" : { @@ -871,12 +1021,6 @@ } } }, - "Choose **CarPlay** → **Connects**" : { - "comment" : "Onboarding automation start step 4" - }, - "Choose **CarPlay** → **Disconnects**" : { - "comment" : "Onboarding automation finish step 4" - }, "Clear text" : { "comment" : "Accessibility label for button that clears a text field", "localizations" : { @@ -907,7 +1051,33 @@ } }, "Close" : { - "comment" : "Close button to dismiss the automation setup flow\nClose button to dismiss the location permission flow" + "comment" : "Close button to dismiss the automation setup flow\nClose button to dismiss the location permission flow", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schließen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Close" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sluiten" + } + } + } }, "Combined Result" : { "comment" : "Merge combined result section header", @@ -939,7 +1109,33 @@ } }, "Continue" : { - "comment" : "Onboarding continue button — shown after permission is granted" + "comment" : "Onboarding continue button — shown after permission is granted", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fortfahren" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continue" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continuer" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doorgaan" + } + } + } }, "Couldn't Share Drive" : { "comment" : "Export failure alert title", @@ -1145,7 +1341,33 @@ } }, "Done" : { - "comment" : "Final button on automation setup flow" + "comment" : "Final button on automation setup flow", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fertig" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Done" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminé" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gereed" + } + } + } }, "Double tap to switch between last 30 days and all time" : { "comment" : "Accessibility hint for toggling stats panel scope", @@ -1324,168 +1546,298 @@ } }, "Driveline needs your location" : { - "comment" : "Onboarding location primer title" - }, - "Driveline starts recording your drive." : { - "comment" : "Onboarding automations intro row 1 body" - }, - "Driveline stops and saves your drive." : { - "comment" : "Onboarding automations intro row 2 body" - }, - "Driveline uses your location to record the route, distance, and duration of each drive. Without it, trips can't be tracked." : { - "comment" : "Onboarding location primer body paragraph 1" - }, - "Drives" : { - "comment" : "A title for the list of drives.", - "isCommentAutoGenerated" : true, + "comment" : "Onboarding location primer title", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrten" + "value" : "Driveline benötigt deinen Standort" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Drives" + "value" : "Driveline needs your location" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Trajets" + "value" : "Driveline a besoin de votre position" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ritten" + "value" : "Driveline heeft je locatie nodig" } } } }, - "DRIVES" : { - "comment" : "Stats panel drives card label", + "Driveline starts recording your drive." : { + "comment" : "Onboarding automations intro row 1 body", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "FAHRTEN" + "value" : "Driveline beginnt mit der Aufzeichnung deiner Fahrt." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "DRIVES" + "value" : "Driveline starts recording your drive." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "TRAJETS" + "value" : "Driveline commence à enregistrer votre trajet." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "RITTEN" + "value" : "Driveline begint je rit op te nemen." } } } }, - "Drives capture without opening the app." : { - "comment" : "Onboarding Always-location info row 1 body" - }, - "Duration" : { - "comment" : "Stat tile label", + "Driveline stops and saves your drive." : { + "comment" : "Onboarding automations intro row 2 body", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dauer" + "value" : "Driveline stoppt und speichert deine Fahrt." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Duration" + "value" : "Driveline stops and saves your drive." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Durée" + "value" : "Driveline arrête et enregistre votre trajet." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Duur" + "value" : "Driveline stopt en bewaart je rit." } } } }, - "Edit Drive" : { - "comment" : "Edit drive sheet title", + "Driveline uses your location to record the route, distance, and duration of each drive. Without it, trips can't be tracked." : { + "comment" : "Onboarding location primer body paragraph 1", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrt bearbeiten" + "value" : "Driveline verwendet deinen Standort, um Route, Distanz und Dauer jeder Fahrt aufzuzeichnen. Ohne ihn können Fahrten nicht erfasst werden." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Edit Drive" + "value" : "Driveline uses your location to record the route, distance, and duration of each drive. Without it, trips can't be tracked." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Modifier le trajet" + "value" : "Driveline utilise votre position pour enregistrer l'itinéraire, la distance et la durée de chaque trajet. Sans elle, les trajets ne peuvent pas être suivis." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Rit bewerken" + "value" : "Driveline gebruikt je locatie om de route, afstand en duur van elke rit vast te leggen. Zonder locatie kunnen ritten niet worden geregistreerd." } } } }, - "Edit Drive Details" : { - "comment" : "More menu action", + "Drives" : { + "comment" : "A title for the list of drives.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrtendetails bearbeiten" + "value" : "Fahrten" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Edit Drive Details" + "value" : "Drives" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Modifier les détails du trajet" + "value" : "Trajets" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ritdetails bewerken" + "value" : "Ritten" } } } }, - "Edit Your Drive" : { - "comment" : "TipKit tip title for the drive detail options button", + "DRIVES" : { + "comment" : "Stats panel drives card label", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "FAHRTEN" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "DRIVES" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "TRAJETS" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "RITTEN" + } + } + } + }, + "Drives capture without opening the app." : { + "comment" : "Onboarding Always-location info row 1 body", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrten werden erfasst, ohne die App zu öffnen." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Drives capture without opening the app." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les trajets sont enregistrés sans ouvrir l'app." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ritten worden vastgelegd zonder de app te openen." + } + } + } + }, + "Duration" : { + "comment" : "Stat tile label", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dauer" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Duration" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durée" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Duur" + } + } + } + }, + "Edit Drive" : { + "comment" : "Edit drive sheet title", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrt bearbeiten" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit Drive" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modifier le trajet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rit bewerken" + } + } + } + }, + "Edit Drive Details" : { + "comment" : "More menu action", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrtendetails bearbeiten" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit Drive Details" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modifier les détails du trajet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ritdetails bewerken" + } + } + } + }, + "Edit Your Drive" : { + "comment" : "TipKit tip title for the drive detail options button", "localizations" : { "de" : { "stringUnit" : { @@ -1572,7 +1924,33 @@ } }, "Enable Background Location" : { - "comment" : "Onboarding Always-location primer button — not yet granted" + "comment" : "Onboarding Always-location primer button — not yet granted", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hintergrundstandort aktivieren" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable Background Location" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer la localisation en arrière-plan" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locatie op de achtergrond inschakelen" + } + } + } }, "End Location" : { "comment" : "Edit drive section header", @@ -2072,9 +2450,6 @@ } } }, - "For truly hands-free recording, set up two automations in the Shortcuts app. They start and stop Driveline the moment you connect or disconnect from CarPlay." : { - "comment" : "Onboarding automations intro screen body" - }, "Full screen map" : { "comment" : "Accessibility label for the button that opens the full screen map on the drive detail screen", "localizations" : { @@ -2105,283 +2480,413 @@ } }, "Get Started" : { - "comment" : "Onboarding welcome primary button" - }, - "Hands-free recording" : { - "comment" : "Onboarding Always-location info row 1 title" - }, - "last 30 days" : { - "comment" : "Stats scope label for recent period", + "comment" : "Onboarding welcome primary button", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "letzte 30 Tage" + "value" : "Loslegen" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "last 30 days" + "value" : "Get Started" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "30 derniers jours" + "value" : "Commencer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "laatste 30 dagen" + "value" : "Aan de slag" } } } }, - "Location access granted" : { - "comment" : "Onboarding confirmation that When In Use location access is granted" - }, - "Location access was denied. Open Settings and choose Always to enable background recording." : { - "comment" : "Explanation shown on the Always location screen when access is denied" - }, - "Location access was denied. Open Settings to enable it." : { - "comment" : "Explanation shown on location permission screen when access is denied" - }, - "logged" : { - "comment" : "Label for the count of GPS positions logged during a drive", + "Hands-free recording" : { + "comment" : "Onboarding Always-location info row 1 title", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "protokolliert" + "value" : "Freihändige Aufzeichnung" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "logged" + "value" : "Hands-free recording" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "enregistré(s)" + "value" : "Enregistrement mains libres" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "geregistreerd" + "value" : "Handsfree opnemen" } } } }, - "Manually" : { - "comment" : "Recording trigger: started manually by the user", + "last 30 days" : { + "comment" : "Stats scope label for recent period", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell" + "value" : "letzte 30 Tage" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Manually" + "value" : "last 30 days" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Manuellement" + "value" : "30 derniers jours" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Handmatig" + "value" : "laatste 30 dagen" } } } }, - "Merge" : { - "comment" : "Confirm drive merge\nMerge selected drives button", + "Location access granted" : { + "comment" : "Onboarding confirmation that When In Use location access is granted", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zusammenführen" + "value" : "Standortzugriff erteilt" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Merge" + "value" : "Location access granted" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fusionner" + "value" : "Accès à la localisation accordé" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Samenvoegen" + "value" : "Locatietoegang verleend" } } } }, - "Merge Drives" : { - "comment" : "Merge sheet navigation title", + "Location access was denied. Open Settings and choose Always to enable background recording." : { + "comment" : "Explanation shown on the Always location screen when access is denied", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrten zusammenführen" + "value" : "Der Standortzugriff wurde verweigert. Öffne die Einstellungen und wähle „Immer“, um die Aufzeichnung im Hintergrund zu aktivieren." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Merge Drives" + "value" : "Location access was denied. Open Settings and choose Always to enable background recording." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fusionner les trajets" + "value" : "L'accès à la localisation a été refusé. Ouvrez Réglages et choisissez Toujours pour activer l'enregistrement en arrière-plan." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ritten samenvoegen" + "value" : "Locatietoegang is geweigerd. Open Instellingen en kies Altijd om opnemen op de achtergrond in te schakelen." } } } }, - "Merged Drive Name" : { - "comment" : "Merge name section header", + "Location access was denied. Open Settings to enable it." : { + "comment" : "Explanation shown on location permission screen when access is denied", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Name der zusammengeführten Fahrt" + "value" : "Der Standortzugriff wurde verweigert. Öffne die Einstellungen, um ihn zu aktivieren." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Merged Drive Name" + "value" : "Location access was denied. Open Settings to enable it." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nom du trajet fusionné" + "value" : "L'accès à la localisation a été refusé. Ouvrez Réglages pour l'activer." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Naam van de samengevoegde rit" + "value" : "Locatietoegang is geweigerd. Open Instellingen om dit in te schakelen." } } } }, - "Merging drives" : { - "comment" : "Accessibility label for the merge progress indicator", + "logged" : { + "comment" : "Label for the count of GPS positions logged during a drive", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrten werden zusammengeführt" + "value" : "protokolliert" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Merging drives" + "value" : "logged" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fusion des trajets" + "value" : "enregistré(s)" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ritten samenvoegen" + "value" : "geregistreerd" } } } }, - "Merging…" : { - "comment" : "Label shown while two drives are being merged", + "Manually" : { + "comment" : "Recording trigger: started manually by the user", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wird zusammengeführt…" + "value" : "Manuell" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Merging…" + "value" : "Manually" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fusion…" + "value" : "Manuellement" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Samenvoegen…" + "value" : "Handmatig" } } } }, - "Mixed" : { - "comment" : "Drive category: a mix of driving types", + "Merge" : { + "comment" : "Confirm drive merge\nMerge selected drives button", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gemischt" + "value" : "Zusammenführen" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Mixed" + "value" : "Merge" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mixte" + "value" : "Fusionner" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gemengd" + "value" : "Samenvoegen" } } } }, - "More options" : { - "comment" : "Accessibility label for the more options button on the drive detail screen\nEllipsis menu accessibility label", + "Merge Drives" : { + "comment" : "Merge sheet navigation title", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrten zusammenführen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Merge Drives" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fusionner les trajets" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ritten samenvoegen" + } + } + } + }, + "Merged Drive Name" : { + "comment" : "Merge name section header", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name der zusammengeführten Fahrt" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Merged Drive Name" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom du trajet fusionné" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naam van de samengevoegde rit" + } + } + } + }, + "Merging drives" : { + "comment" : "Accessibility label for the merge progress indicator", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrten werden zusammengeführt" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Merging drives" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fusion des trajets" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ritten samenvoegen" + } + } + } + }, + "Merging…" : { + "comment" : "Label shown while two drives are being merged", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wird zusammengeführt…" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Merging…" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fusion…" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Samenvoegen…" + } + } + } + }, + "Mixed" : { + "comment" : "Drive category: a mix of driving types", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gemischt" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mixed" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mixte" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gemengd" + } + } + } + }, + "More options" : { + "comment" : "Accessibility label for the more options button on the drive detail screen\nEllipsis menu accessibility label", "localizations" : { "de" : { "stringUnit" : { @@ -2467,9 +2972,6 @@ } } }, - "New Drive" : { - "comment" : "Button on empty state to start a new drive" - }, "Night" : { "comment" : "Time-of-day word in drive name", "localizations" : { @@ -2529,7 +3031,33 @@ } }, "No buttons to remember. Driveline logs each trip from start to finish." : { - "comment" : "Onboarding welcome feature row 1 body" + "comment" : "Onboarding welcome feature row 1 body", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Knöpfe, an die du denken musst. Driveline zeichnet jede Fahrt von Anfang bis Ende auf." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "No buttons to remember. Driveline logs each trip from start to finish." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun bouton à retenir. Driveline enregistre chaque trajet du début à la fin." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen knoppen om te onthouden. Driveline legt elke rit van begin tot eind vast." + } + } + } }, "No Drives" : { "comment" : "A title shown when the user has not yet recorded any drives.", @@ -2621,84 +3149,266 @@ }, "On the next screen, choose **Allow While Using App** to continue." : { "comment" : "A description of the location access request.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wähle auf dem nächsten Bildschirm **Beim Verwenden der App erlauben**, um fortzufahren." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "On the next screen, choose **Allow While Using App** to continue." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sur l'écran suivant, choisissez **Autoriser lorsque l'app est active** pour continuer." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kies op het volgende scherm **Sta toe tijdens gebruik van app** om door te gaan." + } + } + } }, "On the next screen, choose **Change to Always Allow**. This is what lets a trip record start the moment you get in the car." : { "comment" : "A description of the benefits of allowing background location.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wähle auf dem nächsten Bildschirm **Auf „Immer erlauben“ ändern**. Damit kann eine Fahrt aufgezeichnet werden, sobald du ins Auto steigst." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "On the next screen, choose **Change to Always Allow**. This is what lets a trip record start the moment you get in the car." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sur l'écran suivant, choisissez **Toujours autoriser**. C'est ce qui permet à un enregistrement de démarrer dès que vous montez dans la voiture." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kies op het volgende scherm **Wijzig naar Altijd toestaan**. Hierdoor kan een rit beginnen met opnemen zodra je instapt." + } + } + } }, "Open Settings" : { - "comment" : "Button shown when location permission is denied — opens iOS Settings" + "comment" : "Button shown when location permission is denied — opens iOS Settings", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen öffnen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open Settings" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvrir Réglages" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open Instellingen" + } + } + } }, "Open Shortcuts" : { - "comment" : "Onboarding button to open the Shortcuts app" - }, - "Open the **Shortcuts** app" : { - "comment" : "Onboarding automation finish step 1\nOnboarding automation start step 1" - }, - "Order" : { - "comment" : "Merge order section header", + "comment" : "Onboarding button to open the Shortcuts app", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Reihenfolge" + "value" : "Kurzbefehle öffnen" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Order" + "value" : "Open Shortcuts" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ordre" + "value" : "Ouvrir Raccourcis" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Volgorde" + "value" : "Open Opdrachten" } } } }, - "Private by design" : { - "comment" : "Onboarding welcome feature row 3 title" - }, - "Record a Drive" : { - "comment" : "TipKit tip title for the home screen record button", + "Open the **Shortcuts** app" : { + "comment" : "Onboarding automation finish step 1\nOnboarding automation start step 1", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrt aufnehmen" + "value" : "Öffne die App **Kurzbefehle**" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Record a Drive" + "value" : "Open the **Shortcuts** app" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Enregistrer un trajet" + "value" : "Ouvrez l'app **Raccourcis**" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Rit opnemen" + "value" : "Open de app **Opdrachten**" } } } }, - "Record drives hands-free when CarPlay connects." : { - "comment" : "Subtitle of the automation setup panel on the home screen" + "Order" : { + "comment" : "Merge order section header", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reihenfolge" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Order" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordre" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Volgorde" + } + } + } + }, + "Private by design" : { + "comment" : "Onboarding welcome feature row 3 title", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Von Grund auf privat" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Private by design" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confidentiel par conception" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privé door ontwerp" + } + } + } + }, + "Record a Drive" : { + "comment" : "TipKit tip title for the home screen record button", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrt aufnehmen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Record a Drive" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer un trajet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rit opnemen" + } + } + } + }, + "Record Your First Drive" : { + "comment" : "Button on empty state to start a new drive", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeichne deine erste Fahrt auf" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Record Your First Drive" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrez votre premier trajet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neem je eerste rit op" + } + } + } }, "RECORDING" : { "comment" : "Status badge text — translators may lowercase if appropriate", @@ -2706,1449 +3416,2143 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "AUFNAHME" + "value" : "AUFNAHME" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "RECORDING" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ENREGISTREMENT" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "OPNAME" + } + } + } + }, + "Recording in progress" : { + "comment" : "Status badge accessibility label", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aufzeichnung läuft" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recording in progress" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrement en cours" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opname bezig" + } + } + } + }, + "Records every drive automatically" : { + "comment" : "Onboarding welcome feature row 1 title", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeichnet jede Fahrt automatisch auf" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Records every drive automatically" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistre chaque trajet automatiquement" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neemt elke rit automatisch op" + } + } + } + }, + "Road Trip" : { + "comment" : "Drive category: long-distance journey", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Roadtrip" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Road Trip" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Road trip" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Roadtrip" + } + } + } + }, + "Route from %@ to %@, %@" : { + "comment" : "Accessibility label summarising a drive's route on the map", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route von %@ nach %@, %@" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route from %1$@ to %2$@, %3$@" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route from %@ to %@, %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Itinéraire de %@ à %@, %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route van %@ naar %@, %@" + } + } + } + }, + "Route from %@, %@" : { + "comment" : "Accessibility label for a route map with only a known start place", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route von %@, %@" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route from %1$@, %2$@" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route from %@, %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Itinéraire depuis %@, %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route vanaf %@, %@" + } + } + } + }, + "Route to %@, %@" : { + "comment" : "Accessibility label for a route map with only a known end place", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route nach %@, %@" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route to %1$@, %2$@" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route to %@, %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Itinéraire vers %@, %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route naar %@, %@" + } + } + } + }, + "Route, %@" : { + "comment" : "Accessibility label for a route map with no known place names", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route, %@" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route, %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Itinéraire, %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Route, %@" + } + } + } + }, + "Running in the background to save battery. Your full drive map appears here when the drive ends." : { + "comment" : "Battery saving note shown on the recording screen while a drive is in progress", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Läuft im Hintergrund, um Akku zu sparen. Die vollständige Fahrtenkarte erscheint hier, wenn die Fahrt endet." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running in the background to save battery. Your full drive map appears here when the drive ends." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fonctionne en arrière-plan pour économiser la batterie. La carte complète du trajet apparaît ici à la fin du trajet." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopt op de achtergrond om batterij te sparen. Uw volledige ritkaart verschijnt hier wanneer de rit eindigt." + } + } + } + }, + "Save" : { + "comment" : "Save changes", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speichern" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Save" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opslaan" + } + } + } + }, + "Scenic" : { + "comment" : "Drive category: scenic or leisure drive", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Landschaftlich" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scenic" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pittoresque" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schilderachtig" + } + } + } + }, + "Search" : { + "comment" : "A label for a search bar.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suchen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rechercher" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoeken" + } + } + } + }, + "Select 2 drives to merge" : { + "comment" : "Multiselect placeholder when nothing is selected", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "2 Fahrten zum Zusammenführen auswählen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select 2 drives to merge" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner 2 trajets à fusionner" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selecteer 2 ritten om samen te voegen" + } + } + } + }, + "Select Drives" : { + "comment" : "Menu item to enter multiselect mode", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrten auswählen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select Drives" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner des trajets" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ritten selecteren" + } + } + } + }, + "Set up “Finish Drive”" : { + "comment" : "Onboarding automation finish screen title", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "„Fahrt beenden“ einrichten" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set up “Finish Drive”" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurer « Terminer le trajet »" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "“Rit beëindigen” instellen" + } + } + } + }, + "Set up “Start Drive”" : { + "comment" : "Onboarding automation start screen title", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "„Fahrt starten“ einrichten" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set up “Start Drive”" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurer « Démarrer le trajet »" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "“Rit starten” instellen" + } + } + } + }, + "Set Up Automated Recording" : { + "comment" : "Title of the automation setup panel on the home screen", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatische Aufzeichnung einrichten" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Up Automated Recording" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurer l'enregistrement automatique" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch opnemen instellen" + } + } + } + }, + "Set Up Automations" : { + "comment" : "Onboarding automations intro primary button", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automationen einrichten" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Set Up Automations" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurer les automatisations" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatiseringen instellen" + } + } + } + }, + "Settings" : { + "comment" : "Menu item to open settings", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réglages" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Instellingen" + } + } + } + }, + "Share as GPX" : { + "comment" : "Share drive as GPX", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Als GPX teilen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Share as GPX" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager en GPX" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deel als GPX" + } + } + } + }, + "Share as PNG" : { + "comment" : "Share drive as PNG", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Als PNG teilen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Share as PNG" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager en PNG" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deel als PNG" + } + } + } + }, + "Share Drive" : { + "comment" : "Share button", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrt teilen" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Share Drive" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager le trajet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rit delen" + } + } + } + }, + "Start" : { + "comment" : "Export PNG start marker label", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Départ" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start" + } + } + } + }, + "Start a drive in ${applicationName}" : { + "comment" : "Siri shortcut phrase to start a drive", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eine Fahrt starten in ${applicationName}" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start a drive in ${applicationName}" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Démarrer un trajet dans ${applicationName}" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Een rit starten in ${applicationName}" + } + } + } + }, + "Start a new drive" : { + "comment" : "Record button when idle", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neue Fahrt starten" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start a new drive" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Démarrer un nouveau trajet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Een nieuwe rit starten" + } + } + } + }, + "Start drive" : { + "comment" : "Title of the Start Drive intent.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrt starten" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start drive" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Démarrer un trajet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rit starten" + } + } + } + }, + "Start Drive" : { + "comment" : "Short title for the Start Drive intent.", + "isCommentAutoGenerated" : true, + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fahrt starten" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "RECORDING" + "value" : "Start Drive" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "ENREGISTREMENT" + "value" : "Démarrer un trajet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "OPNAME" + "value" : "Rit starten" } } } }, - "Recording in progress" : { - "comment" : "Status badge accessibility label", + "Start Location" : { + "comment" : "Edit drive section header", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aufzeichnung läuft" + "value" : "Startpunkt" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Recording in progress" + "value" : "Start Location" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Enregistrement en cours" + "value" : "Lieu de départ" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Opname bezig" + "value" : "Startlocatie" } } } }, - "Records every drive automatically" : { - "comment" : "Onboarding welcome feature row 1 title" - }, - "Road Trip" : { - "comment" : "Drive category: long-distance journey", + "Start location name" : { + "comment" : "Start location text field placeholder", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Roadtrip" + "value" : "Name des Startorts" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Road Trip" + "value" : "Start location name" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Road trip" + "value" : "Nom du lieu de départ" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Roadtrip" + "value" : "Naam startlocatie" } } } }, - "Route from %@ to %@, %@" : { - "comment" : "Accessibility label summarising a drive's route on the map", + "Start my drive in ${applicationName}" : { + "comment" : "Siri shortcut phrase to start my drive", + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Route von %@ nach %@, %@" - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Route from %1$@ to %2$@, %3$@" + "value" : "Meine Fahrt starten in ${applicationName}" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Route from %@ to %@, %@" + "value" : "Start my drive in ${applicationName}" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Itinéraire de %@ à %@, %@" + "value" : "Démarrer mon trajet dans ${applicationName}" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Route van %@ naar %@, %@" + "value" : "Mijn rit starten in ${applicationName}" } } } }, - "Route from %@, %@" : { - "comment" : "Accessibility label for a route map with only a known start place", + "Start recording a drive with ${applicationName}" : { + "comment" : "Siri shortcut phrase to start recording a drive", + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Route von %@, %@" - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Route from %1$@, %2$@" + "value" : "Eine Fahrt aufzeichnen mit ${applicationName}" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Route from %@, %@" + "value" : "Start recording a drive with ${applicationName}" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Itinéraire depuis %@, %@" + "value" : "Commencer l'enregistrement d'un trajet avec ${applicationName}" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Route vanaf %@, %@" + "value" : "Een rit opnemen met ${applicationName}" } } } }, - "Route to %@, %@" : { - "comment" : "Accessibility label for a route map with only a known end place", + "Start recording a drive." : { + "comment" : "The description of the Start Drive intent.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Route nach %@, %@" - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Route to %1$@, %2$@" + "value" : "Aufzeichnung einer Fahrt starten." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Route to %@, %@" + "value" : "Start recording a drive." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Itinéraire vers %@, %@" + "value" : "Démarrer l'enregistrement d'un trajet." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Route naar %@, %@" + "value" : "Opname van een rit starten." } } } }, - "Route, %@" : { - "comment" : "Accessibility label for a route map with no known place names", + "Start Using Driveline" : { + "comment" : "Onboarding final completion button", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Route, %@" + "value" : "Driveline verwenden" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Route, %@" + "value" : "Start Using Driveline" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Itinéraire, %@" + "value" : "Commencer à utiliser Driveline" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Route, %@" + "value" : "Driveline gebruiken" } } } }, - "Running in the background to save battery. Your full drive map appears here when the drive ends." : { - "comment" : "Battery saving note shown on the recording screen while a drive is in progress", + "started" : { + "comment" : "Label for the time the drive started", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Läuft im Hintergrund, um Akku zu sparen. Die vollständige Fahrtenkarte erscheint hier, wenn die Fahrt endet." + "value" : "gestartet" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Running in the background to save battery. Your full drive map appears here when the drive ends." + "value" : "started" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fonctionne en arrière-plan pour économiser la batterie. La carte complète du trajet apparaît ici à la fin du trajet." + "value" : "démarré" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loopt op de achtergrond om batterij te sparen. Uw volledige ritkaart verschijnt hier wanneer de rit eindigt." + "value" : "gestart" } } } }, - "Save" : { - "comment" : "Save changes", + "Started by" : { + "comment" : "Metadata row", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Speichern" + "value" : "Gestartet von" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Save" + "value" : "Started by" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Enregistrer" + "value" : "Démarré par" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Opslaan" + "value" : "Gestart door" } } } }, - "Scenic" : { - "comment" : "Drive category: scenic or leisure drive", + "Starts when your car connects" : { + "comment" : "Onboarding welcome feature row 2 title", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Landschaftlich" + "value" : "Startet, wenn dein Auto sich verbindet" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Scenic" + "value" : "Starts when your car connects" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Pittoresque" + "value" : "Démarre quand votre voiture se connecte" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Schilderachtig" + "value" : "Start wanneer je auto verbinding maakt" } } } }, - "Search" : { - "comment" : "A label for a search bar.", - "isCommentAutoGenerated" : true, + "Swap order" : { + "comment" : "Accessibility label for button that swaps the order of the two drives being merged", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Suchen" + "value" : "Reihenfolge tauschen" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Search" + "value" : "Swap order" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rechercher" + "value" : "Inverser l'ordre" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zoeken" + "value" : "Volgorde omwisselen" } } } }, - "Select 2 drives to merge" : { - "comment" : "Multiselect placeholder when nothing is selected", + "Switch Stats View" : { + "comment" : "TipKit tip title for the home screen stats panel", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "2 Fahrten zum Zusammenführen auswählen" + "value" : "Statistikansicht wechseln" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Select 2 drives to merge" + "value" : "Switch Stats View" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Sélectionner 2 trajets à fusionner" + "value" : "Changer la vue des statistiques" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Selecteer 2 ritten om samen te voegen" + "value" : "Statistieken wisselen" } } } }, - "Select Drives" : { - "comment" : "Menu item to enter multiselect mode", + "Tap **+** then **New Automation**" : { + "comment" : "Onboarding automation finish step 3\nOnboarding automation start step 3", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrten auswählen" + "value" : "Tippe auf **+** und dann auf **Neue Automation**" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Select Drives" + "value" : "Tap **+** then **New Automation**" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Sélectionner des trajets" + "value" : "Touchez **+** puis **Nouvelle automatisation**" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ritten selecteren" + "value" : "Tik op **+** en daarna op **Nieuwe automatisering**" } } } }, - "Set up “Finish Drive”" : { - "comment" : "Onboarding automation finish screen title" - }, - "Set up “Start Drive”" : { - "comment" : "Onboarding automation start screen title" - }, - "Set Up Automated Recording" : { - "comment" : "Title of the automation setup panel on the home screen" - }, - "Set Up Automations" : { - "comment" : "Onboarding automations intro primary button" - }, - "Settings" : { - "comment" : "Menu item to open settings", + "Tap **Add Action**, search **Finish Drive** and select it" : { + "comment" : "Onboarding automation finish step 5", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einstellungen" + "value" : "Tippe auf **Aktion hinzufügen**, suche nach **Fahrt beenden** und wähle sie aus" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "Tap **Add Action**, search **Finish Drive** and select it" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Réglages" + "value" : "Touchez **Ajouter une action**, recherchez **Terminer le trajet** et sélectionnez-la" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Instellingen" + "value" : "Tik op **Actie toevoegen**, zoek **Rit beëindigen** en selecteer deze" } } } }, - "Share as GPX" : { - "comment" : "Share drive as GPX", + "Tap **Add Action**, search **Start Drive** and select it" : { + "comment" : "Onboarding automation start step 5", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Als GPX teilen" + "value" : "Tippe auf **Aktion hinzufügen**, suche nach **Fahrt starten** und wähle sie aus" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Share as GPX" + "value" : "Tap **Add Action**, search **Start Drive** and select it" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Partager en GPX" + "value" : "Touchez **Ajouter une action**, recherchez **Démarrer le trajet** et sélectionnez-la" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Deel als GPX" + "value" : "Tik op **Actie toevoegen**, zoek **Rit starten** en selecteer deze" } } } }, - "Share as PNG" : { - "comment" : "Share drive as PNG", + "Tap **Automation** at the bottom" : { + "comment" : "Onboarding automation finish step 2\nOnboarding automation start step 2", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Als PNG teilen" + "value" : "Tippe unten auf **Automation**" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Share as PNG" + "value" : "Tap **Automation** at the bottom" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Partager en PNG" + "value" : "Touchez **Automatisation** en bas" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Deel als PNG" + "value" : "Tik onderaan op **Automatisering**" } } } }, - "Share Drive" : { - "comment" : "Share button", + "Tap **Done**" : { + "comment" : "Onboarding automation finish step 7\nOnboarding automation start step 7", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrt teilen" + "value" : "Tippe auf **Fertig**" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Share Drive" + "value" : "Tap **Done**" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Partager le trajet" + "value" : "Touchez **OK**" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Rit delen" + "value" : "Tik op **Gereed**" } } } }, - "Start" : { - "comment" : "Export PNG start marker label", + "Tap the options button to edit the name and other details." : { + "comment" : "TipKit tip message for the drive detail options button", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Start" + "value" : "Tippe auf die Optionsschaltfläche, um den Namen und weitere Details zu bearbeiten." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start" + "value" : "Tap the options button to edit the name and other details." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Départ" + "value" : "Touchez le bouton d'options pour modifier le nom et d'autres détails." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Start" + "value" : "Tik op de optieknop om de naam en andere details te bewerken." } } } }, - "Start a drive in ${applicationName}" : { - "comment" : "Siri shortcut phrase to start a drive", - "extractionState" : "stale", + "Tap to start manually tracking a new journey." : { + "comment" : "TipKit tip message for the home screen record button", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Eine Fahrt starten in ${applicationName}" + "value" : "Tippen, um eine neue Fahrt manuell aufzuzeichnen." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start a drive in ${applicationName}" + "value" : "Tap to start manually tracking a new journey." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Démarrer un trajet dans ${applicationName}" + "value" : "Appuyez pour commencer à enregistrer manuellement un nouveau trajet." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Een rit starten in ${applicationName}" + "value" : "Tik om handmatig een nieuwe rit op te nemen." } } } }, - "Start a new drive" : { - "comment" : "Record button when idle", + "Tap to toggle between the last 30 days and all time." : { + "comment" : "TipKit tip message for the home screen stats panel", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Neue Fahrt starten" + "value" : "Tippen, um zwischen den letzten 30 Tagen und allen Fahrten zu wechseln." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start a new drive" + "value" : "Tap to toggle between the last 30 days and all time." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Démarrer un nouveau trajet" + "value" : "Appuyez pour basculer entre les 30 derniers jours et toutes les statistiques." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Een nieuwe rit starten" + "value" : "Tik om te wisselen tussen de laatste 30 dagen en alle ritten." } } } }, - "Start drive" : { - "comment" : "Title of the Start Drive intent.", - "isCommentAutoGenerated" : true, + "The two drives are joined end-to-end in this order. The original drives are removed and can't be restored." : { + "comment" : "Merge disclaimer about track joining and deletion", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrt starten" + "value" : "Die beiden Fahrten werden in dieser Reihenfolge aneinandergehängt. Die ursprünglichen Fahrten werden gelöscht und können nicht wiederhergestellt werden." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start drive" + "value" : "The two drives are joined end-to-end in this order. The original drives are removed and can't be restored." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Démarrer un trajet" + "value" : "Les deux trajets sont assemblés bout à bout dans cet ordre. Les trajets d'origine sont supprimés et ne peuvent pas être restaurés." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Rit starten" + "value" : "De twee ritten worden in deze volgorde aaneengesloten. De originele ritten worden verwijderd en kunnen niet worden hersteld." } } } }, - "Start Drive" : { - "comment" : "Short title for the Start Drive intent.", - "isCommentAutoGenerated" : true, + "This drive and all its data will be permanently deleted." : { + "comment" : "Delete drive confirmation message", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fahrt starten" + "value" : "Diese Fahrt und alle ihre Daten werden endgültig gelöscht." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start Drive" + "value" : "This drive and all its data will be permanently deleted." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Démarrer un trajet" + "value" : "Ce trajet et toutes ses données seront définitivement supprimés." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Rit starten" + "value" : "Deze rit en al haar gegevens worden permanent verwijderd." } } } }, - "Start Location" : { - "comment" : "Edit drive section header", + "To record drives automatically — even when Driveline isn't open — your location needs to be available in the background." : { + "comment" : "Onboarding Always-location primer body paragraph 1", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Startpunkt" + "value" : "Damit Fahrten automatisch aufgezeichnet werden — auch wenn Driveline nicht geöffnet ist — muss dein Standort im Hintergrund verfügbar sein." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start Location" + "value" : "To record drives automatically — even when Driveline isn't open — your location needs to be available in the background." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Lieu de départ" + "value" : "Pour enregistrer les trajets automatiquement — même lorsque Driveline n'est pas ouverte — votre position doit être disponible en arrière-plan." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Startlocatie" + "value" : "Om ritten automatisch op te nemen — ook wanneer Driveline niet open is — moet je locatie op de achtergrond beschikbaar zijn." } } } }, - "Start location name" : { - "comment" : "Start location text field placeholder", + "Today" : { + "comment" : "Title of a drive section for today.", + "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Name des Startorts" + "value" : "Heute" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start location name" + "value" : "Today" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nom du lieu de départ" + "value" : "Aujourd'hui" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Naam startlocatie" + "value" : "Vandaag" } } } }, - "Start my drive in ${applicationName}" : { - "comment" : "Siri shortcut phrase to start my drive", - "extractionState" : "stale", + "Top Speed" : { + "comment" : "Metadata row", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Meine Fahrt starten in ${applicationName}" + "value" : "Höchstgeschwindigkeit" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start my drive in ${applicationName}" + "value" : "Top Speed" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Démarrer mon trajet dans ${applicationName}" + "value" : "Vitesse maximale" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Mijn rit starten in ${applicationName}" + "value" : "Topsnelheid" } } } }, - "Start recording a drive with ${applicationName}" : { - "comment" : "Siri shortcut phrase to start recording a drive", - "extractionState" : "stale", + "Total distance" : { + "comment" : "Merge stat: total combined distance", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Eine Fahrt aufzeichnen mit ${applicationName}" + "value" : "Gesamtstrecke" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start recording a drive with ${applicationName}" + "value" : "Total distance" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Commencer l'enregistrement d'un trajet avec ${applicationName}" + "value" : "Distance totale" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Een rit opnemen met ${applicationName}" + "value" : "Totale afstand" } } } }, - "Start recording a drive." : { - "comment" : "The description of the Start Drive intent.", - "isCommentAutoGenerated" : true, + "Total duration" : { + "comment" : "Merge stat: total combined duration", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aufzeichnung einer Fahrt starten." + "value" : "Gesamtdauer" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Start recording a drive." + "value" : "Total duration" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Démarrer l'enregistrement d'un trajet." + "value" : "Durée totale" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Opname van een rit starten." + "value" : "Totale duur" } } } }, - "Start Using Driveline" : { - "comment" : "Onboarding final completion button" - }, - "started" : { - "comment" : "Label for the time the drive started", + "Track points" : { + "comment" : "Merge stat: combined GPS track point count", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "gestartet" + "value" : "Spurpunkte" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "started" + "value" : "Track points" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "démarré" + "value" : "Points de trace" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "gestart" + "value" : "Trackpunten" } } } }, - "Started by" : { + "Track Points" : { "comment" : "Metadata row", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gestartet von" + "value" : "Spurpunkte" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Started by" + "value" : "Track Points" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Démarré par" + "value" : "Points de trace" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gestart door" + "value" : "Trackpunten" } } } }, - "Starts when your car connects" : { - "comment" : "Onboarding welcome feature row 2 title" - }, - "Swap order" : { - "comment" : "Accessibility label for button that swaps the order of the two drives being merged", + "Turn off **Ask Before Running**" : { + "comment" : "Onboarding automation finish step 6\nOnboarding automation start step 6", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Reihenfolge tauschen" + "value" : "Schalte **Vor Ausführung fragen** aus" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Swap order" + "value" : "Turn off **Ask Before Running**" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Inverser l'ordre" + "value" : "Désactivez **Demander avant d'exécuter**" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Volgorde omwisselen" + "value" : "Schakel **Vraag voor uitvoering** uit" } } } }, - "Switch Stats View" : { - "comment" : "TipKit tip title for the home screen stats panel", + "Two quick automations" : { + "comment" : "Onboarding automations intro screen title", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Statistikansicht wechseln" + "value" : "Zwei schnelle Automationen" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Switch Stats View" + "value" : "Two quick automations" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Changer la vue des statistiques" + "value" : "Deux automatisations rapides" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Statistieken wisselen" + "value" : "Twee snelle automatiseringen" } } } }, - "Tap **+** then **New Automation**" : { - "comment" : "Onboarding automation finish step 3\nOnboarding automation start step 3" - }, - "Tap **Add Action**, search **Finish Drive** and select it" : { - "comment" : "Onboarding automation finish step 5" - }, - "Tap **Add Action**, search **Start Drive** and select it" : { - "comment" : "Onboarding automation start step 5" - }, - "Tap **Automation** at the bottom" : { - "comment" : "Onboarding automation finish step 2\nOnboarding automation start step 2" - }, - "Tap **Done**" : { - "comment" : "Onboarding automation finish step 7\nOnboarding automation start step 7" - }, - "Tap the options button to edit the name and other details." : { - "comment" : "TipKit tip message for the drive detail options button", + "Unknown" : { + "comment" : "Unknown place name", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tippe auf die Optionsschaltfläche, um den Namen und weitere Details zu bearbeiten." + "value" : "Unbekannt" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Tap the options button to edit the name and other details." + "value" : "Unknown" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Touchez le bouton d'options pour modifier le nom et d'autres détails." + "value" : "Inconnu" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Tik op de optieknop om de naam en andere details te bewerken." + "value" : "Onbekend" } } } }, - "Tap to start manually tracking a new journey." : { - "comment" : "TipKit tip message for the home screen record button", + "Urban" : { + "comment" : "Drive category: city or town driving", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tippen, um eine neue Fahrt manuell aufzuzeichnen." + "value" : "Stadt" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Tap to start manually tracking a new journey." + "value" : "Urban" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Appuyez pour commencer à enregistrer manuellement un nouveau trajet." + "value" : "Urbain" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Tik om handmatig een nieuwe rit op te nemen." + "value" : "Stedelijk" } } } }, - "Tap to toggle between the last 30 days and all time." : { - "comment" : "TipKit tip message for the home screen stats panel", + "Used only while driving" : { + "comment" : "Onboarding Always-location info row 2 title", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tippen, um zwischen den letzten 30 Tagen und allen Fahrten zu wechseln." + "value" : "Wird nur während der Fahrt verwendet" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Tap to toggle between the last 30 days and all time." + "value" : "Used only while driving" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Appuyez pour basculer entre les 30 derniers jours et toutes les statistiques." + "value" : "Utilisée uniquement pendant la conduite" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Tik om te wisselen tussen de laatste 30 dagen en alle ritten." + "value" : "Alleen gebruikt tijdens het rijden" } } } }, - "The two drives are joined end-to-end in this order. The original drives are removed and can't be restored." : { - "comment" : "Merge disclaimer about track joining and deletion", + "Weather data provided by Apple Weather" : { + "comment" : "Weather attribution footer link", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Die beiden Fahrten werden in dieser Reihenfolge aneinandergehängt. Die ursprünglichen Fahrten werden gelöscht und können nicht wiederhergestellt werden." + "value" : "Wetterdaten bereitgestellt von Apple Wetter" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "The two drives are joined end-to-end in this order. The original drives are removed and can't be restored." + "value" : "Weather data provided by Apple Weather" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Les deux trajets sont assemblés bout à bout dans cet ordre. Les trajets d'origine sont supprimés et ne peuvent pas être restaurés." + "value" : "Données météo fournies par Apple Météo" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De twee ritten worden in deze volgorde aaneengesloten. De originele ritten worden verwijderd en kunnen niet worden hersteld." + "value" : "Weergegevens aangeboden door Apple Weather" } } } }, - "This automation starts recording the moment CarPlay connects — no button needed." : { - "comment" : "Onboarding automation start screen body" - }, - "This automation stops and saves your drive the moment CarPlay disconnects." : { - "comment" : "Onboarding automation finish screen body" - }, - "This drive and all its data will be permanently deleted." : { - "comment" : "Delete drive confirmation message", + "Welcome to Driveline" : { + "comment" : "Onboarding welcome screen title", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Diese Fahrt und alle ihre Daten werden endgültig gelöscht." + "value" : "Willkommen bei Driveline" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "This drive and all its data will be permanently deleted." + "value" : "Welcome to Driveline" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ce trajet et toutes ses données seront définitivement supprimés." + "value" : "Bienvenue dans Driveline" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Deze rit en al haar gegevens worden permanent verwijderd." + "value" : "Welkom bij Driveline" } } } }, - "To record drives automatically — even when Driveline isn't open — your location needs to be available in the background." : { - "comment" : "Onboarding Always-location primer body paragraph 1" - }, - "Today" : { - "comment" : "Title of a drive section for today.", + "Yesterday" : { + "comment" : "Title of the section for drives from yesterday.", "isCommentAutoGenerated" : true, "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Heute" + "value" : "Gestern" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Today" + "value" : "Yesterday" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aujourd'hui" + "value" : "Hier" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vandaag" + "value" : "Gisteren" } } } }, - "Top Speed" : { - "comment" : "Metadata row", + "Your recorded drives will appear here." : { + "comment" : "Empty state description shown on the home screen when no drives have been recorded yet", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Höchstgeschwindigkeit" + "value" : "Deine aufgezeichneten Fahrten erscheinen hier." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Top Speed" + "value" : "Your recorded drives will appear here." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Vitesse maximale" + "value" : "Vos trajets enregistrés apparaîtront ici." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Topsnelheid" + "value" : "Uw opgenomen ritten verschijnen hier." } } } }, - "Total distance" : { - "comment" : "Merge stat: total combined distance", + "Your routes stay on your device. Location is used only to record drives." : { + "comment" : "Onboarding welcome feature row 3 body", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gesamtstrecke" + "value" : "Deine Routen bleiben auf deinem Gerät. Der Standort wird nur zur Aufzeichnung von Fahrten verwendet." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Total distance" + "value" : "Your routes stay on your device. Location is used only to record drives." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Distance totale" + "value" : "Vos itinéraires restent sur votre appareil. La position n'est utilisée que pour enregistrer les trajets." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Totale afstand" + "value" : "Je routes blijven op je apparaat. Locatie wordt alleen gebruikt om ritten op te nemen." } } } }, - "Total duration" : { - "comment" : "Merge stat: total combined duration", + "Record drives hands-free when Bluetooth or CarPlay connects." : { + "comment" : "Subtitle of the automation setup panel on the home screen", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gesamtdauer" + "value" : "Zeichne Fahrten freihändig auf, wenn Bluetooth oder CarPlay sich verbindet." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Total duration" + "value" : "Record drives hands-free when Bluetooth or CarPlay connects." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Durée totale" + "value" : "Enregistrez des trajets en mains libres dès que le Bluetooth ou CarPlay se connecte." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Totale duur" + "value" : "Neem ritten handsfree op zodra bluetooth of CarPlay verbinding maakt." } } } }, - "Track points" : { - "comment" : "Merge stat: combined GPS track point count", + "When your car connects" : { + "comment" : "Onboarding automations intro row 1 title", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Spurpunkte" + "value" : "Wenn dein Auto sich verbindet" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Track points" + "value" : "When your car connects" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Points de trace" + "value" : "Quand votre voiture se connecte" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Trackpunten" + "value" : "Wanneer je auto verbinding maakt" } } } }, - "Track Points" : { - "comment" : "Metadata row", + "When your car disconnects" : { + "comment" : "Onboarding automations intro row 2 title", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Spurpunkte" + "value" : "Wenn dein Auto die Verbindung trennt" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Track Points" + "value" : "When your car disconnects" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Points de trace" + "value" : "Quand votre voiture se déconnecte" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Trackpunten" + "value" : "Wanneer je auto de verbinding verbreekt" } } } }, - "Turn off **Ask Before Running**" : { - "comment" : "Onboarding automation finish step 6\nOnboarding automation start step 6" - }, - "Two quick automations" : { - "comment" : "Onboarding automations intro screen title" - }, - "Unknown" : { - "comment" : "Unknown place name", + "Choose **CarPlay** → **Connects** (or **Bluetooth** → your car)" : { + "comment" : "Onboarding automation start step 4", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Unbekannt" + "value" : "Wähle **CarPlay** → **Wird verbunden** (oder **Bluetooth** → dein Auto)" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Unknown" + "value" : "Choose **CarPlay** → **Connects** (or **Bluetooth** → your car)" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Inconnu" + "value" : "Choisissez **CarPlay** → **Se connecte** (ou **Bluetooth** → votre voiture)" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Onbekend" + "value" : "Kies **CarPlay** → **Maakt verbinding** (of **Bluetooth** → je auto)" } } } }, - "Urban" : { - "comment" : "Drive category: city or town driving", + "Choose **CarPlay** → **Disconnects** (or **Bluetooth** → your car)" : { + "comment" : "Onboarding automation finish step 4", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Stadt" + "value" : "Wähle **CarPlay** → **Wird getrennt** (oder **Bluetooth** → dein Auto)" } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Urban" + "value" : "Choose **CarPlay** → **Disconnects** (or **Bluetooth** → your car)" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Urbain" + "value" : "Choisissez **CarPlay** → **Se déconnecte** (ou **Bluetooth** → votre voiture)" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stedelijk" + "value" : "Kies **CarPlay** → **Verbreekt verbinding** (of **Bluetooth** → je auto)" } } } }, - "Used only while driving" : { - "comment" : "Onboarding Always-location info row 2 title" - }, - "Weather data provided by Apple Weather" : { - "comment" : "Weather attribution footer link", + "For truly hands-free recording, set up two automations in the Shortcuts app. They start and stop Driveline the moment your iPhone connects to your car — over CarPlay or Bluetooth." : { + "comment" : "Onboarding automations intro screen body", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wetterdaten bereitgestellt von Apple Wetter" + "value" : "Für wirklich freihändige Aufzeichnung richte zwei Automationen in der App „Kurzbefehle“ ein. Sie starten und stoppen Driveline, sobald sich dein iPhone mit deinem Auto verbindet — über CarPlay oder Bluetooth." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Weather data provided by Apple Weather" + "value" : "For truly hands-free recording, set up two automations in the Shortcuts app. They start and stop Driveline the moment your iPhone connects to your car — over CarPlay or Bluetooth." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Données météo fournies par Apple Météo" + "value" : "Pour un enregistrement vraiment mains libres, configurez deux automatisations dans l'app Raccourcis. Elles démarrent et arrêtent Driveline dès que votre iPhone se connecte à votre voiture — via CarPlay ou Bluetooth." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Weergegevens aangeboden door Apple Weather" + "value" : "Voor echt handsfree opnemen stel je twee automatiseringen in de app Opdrachten in. Ze starten en stoppen Driveline zodra je iPhone verbinding maakt met je auto — via CarPlay of bluetooth." } } } }, - "Welcome to Driveline" : { - "comment" : "Onboarding welcome screen title" - }, - "Yesterday" : { - "comment" : "Title of the section for drives from yesterday.", - "isCommentAutoGenerated" : true, + "This automation starts recording the moment CarPlay connects — or your car's Bluetooth pairs." : { + "comment" : "Onboarding automation start screen body", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gestern" + "value" : "Diese Automation startet die Aufzeichnung, sobald CarPlay sich verbindet — oder das Bluetooth deines Autos koppelt." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Yesterday" + "value" : "This automation starts recording the moment CarPlay connects — or your car's Bluetooth pairs." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Hier" + "value" : "Cette automatisation démarre l'enregistrement dès que CarPlay se connecte — ou que le Bluetooth de votre voiture s'appaire." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gisteren" + "value" : "Deze automatisering start de opname zodra CarPlay verbinding maakt — of de bluetooth van je auto koppelt." } } } }, - "Your recorded drives will appear here." : { - "comment" : "Empty state description shown on the home screen when no drives have been recorded yet", + "This automation stops and saves your drive the moment CarPlay disconnects — or your car's Bluetooth drops." : { + "comment" : "Onboarding automation finish screen body", "localizations" : { "de" : { "stringUnit" : { "state" : "translated", - "value" : "Deine aufgezeichneten Fahrten erscheinen hier." + "value" : "Diese Automation stoppt und speichert deine Fahrt, sobald CarPlay die Verbindung trennt — oder das Bluetooth deines Autos abbricht." } }, "en-GB" : { "stringUnit" : { "state" : "translated", - "value" : "Your recorded drives will appear here." + "value" : "This automation stops and saves your drive the moment CarPlay disconnects — or your car's Bluetooth drops." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Vos trajets enregistrés apparaîtront ici." + "value" : "Cette automatisation arrête et enregistre votre trajet dès que CarPlay se déconnecte — ou que le Bluetooth de votre voiture se coupe." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Uw opgenomen ritten verschijnen hier." + "value" : "Deze automatisering stopt en bewaart je rit zodra CarPlay de verbinding verbreekt — of de bluetooth van je auto wegvalt." } } } - }, - "Your routes stay on your device. Location is used only to record drives." : { - "comment" : "Onboarding welcome feature row 3 body" } }, "version" : "1.2" diff --git a/Driveline/UI/Home/HomePresenter.swift b/Driveline/UI/Home/HomePresenter.swift index 87cec6b..a4e7ba9 100644 --- a/Driveline/UI/Home/HomePresenter.swift +++ b/Driveline/UI/Home/HomePresenter.swift @@ -36,7 +36,7 @@ enum HomePresenter { } static var newDriveButtonTitle: String { - String(localized: "New Drive", comment: "Button on empty state to start a new drive") + String(localized: "Record Your First Drive", comment: "Button on empty state to start a new drive") } static var automationSetupTitle: String { @@ -44,6 +44,6 @@ enum HomePresenter { } static var automationSetupSubtitle: String { - String(localized: "Record drives hands-free when CarPlay connects.", comment: "Subtitle of the automation setup panel on the home screen") + String(localized: "Record drives hands-free when Bluetooth or CarPlay connects.", comment: "Subtitle of the automation setup panel on the home screen") } } diff --git a/Driveline/UI/Home/HomeView.swift b/Driveline/UI/Home/HomeView.swift index 5cf878b..bf48a27 100644 --- a/Driveline/UI/Home/HomeView.swift +++ b/Driveline/UI/Home/HomeView.swift @@ -76,10 +76,14 @@ struct HomeView: View { .onChange(of: driveService.isRecording, initial: true) { _, isRecording in if isRecording { exitSelectMode() } StatsPanelTip.isRecording = isRecording + RecordButtonTip.isRecording = isRecording } .onChange(of: recentStats.driveCount, initial: true) { _, count in StatsPanelTip.driveCount = count } + .onChange(of: drives.isEmpty, initial: true) { _, isEmpty in + RecordButtonTip.hasDrives = !isEmpty + } } .onContinueUserActivity(CSSearchableItemActionType) { activity in guard let identifier = activity.userInfo?[CSSearchableItemActivityIdentifier] as? String else { return } diff --git a/Driveline/UI/Onboarding/OnboardingAutomationDetailView.swift b/Driveline/UI/Onboarding/OnboardingAutomationDetailView.swift index c7bfb21..f6f985e 100644 --- a/Driveline/UI/Onboarding/OnboardingAutomationDetailView.swift +++ b/Driveline/UI/Onboarding/OnboardingAutomationDetailView.swift @@ -35,15 +35,25 @@ struct OnboardingAutomationDetailView: View { } private var steps: [String] { - [ - OnboardingPresenter.automationStartStep1, - OnboardingPresenter.automationStartStep2, - OnboardingPresenter.automationStartStep3, - OnboardingPresenter.automationStartStep4, - OnboardingPresenter.automationStartStep5, - OnboardingPresenter.automationStartStep6, - OnboardingPresenter.automationStartStep7 - ] + isStart + ? [ + OnboardingPresenter.automationStartStep1, + OnboardingPresenter.automationStartStep2, + OnboardingPresenter.automationStartStep3, + OnboardingPresenter.automationStartStep4, + OnboardingPresenter.automationStartStep5, + OnboardingPresenter.automationStartStep6, + OnboardingPresenter.automationStartStep7 + ] + : [ + OnboardingPresenter.automationFinishStep1, + OnboardingPresenter.automationFinishStep2, + OnboardingPresenter.automationFinishStep3, + OnboardingPresenter.automationFinishStep4, + OnboardingPresenter.automationFinishStep5, + OnboardingPresenter.automationFinishStep6, + OnboardingPresenter.automationFinishStep7 + ] } private var primaryLabel: String { diff --git a/Driveline/UI/Onboarding/OnboardingPresenter.swift b/Driveline/UI/Onboarding/OnboardingPresenter.swift index 762c8ac..94c24de 100644 --- a/Driveline/UI/Onboarding/OnboardingPresenter.swift +++ b/Driveline/UI/Onboarding/OnboardingPresenter.swift @@ -106,11 +106,11 @@ enum OnboardingPresenter { } static var automationsIntroBody: String { - String(localized: "For truly hands-free recording, set up two automations in the Shortcuts app. They start and stop Driveline the moment you connect or disconnect from CarPlay.", comment: "Onboarding automations intro screen body") + String(localized: "For truly hands-free recording, set up two automations in the Shortcuts app. They start and stop Driveline the moment your iPhone connects to your car — over CarPlay or Bluetooth.", comment: "Onboarding automations intro screen body") } static var automationsIntroRow1Title: String { - String(localized: "CarPlay connects", comment: "Onboarding automations intro row 1 title") + String(localized: "When your car connects", comment: "Onboarding automations intro row 1 title") } static var automationsIntroRow1Body: String { @@ -118,7 +118,7 @@ enum OnboardingPresenter { } static var automationsIntroRow2Title: String { - String(localized: "CarPlay disconnects", comment: "Onboarding automations intro row 2 title") + String(localized: "When your car disconnects", comment: "Onboarding automations intro row 2 title") } static var automationsIntroRow2Body: String { @@ -136,7 +136,7 @@ enum OnboardingPresenter { } static var automationStartBody: String { - String(localized: "This automation starts recording the moment CarPlay connects — no button needed.", comment: "Onboarding automation start screen body") + String(localized: "This automation starts recording the moment CarPlay connects — or your car's Bluetooth pairs.", comment: "Onboarding automation start screen body") } static var automationStartStep1: String { @@ -152,7 +152,7 @@ enum OnboardingPresenter { } static var automationStartStep4: String { - String(localized: "Choose **CarPlay** → **Connects**", comment: "Onboarding automation start step 4") + String(localized: "Choose **CarPlay** → **Connects** (or **Bluetooth** → your car)", comment: "Onboarding automation start step 4") } static var automationStartStep5: String { @@ -174,7 +174,7 @@ enum OnboardingPresenter { } static var automationFinishBody: String { - String(localized: "This automation stops and saves your drive the moment CarPlay disconnects.", comment: "Onboarding automation finish screen body") + String(localized: "This automation stops and saves your drive the moment CarPlay disconnects — or your car's Bluetooth drops.", comment: "Onboarding automation finish screen body") } static var automationFinishStep1: String { @@ -190,7 +190,7 @@ enum OnboardingPresenter { } static var automationFinishStep4: String { - String(localized: "Choose **CarPlay** → **Disconnects**", comment: "Onboarding automation finish step 4") + String(localized: "Choose **CarPlay** → **Disconnects** (or **Bluetooth** → your car)", comment: "Onboarding automation finish step 4") } static var automationFinishStep5: String { diff --git a/Driveline/UI/Tips/RecordButtonTip.swift b/Driveline/UI/Tips/RecordButtonTip.swift index 7840034..8b88dea 100644 --- a/Driveline/UI/Tips/RecordButtonTip.swift +++ b/Driveline/UI/Tips/RecordButtonTip.swift @@ -10,9 +10,13 @@ import TipKit struct RecordButtonTip: Tip { @Parameter static var isOnboardingPresented: Bool = true + @Parameter static var hasDrives: Bool = false + @Parameter static var isRecording: Bool = false var rules: [Rule] { #Rule(Self.$isOnboardingPresented) { $0 == false } + #Rule(Self.$hasDrives) { $0 == true } + #Rule(Self.$isRecording) { $0 == false } } var title: Text { diff --git a/DrivelineTests/UITests/EditDriveTipTests.swift b/DrivelineTests/UITests/EditDriveTipTests.swift index 0161225..dd5d343 100644 --- a/DrivelineTests/UITests/EditDriveTipTests.swift +++ b/DrivelineTests/UITests/EditDriveTipTests.swift @@ -21,6 +21,13 @@ struct EditDriveTipTests { _ = EditDriveTip() } + // MARK: - Rules + + @Test + func gatesOnOnboardingDismissed() { + #expect(EditDriveTip().rules.count == 1) + } + // MARK: - isOnboardingPresented parameter @Test diff --git a/DrivelineTests/UITests/RecordButtonTipTests.swift b/DrivelineTests/UITests/RecordButtonTipTests.swift index 3818a92..de83814 100644 --- a/DrivelineTests/UITests/RecordButtonTipTests.swift +++ b/DrivelineTests/UITests/RecordButtonTipTests.swift @@ -14,24 +14,47 @@ struct RecordButtonTipTests { init() { RecordButtonTip.isOnboardingPresented = true + RecordButtonTip.hasDrives = false + RecordButtonTip.isRecording = false } + // MARK: - Instantiation + @Test func tipCanBeInstantiated() { _ = RecordButtonTip() } - // MARK: - isOnboardingPresented parameter + // MARK: - Rules + + @Test + func gatesOnOnboardingDismissedAndHavingDrivesAndNotRecording() { + #expect(RecordButtonTip().rules.count == 3) + } + + // MARK: - Parameters + + @Test + func hasDrivesDefaultsToFalse() { + #expect(RecordButtonTip.hasDrives == false) + } @Test func isOnboardingPresentedDefaultsToTrue() { #expect(RecordButtonTip.isOnboardingPresented == true) } + // MARK: - isRecording parameter + + @Test + func isRecordingDefaultsToFalse() { + #expect(RecordButtonTip.isRecording == false) + } + @Test - func isOnboardingPresentedCanBeSetToFalse() { - RecordButtonTip.isOnboardingPresented = false - defer { RecordButtonTip.isOnboardingPresented = true } - #expect(RecordButtonTip.isOnboardingPresented == false) + func isRecordingCanBeSetToTrue() { + RecordButtonTip.isRecording = true + defer { RecordButtonTip.isRecording = false } + #expect(RecordButtonTip.isRecording == true) } } diff --git a/DrivelineTests/UITests/StatsPanelTipTests.swift b/DrivelineTests/UITests/StatsPanelTipTests.swift index e342254..6931a08 100644 --- a/DrivelineTests/UITests/StatsPanelTipTests.swift +++ b/DrivelineTests/UITests/StatsPanelTipTests.swift @@ -12,6 +12,13 @@ import Testing @MainActor struct StatsPanelTipTests { + // MARK: - Rules + + @Test + func gatesOnDriveCountRecordingAndOnboarding() { + #expect(StatsPanelTip().rules.count == 3) + } + // MARK: - driveCount parameter @Test diff --git a/DrivelineUITests/TipUITests.swift b/DrivelineUITests/TipUITests.swift index fdf3fca..e2157c4 100644 --- a/DrivelineUITests/TipUITests.swift +++ b/DrivelineUITests/TipUITests.swift @@ -10,54 +10,67 @@ import XCTest final class TipUITests: BaseXCTestCase { // MARK: - Lifecycle - + override func setUp() async throws { enableTips() try await super.setUp() } - + // MARK: - Tests @MainActor - func testRecordButtonTipAppearsOnFirstLaunch() throws { - XCTAssertTrue(app.staticTexts["Record a Drive"].waitForExistence(timeout: 3)) + func testRecordButtonTipHiddenInEmptyState() throws { + // With no drives recorded the home screen shows the empty state, where the + // record-button tip must stay hidden (the empty state has its own CTA). + XCTAssertFalse(app.staticTexts["Record a Drive"].waitForExistence(timeout: 3)) + } + + @MainActor + func testRecordButtonTipAppearsAfterFirstDrive() throws { + navigateToHomeScreen() + + XCTAssertTrue(app.staticTexts["Record a Drive"].waitForExistence(timeout: 5)) XCTAssertTrue(app.staticTexts["Tap to start manually tracking a new journey."].exists) } @MainActor func testStatsPanelTipAppearsAfterThreeDrivesRecorded() throws { - closeRecordButtonTip() navigatePastEmptyState() - XCTAssertTrue(app.staticTexts["RecordingBanner"].waitForExistence(timeout: 5)) app.buttons["FinishDriveButton"].tap() - + + // The record-button tip appears once the first drive exists; dismiss it so the + // popover doesn't intercept the next record taps. + dismissRecordButtonTipIfPresent() + app.buttons["NewDriveButton"].tap() XCTAssertTrue(app.staticTexts["RecordingBanner"].waitForExistence(timeout: 5)) app.buttons["FinishDriveButton"].tap() - + app.buttons["NewDriveButton"].tap() XCTAssertTrue(app.staticTexts["RecordingBanner"].waitForExistence(timeout: 5)) app.buttons["FinishDriveButton"].tap() - + XCTAssertTrue(app.staticTexts["Switch Stats View"].waitForExistence(timeout: 5)) XCTAssertTrue(app.staticTexts["Tap to toggle between the last 30 days and all time."].waitForExistence(timeout: 5)) } @MainActor func testEditDriveTipAppearsOnDriveDetail() throws { - closeRecordButtonTip() navigateToHomeScreen() + dismissRecordButtonTipIfPresent() app.buttons["Drive row 0"].tap() XCTAssertTrue(app.staticTexts["Edit Your Drive"].waitForExistence(timeout: 5)) XCTAssertTrue(app.staticTexts["Tap the options button to edit the name and other details."].exists) } - + // MARK: - Private - - private func closeRecordButtonTip() { - app.buttons["Close"].tap() + + private func dismissRecordButtonTipIfPresent() { + if app.staticTexts["Record a Drive"].waitForExistence(timeout: 5) { + app.buttons["Close"].tap() + } } }