 // Will automatically read localized Instructions.plist
 let OSRMTextInstructionsStrings = NSDictionary(contentsOfFile: Bundle(for: OSRMInstructionFormatter.self).path(forResource: "Instructions", ofType: "plist")!)!
 
-extension String {
-    public var sentenceCased: String {
-        return String(characters.prefix(1)).uppercased() + String(characters.dropFirst())
-    }
+protocol Tokenized {
+    associatedtype T
     
     /**
      Replaces `{tokens}` in the receiver using the given closure.
      */
+    func replacingTokens(using interpolator: ((TokenType) -> T)) -> T
+}
+
+extension String: Tokenized {
+    public var sentenceCased: String {
+        return String(characters.prefix(1)).uppercased() + String(characters.dropFirst())
+    }
+    
     public func replacingTokens(using interpolator: ((TokenType) -> String)) -> String {
         let scanner = Scanner(string: self)
         scanner.charactersToBeSkipped = nil

            let isMotorway = roadClasses?.contains(.motorway) ?? false
            
            if let name = name, let ref = ref, name != ref, !isMotorway {
                wayName = phrase(named: .nameWithCode).replacingTokens(using: { (tokenType) -> String in
                    switch tokenType {
                    case .wayName:
                        return modifyValueByKey?(.wayName, name) ?? name
                    case .code:
                        return modifyValueByKey?(.code, ref) ?? ref
                    default:
                        fatalError("Unexpected token type \(tokenType) in name-and-ref phrase")
                    }
                })
            } else if let ref = ref, isMotorway, let decimalRange = ref.rangeOfCharacter(from: .decimalDigits), !decimalRange.isEmpty {
                wayName = modifyValueByKey != nil ? "\(modifyValueByKey!(.code, ref))" : ref
            } else if name == nil, let ref = ref {
                wayName = modifyValueByKey != nil ? "\(modifyValueByKey!(.code, ref))" : ref
            } else {
                wayName = name != nil ? modifyValueByKey != nil ? "\(modifyValueByKey!(.wayName, name!))" : name! : ""
            }
        }


            instruction = obj
        } else if let _ = step.exitCodes?.first, let obj = instructionObject["exit"] {
            instruction = obj
        } else if !wayName.string.isEmpty, let obj = instructionObject["name"] {
            instruction = obj
        } else {
            instruction = instructionObject["default"]!
