From d602c061b42b9337af85d60e7cc728b5f83a9e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Fri, 21 Jul 2017 18:35:55 -0700 Subject: [PATCH] Suppress freeway names in post-maneuver instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored the post-maneuver instruction generation code to account for the road class and moved the code to an extension on RouteStep. Consistently mark up road names and route numbers as addresses when either (but not both) is available on the step in question. In the rare event that a motorway has no name or number, deliver a post-maneuver instruction without the “on [road name]” prepositional phrase. --- .../Resources/Base.lproj/Localizable.strings | 7 ++- .../Resources/ca.lproj/Localizable.strings | 4 +- .../Resources/es.lproj/Localizable.strings | 4 +- .../Resources/fr.lproj/Localizable.strings | 4 +- .../Resources/hu.lproj/Localizable.strings | 4 +- .../Resources/lt.lproj/Localizable.strings | 4 +- .../Resources/sv.lproj/Localizable.strings | 4 +- .../Resources/vi.lproj/Localizable.strings | 4 +- .../zh-Hans.lproj/Localizable.strings | 4 +- .../RouteManeuverViewController.swift | 3 +- MapboxNavigation/RouteStepFormatter.swift | 44 +++++++++++++++++++ MapboxNavigation/RouteVoiceController.swift | 29 ++++-------- 12 files changed, 74 insertions(+), 41 deletions(-) diff --git a/MapboxNavigation/Resources/Base.lproj/Localizable.strings b/MapboxNavigation/Resources/Base.lproj/Localizable.strings index 6139fadf79e..dc921028716 100644 --- a/MapboxNavigation/Resources/Base.lproj/Localizable.strings +++ b/MapboxNavigation/Resources/Base.lproj/Localizable.strings @@ -1,5 +1,8 @@ -/* Format for speech string; 1 = way name; 2 = distance */ -"CONTINUE" = "Continue on %1$@ for %2$@"; +/* Format for speech string after completing a maneuver and starting a new step; 1 = distance */ +"CONTINUE" = "Continue for %@"; + +/* Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance */ +"CONTINUE_ON_ROAD" = "Continue on %1$@ for %2$@"; /* Format string for less than; 1 = duration remaining */ "LESS_THAN" = "<%@"; diff --git a/MapboxNavigation/Resources/ca.lproj/Localizable.strings b/MapboxNavigation/Resources/ca.lproj/Localizable.strings index ef07c081fe7..25db9925ffc 100644 --- a/MapboxNavigation/Resources/ca.lproj/Localizable.strings +++ b/MapboxNavigation/Resources/ca.lproj/Localizable.strings @@ -1,5 +1,5 @@ -/* Format for speech string; 1 = way name; 2 = distance */ -"CONTINUE" = "Continua per %1$@ cap a %2$@"; +/* Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance */ +"CONTINUE_ON_ROAD" = "Continua per %1$@ cap a %2$@"; /* Format string for less than; 1 = duration remaining */ "LESS_THAN" = "<%@"; diff --git a/MapboxNavigation/Resources/es.lproj/Localizable.strings b/MapboxNavigation/Resources/es.lproj/Localizable.strings index cf8918f5fc9..3eeb74c65fb 100644 --- a/MapboxNavigation/Resources/es.lproj/Localizable.strings +++ b/MapboxNavigation/Resources/es.lproj/Localizable.strings @@ -1,5 +1,5 @@ -/* Format for speech string; 1 = way name; 2 = distance */ -"CONTINUE" = "Continúe en %1$@ por %2$@"; +/* Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance */ +"CONTINUE_ON_ROAD" = "Continúe en %1$@ por %2$@"; /* Format string for less than; 1 = duration remaining */ "LESS_THAN" = "<%@"; diff --git a/MapboxNavigation/Resources/fr.lproj/Localizable.strings b/MapboxNavigation/Resources/fr.lproj/Localizable.strings index 331e65d5bb6..f487bfbabe6 100644 --- a/MapboxNavigation/Resources/fr.lproj/Localizable.strings +++ b/MapboxNavigation/Resources/fr.lproj/Localizable.strings @@ -1,5 +1,5 @@ -/* Format for speech string; 1 = way name; 2 = distance */ -"CONTINUE" = "Continuez sur %1$@ pendant %2$@"; +/* Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance */ +"CONTINUE_ON_ROAD" = "Continuez sur %1$@ pendant %2$@"; /* Format string for less than; 1 = duration remaining */ "LESS_THAN" = "<%@"; diff --git a/MapboxNavigation/Resources/hu.lproj/Localizable.strings b/MapboxNavigation/Resources/hu.lproj/Localizable.strings index aede3a59cd1..2872556a526 100644 --- a/MapboxNavigation/Resources/hu.lproj/Localizable.strings +++ b/MapboxNavigation/Resources/hu.lproj/Localizable.strings @@ -1,5 +1,5 @@ -/* Format for speech string; 1 = way name; 2 = distance */ -"CONTINUE" = "Continue on %1$@ for %2$@"; +/* Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance */ +"CONTINUE_ON_ROAD" = "Continue on %1$@ for %2$@"; /* Format string for less than; 1 = duration remaining */ "LESS_THAN" = "<%@"; diff --git a/MapboxNavigation/Resources/lt.lproj/Localizable.strings b/MapboxNavigation/Resources/lt.lproj/Localizable.strings index 778c50c9c84..d57a883e3ad 100644 --- a/MapboxNavigation/Resources/lt.lproj/Localizable.strings +++ b/MapboxNavigation/Resources/lt.lproj/Localizable.strings @@ -1,5 +1,5 @@ -/* Format for speech string; 1 = way name; 2 = distance */ -"CONTINUE" = "Tęskite %1$@ %2$@"; +/* Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance */ +"CONTINUE_ON_ROAD" = "Tęskite %1$@ %2$@"; /* Format string for less than; 1 = duration remaining */ "LESS_THAN" = "iki %@"; diff --git a/MapboxNavigation/Resources/sv.lproj/Localizable.strings b/MapboxNavigation/Resources/sv.lproj/Localizable.strings index f97c44fc961..810b852203c 100644 --- a/MapboxNavigation/Resources/sv.lproj/Localizable.strings +++ b/MapboxNavigation/Resources/sv.lproj/Localizable.strings @@ -1,5 +1,5 @@ -/* Format for speech string; 1 = way name; 2 = distance */ -"CONTINUE" = "Fortsätt på %1$@ i %2$@"; +/* Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance */ +"CONTINUE_ON_ROAD" = "Fortsätt på %1$@ i %2$@"; /* Format string for less than; 1 = duration remaining */ "LESS_THAN" = "<%@"; diff --git a/MapboxNavigation/Resources/vi.lproj/Localizable.strings b/MapboxNavigation/Resources/vi.lproj/Localizable.strings index 6945e488ca3..0aa305822ce 100644 --- a/MapboxNavigation/Resources/vi.lproj/Localizable.strings +++ b/MapboxNavigation/Resources/vi.lproj/Localizable.strings @@ -1,5 +1,5 @@ -/* Format for speech string; 1 = way name; 2 = distance */ -"CONTINUE" = "Chạy tiếp trên %1$@ cho %2$@"; +/* Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance */ +"CONTINUE_ON_ROAD" = "Chạy tiếp trên %1$@ cho %2$@"; /* Format string for less than; 1 = duration remaining */ "LESS_THAN" = "<%@"; diff --git a/MapboxNavigation/Resources/zh-Hans.lproj/Localizable.strings b/MapboxNavigation/Resources/zh-Hans.lproj/Localizable.strings index 5dc34570f18..d735d09028b 100644 --- a/MapboxNavigation/Resources/zh-Hans.lproj/Localizable.strings +++ b/MapboxNavigation/Resources/zh-Hans.lproj/Localizable.strings @@ -1,5 +1,5 @@ -/* Format for speech string; 1 = way name; 2 = distance */ -"CONTINUE" = "继续沿%@行驶%@"; +/* Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance */ +"CONTINUE_ON_ROAD" = "继续沿%@行驶%@"; /* Format string for less than; 1 = duration remaining */ "LESS_THAN" = "<%@"; diff --git a/MapboxNavigation/RouteManeuverViewController.swift b/MapboxNavigation/RouteManeuverViewController.swift index 168b4e6aed3..60f93eb0425 100644 --- a/MapboxNavigation/RouteManeuverViewController.swift +++ b/MapboxNavigation/RouteManeuverViewController.swift @@ -170,8 +170,7 @@ class RouteManeuverViewController: UIViewController { } func updateStreetNameForStep() { - let isMotorway = step?.intersections?.first?.outletRoadClasses?.contains(.motorway) ?? false - if isMotorway, let codes = step?.codes, let digitRange = codes.first?.rangeOfCharacter(from: .decimalDigits), !digitRange.isEmpty { + if let step = step, step.isNumberedMotorway, let codes = step.codes { destinationLabel.unabridgedText = codes.joined(separator: NSLocalizedString("REF_DELIMITER", bundle: .mapboxNavigation, value: " / ", comment: "Delimiter between route numbers in a road concurrency")) } else if let name = step?.names?.first { destinationLabel.unabridgedText = name diff --git a/MapboxNavigation/RouteStepFormatter.swift b/MapboxNavigation/RouteStepFormatter.swift index c4767bf0fa3..1e58f2ff3c5 100644 --- a/MapboxNavigation/RouteStepFormatter.swift +++ b/MapboxNavigation/RouteStepFormatter.swift @@ -38,3 +38,47 @@ public class RouteStepFormatter: Formatter { return false } } + +extension RouteStep { + /** + Returns true if the route travels on a motorway primarily identified by a route number rather than a road name. + */ + var isNumberedMotorway: Bool { + guard intersections?.first?.outletRoadClasses?.contains(.motorway) == true else { + return false + } + guard let codes = codes, let digitRange = codes.first?.rangeOfCharacter(from: .decimalDigits) else { + return false + } + return !digitRange.isEmpty + } + + /** + Returns a string describing the step’s road by its name, route number, or both, depending on the kind of road. + + - parameter markedUpWithSSML: True to wrap the name and route number in SSML tags that cause them to be read as addresses. + - returns: A string describing the step’s road, or `nil` if the step lacks the information needed to describe the step. + */ + func roadDescription(markedUpWithSSML: Bool) -> String? { + let addressSSML = { (text: String?) -> String? in + guard let text = text else { + return nil + } + return markedUpWithSSML ? "\(text.addingXMLEscapes)" : text + } + + let nameSSML = addressSSML(names?.first) + let codeSSML = addressSSML(codes?.first) + + if let codeSSML = codeSSML, nameSSML == nil || isNumberedMotorway { + return codeSSML + } else if let nameSSML = nameSSML { + if let codeSSML = codeSSML { + return String.localizedStringWithFormat(NSLocalizedString("NAME_AND_REF", bundle: .mapboxNavigation, value: "%@ (%@)", comment: "Format for speech string; 1 = way name; 2 = way route number"), nameSSML, codeSSML) + } else { + return nameSSML + } + } + return nil + } +} diff --git a/MapboxNavigation/RouteVoiceController.swift b/MapboxNavigation/RouteVoiceController.swift index 52720f19575..f6c8dfd469a 100644 --- a/MapboxNavigation/RouteVoiceController.swift +++ b/MapboxNavigation/RouteVoiceController.swift @@ -226,11 +226,17 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate, AVAudioP if routeProgress.currentLegProgress.currentStep.maneuverType == .depart && alertLevel == .depart { if userDistance < minimumDistanceForHighAlert { text = String.localizedStringWithFormat(NSLocalizedString("LINKED_WITH_DISTANCE_UTTERANCE_FORMAT", bundle: .mapboxNavigation, value: "%@, then in %@, %@", comment: "Format for speech string; 1 = current instruction; 2 = formatted distance to the following linked instruction; 3 = that linked instruction"), currentInstruction!, escapeIfNecessary(maneuverVoiceDistanceFormatter.string(from: userDistance)), upComingInstruction) + } else if let roadDescription = step.roadDescription(markedUpWithSSML: markUpWithSSML) { + text = String.localizedStringWithFormat(NSLocalizedString("CONTINUE_ON_ROAD", bundle: .mapboxNavigation, value: "Continue on %@ for %@", comment: "Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance"), roadDescription, escapeIfNecessary(maneuverVoiceDistanceFormatter.string(from: userDistance))) } else { - text = String.localizedStringWithFormat(NSLocalizedString("CONTINUE", bundle: .mapboxNavigation, value: "Continue on %@ for %@", comment: "Format for speech string; 1 = way name; 2 = distance"), localizeRoadDescription(step, markUpWithSSML: markUpWithSSML), escapeIfNecessary(maneuverVoiceDistanceFormatter.string(from: userDistance))) + text = String.localizedStringWithFormat(NSLocalizedString("CONTINUE", bundle: .mapboxNavigation, value: "Continue for %@", comment: "Format for speech string after completing a maneuver and starting a new step; 1 = distance"), escapeIfNecessary(maneuverVoiceDistanceFormatter.string(from: userDistance))) } } else if routeProgress.currentLegProgress.currentStep.distance > 2_000 && routeProgress.currentLegProgress.alertUserLevel == .low { - text = String.localizedStringWithFormat(NSLocalizedString("CONTINUE", bundle: .mapboxNavigation, value: "Continue on %@ for %@", comment: "Format for speech string; 1 = way name; 2 = distance"), localizeRoadDescription(step, markUpWithSSML: markUpWithSSML), escapeIfNecessary(maneuverVoiceDistanceFormatter.string(from: userDistance))) + if let roadDescription = step.roadDescription(markedUpWithSSML: markUpWithSSML) { + text = String.localizedStringWithFormat(NSLocalizedString("CONTINUE_ON_ROAD", bundle: .mapboxNavigation, value: "Continue on %@ for %@", comment: "Format for speech string after completing a maneuver and starting a new step; 1 = way name; 2 = distance"), roadDescription, escapeIfNecessary(maneuverVoiceDistanceFormatter.string(from: userDistance))) + } else { + text = String.localizedStringWithFormat(NSLocalizedString("CONTINUE", bundle: .mapboxNavigation, value: "Continue for %@", comment: "Format for speech string after completing a maneuver and starting a new step; 1 = distance"), escapeIfNecessary(maneuverVoiceDistanceFormatter.string(from: userDistance))) + } } else if alertLevel == .high && stepDistance < minimumDistanceForHighAlert { text = String.localizedStringWithFormat(NSLocalizedString("LINKED_UTTERANCE_FORMAT", bundle: .mapboxNavigation, value: "%@, then %@", comment: "Format for speech string; 1 = current instruction; 2 = the following linked instruction"), upComingInstruction, followOnInstruction) } else if alertLevel != .high { @@ -242,25 +248,6 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate, AVAudioP return text } - func localizeRoadDescription(_ step: RouteStep, markUpWithSSML: Bool) -> String { - var road = "" - let escapeIfNecessary = {(distance: String) -> String in - return markUpWithSSML ? distance.addingXMLEscapes : distance - } - if let name = step.names?.first { - if let code = step.codes?.first { - let markedUpName = markUpWithSSML ? "\(name.addingXMLEscapes)" : name - let markedUpCode = markUpWithSSML ? "\(code.addingXMLEscapes)" : code - road = String.localizedStringWithFormat(NSLocalizedString("NAME_AND_REF", bundle: .mapboxNavigation, value: "%@ (%@)", comment: "Format for speech string; 1 = way name; 2 = way route number"), markedUpName, markedUpCode) - } else { - road = escapeIfNecessary(name) - } - } else if let code = step.codes?.first { - road = escapeIfNecessary(code) - } - return road - } - func speak(_ text: String, error: String? = nil) { // Note why it failed if let error = error {