Как локализовать международный адрес, особенно проезжая часть и подъездная дорога
Я работаю над приложением, которое активно использует адреса в разных странах. У нас есть несколько способов их ввода: от импорта адресов до добавления меток на карту и обратного геокодирования нашего текущего местоположения.
Мой текущий проект - правильно отформатировать международный адрес:
В США:
18 Street Name
В Норвегии:
Street Name 18
Я придумал несколько способов создать CNMutablePostalAddress
с CLPlacemark
Чтобы получить довольно хорошие результаты, у меня возникла проблема.
Я хочу, чтобы только название улицы и номер возвращались в виде однострочной строки: Итак:
street1: placemarker.thoroughfare, (street name)
street2: placemarker.subThoroughfare (street number),
но CNPostalAddress имеет только один street
свойство, поэтому для его использования вам нужно сделать что-то вроде этого:
cNPostalAddress.street = placemarker.subThoroughfare + " " + placemarker.thoroughfare
Это не сработает для таких стран, как Норвегия, где их почитают.
Вы можете взломать его и использовать первую строчку отформатированного адреса:
CNPostalAddressFormatter.string(from: placemarker.mailingAddress , style: .mailingAddress)
но это супер хакерство, и я уверен, что это сломается со странами, которые упорядочивают свои почтовые адреса иначе, чем в Японии.
На данный момент я даже не могу найти ресурсы, которые подскажут, в каких странах subThoroughfare
а также thoroughfare
, потому что если бы у меня был такой список, я мог бы просто отменить его вручную.
Вот пример кода того, что мне удалось до сих пор:
static func mulitLineAddress(from placemarker: CLPlacemark, detail: AddressDetail) -> String {
let address = MailingAddress(
street1: placemarker.thoroughfare,
street2: placemarker.subThoroughfare,
city: placemarker.locality,
state: placemarker.administrativeArea,
postalCode: placemarker.postalCode,
countryCode: placemarker.country)
return self.mulitLineAddress(from: address, detail: detail)
}
static func mulitLineAddress(from mailingAddress: MailingAddress, detail: AddressDetail) -> String {
let address = CNMutablePostalAddress()
let street1 = mailingAddress.street1 ?? ""
let street2 = mailingAddress.street2 ?? ""
let streetSpacing = street1.isEmpty && street2.isEmpty ? "" : " "
let streetFull = street1 + streetSpacing + street2
switch detail {
case .street1:
address.street = street1
case .street2:
address.street = street2
case .streetFull:
address.street = streetFull
case .full:
address.country = mailingAddress.countryCode ?? ""
fallthrough
case .withoutCountry:
address.street = streetFull
address.city = mailingAddress.city ?? ""
address.state = mailingAddress.state ?? ""
address.postalCode = mailingAddress.postalCode ?? ""
}
return CNPostalAddressFormatter.string(from: address, style: .mailingAddress)
}
Есть идеи? Даже такие ресурсы, как список стран, которые меняют направлениеstreet1
а также street2
было бы полезно.
1 ответ
В итоге я выбрал гибридный подход.
Для CLPlacemark
к CNMutablePostalAddress()
это довольно прямолинейно:
cnPostalAddress.street = placemarker.postalAddress?.street
Однако это не работает ни с одним другим методом ввода и не может быть изменено на формат, отличный от формата CNMutablePostalAddress()
При получении адресной информации из других источников мне нужно было сделать это вручную, вот небольшой пример, который работает с несколькими странами:
static private func generateLocalizedStreetAddress(from adderss: MailingAddress) -> String {
guard adderss.localizedStreet.isEmpty else { return adderss.localizedStreet ?? "" }
let country = CountryCode.country(for: adderss.countryCode)
let thoroughfare = adderss.thoroughfare ?? ""
let subThoroughfare = adderss.subThoroughfare ?? ""
let delimiter = self.generateDelimiter(from: thoroughfare, and: subThoroughfare, with: country)
switch country {
case .belgium, .czechRepublic, .denmark, .finland, .germany, .latvia, .netherlands, .norway, .poland, .portugal, .sweden:
return thoroughfare + delimiter + subThoroughfare
default:
return subThoroughfare + delimiter + thoroughfare
}
}
static private func generateDelimiter(from thoroughfare: String, and subThoroughfare: String, with country: Country) -> String {
guard !thoroughfare.isEmpty && !subThoroughfare.isEmpty else { return "" }
switch country {
case .spain:
return ", "
default:
return " "
}
}
CNPostalAddressFormatter
имеет интересный API для получения
NSAttributedString
где каждый компонент адреса идентифицирован в нем. Вы можете использовать это, чтобы получить только ту информацию, которую вы хотите, например, улицу, которая включает в себя правильно локализованные подмостки и проезды, независимо от того, где они могут находиться в почтовом адресе.
let addressFormatter = CNPostalAddressFormatter()
let attributedAddress = addressFormatter.attributedString(from: postalAddress, withDefaultAttributes: [:])
let nsString = attributedAddress.string as NSString
let range = NSRange(location: 0, length: nsString.length)
var street: String?
attributedAddress.enumerateAttributes(in: range, options: []) { result, range, stop in
if let component = result[NSAttributedString.Key(CNPostalAddressPropertyAttribute)] as? String {
if component == CNPostalAddressStreetKey {
street = nsString.substring(with: range)
stop.pointee = true
}
}
}
Я также отправил отзыв в Apple о добавлении более мощного и гибкого API: FB8648023 Template API, желаемый для форматирования адресов с указанными компонентами, подобными
DateFormatter.setLocalizedDateFormatFromTemplate