Locale — е клас Foundation в iOS и macOS, който капсулира езиковите и културните конвенции на потребителя: формат на дати, числа, валути и мерни единици. Според Apple Developer Documentation, 2024, Locale определя как DateFormatter показва месеца (януари или January), десетичния разделител в числото (запетая или точка) и символа на валутата (лев, долар или евро). Всеки екземпляр на Locale е обвързан с идентификатор от вида ru_RU или en_US, където първата част е кодът на езика (ISO 639-1), а втората — кодът на региона (ISO 3166-1). За разлика от TimeZone, Locale не влияе на абсолютната стойност на времето, а само на неговото текстово представяне.
Основни точки
Locale — е value-тип в Swift (NSLocale в Objective-C), който представлява набор от правила за форматиране, характерни за конкретен език и регион. За разлика от TimeZone, който определя абсолютното отместване на времето, Locale определя как времето, числата и валутата изглеждат в текстово представяне. Същата дата 2024-07-21 ще бъде показана като „21 юли 2024 г.„ за ru_RU и „July 21, 2024„ за en_US.
Всеки екземпляр на Locale се състои от два компонента: Language (език — определя имената на месеците, дните от седмицата, реда на думите) и Region (регион — определя формата на числата, валутата, календара). Комбинацията от тези компоненти се кодира в идентификатора според стандарта BCP 47: ru_RU (руски език, Русия), en_US (английски, САЩ), de_DE (немски, Германия).
Според Unicode CLDR (2024) списъкът на поддържаните локализации в iOS надвишава 700 комбинации език-регион. Foundation използва данни от CLDR (Common Locale Data Repository) — най-пълното хранилище за локализационни данни, поддържано от Unicode Consortium. Това гарантира еднообразно форматиране на всички Apple устройства.
DateFormatter използва Locale за избор на правилните имена на месеците и дните от седмицата, определяне на реда на компонентите на датата (ден/месец/година или месец/ден/година) и разделителите. Без изрично посочване на Locale, DateFormatter използва локала на устройството — това е правилно за UI, но опасно за сървърни данни, където форматът трябва да бъде фиксиран.
| Компонент | ru_RU | en_US | de_DE |
|---|---|---|---|
| Дата (medium) | 21 юли 2024 г. | Jul 21, 2024 | 21.07.2024 |
| Число (1000.5) | 1 000,5 | 1,000.5 | 1.000,5 |
| Валута (100) | 100,00 ₽ | $100.00 | 100,00 € |
| Календар | Григориански | Григориански | Григориански |
| Разделител на списък | ; | , | ; |
NumberFormatter използва Locale за определяне на десетичния разделител (запетая или точка), разделителя за групиране (интервал, запетая, точка) и символа на валутата. Игнорирането на Locale при парсване на числа е една от честите причини за грешки в международни приложения: числото „1,5„ означава „един и половина„ за ru_RU, но за en_US числовият анализатор ще го прочете като „пет„ след запетаята.
Важно: Calendar, създаден чрез Calendar.current, наследява локала на устройството. Calendar(identifier: .gregorian) с изрично зададен locale — препоръчителният начин за предвидимо форматиране. При работа с ISO 8601 дати винаги използвайте Locale(identifier: "en_US_POSIX") — специален локал за техническо форматиране, който не е подложен на влияние от регионални настройки.
Идентификаторът на Locale се състои от код на езика (ISO 639-1, два знака) и код на региона (ISO 3166-1, два знака), разделени с долна черта. Примери: ru_RU, en_US, fr_FR, zh_Hans_CN (китайски, опростено писмо, Китай). Foundation също поддържа идентификатори във формат BCP 47: ru-RU, en-US, използвани в уеб стандартите.
Освен пълните идентификатори, Locale може да бъде създаден само на базата на език: Locale(identifier: "ru") връща локал с руски език и регион по подразбиране за този език (обикновено Русия). Подобно за английски: Locale(identifier: "en") използва региона на САЩ. Този подход е полезен за задаване на език за форматиране без обвързване с конкретен регион.
Специални локали включват en_US_POSIX — технически локал за машинно форматиране на дати и числа, който гарантира стабилен формат независимо от потребителските настройки. Този локал е задължителен за парсване на дати от сървърни API, особено за формат ISO 8601. Използва григориански календар, 24-часов формат на времето и точка като десетичен разделител.
import Foundation
// Налични идентификатори на локали
let available: [String] = Locale.availableIdentifiers
print("Общо локали: \(available.count)")
// Филтриране на руски локали
let russianLocales = available.filter { $0.hasPrefix("ru") }
print("Руски локали: \(russianLocales)")
// Компоненти на локала
let locale = Locale(identifier: "de_DE")
print("Език: \(locale.languageCode ?? "nil")")
print("Регион: \(locale.regionCode ?? "nil")")
print("Валута: \(locale.currencyCode ?? "nil")")
print("Календар: \(locale.calendar.identifier)")
Проверка на наличните локали чрез Locale.availableIdentifiers връща масив от всички идентификатори, поддържани от текущата версия на iOS. За филтриране по регион използвайте Locale.availableIdentifiers.filter с проверка на regionCode. Това е полезно за изграждане на UI за избор на регион без твърдо кодиран списък.
Locale.current — основният начин за получаване на текущия локал на устройството, зададен от потребителя в настройките на iOS (Settings > General > Language & Region). Това свойство се актуализира автоматично при промяна на езика или региона в настройките без рестартиране на приложението. Въпреки това, то може да не съвпада с предпочитания локал за показване на съдържание: потребителят може да зададе езика на интерфейса на английски, но да преглежда дати в руски формат.
За по-точно определяне на предпочитанията на потребителя използвайте Locale.preferredLanguages — масив от езици, сортирани по приоритет на потребителя. Първият елемент е основният език на интерфейса. Този списък съответства на настройките в Language & Region, включително влаченето на езици в ред на предпочитание. Комуникационните приложения (месинджъри, имейл клиенти) трябва да вземат предвид този ред при избора на език за показване на съдържание.
import Foundation
// Текущ системен локал
let current = Locale.current
print("Текущ локал: \(current.identifier)")
print("Език: \(current.language?.disjointName ?? "nil")")
// Предпочитани езици на потребителя
let preferred = Locale.preferredLanguages
print("Предпочитани езици: \(preferred)")
// Вземи регион от текущия локал
if let region = current.regionCode {
let regionLocale = Locale(identifier: "en_\(region)")
let countryName = regionLocale.localizedString(
forRegionCode: region
)
print("Държава: \(countryName ?? region)")
}
// Проверка на 24ч формат
let uses24h = current.uses24hClock(
for: .dateAndTime
)
print("Използва 24ч: \(uses24h)")
Локализация в UI: за показване на имената на месеците и дните от седмицата на езика на интерфейса използвайте Calendar със зададен locale. Calendar.current.symbols(for: .month) връща имената на месеците на езика на текущия локал. За показване на имената на държави на езика на потребителя използвайте Locale.current.localizedString(forRegionCode:).
Форматиране на дата с отчитане на локала — ключова задача при показване на дати на потребителя. DateFormatter със зададен locale автоматично избира правилния формат на дата и час за региона на потребителя. За dateStyle и timeStyle със стойности .short, .medium, .long, .full форматиращият използва правилата на локала за композиране на компонентите на датата.
import Foundation
let date = Date()
// Форматиране с различни локали
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.locale = Locale(identifier: "ru_RU")
print("Руски: \(formatter.string(from: date))")
formatter.locale = Locale(identifier: "en_US")
print("Английски: \(formatter.string(from: date))")
formatter.locale = Locale(identifier: "ja_JP")
print("Японски: \(formatter.string(from: date))")
// Форматиране на валута с локал
let numFormatter = NumberFormatter()
numFormatter.numberStyle = .currency
numFormatter.locale = Locale(identifier: "de_DE")
print("Германска валута: \(numFormatter.string(from: 1234.56) ?? "nil")")
numFormatter.locale = Locale(identifier: "en_US")
print("Американска валута: \(numFormatter.string(from: 1234.56) ?? "nil")")
Парсване на дати от сървърни API винаги трябва да използва Locale(identifier: "en_US_POSIX") за фиксиран формат. Сървърите обикновено изпращат дати във формат ISO 8601 с английски имена на месеци и използването на текущия локал на устройството може да доведе до грешка при парсване, ако потребителят се намира в регион с неанглийски език. en_US_POSIX гарантира, че парсването не зависи от настройките на устройството.
import Foundation
// Правилно парсване на сървърни дати
let isoFormatter = DateFormatter()
isoFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
isoFormatter.locale = Locale(identifier: "en_US_POSIX")
isoFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let serverDate = "2024-07-21T14:30:00+0000"
if let parsed = isoFormatter.date(from: serverDate) {
print("Парсвана дата: \(parsed)")
}
// Локализирано име на валута
let usLocale = Locale(identifier: "en_US")
let currencyName = usLocale.localizedString(
forCurrencyCode: "RUB"
)
print("Руска рубла в американски локал: \(currencyName ?? "nil")")
Допълнителни възможности: Locale предоставя локализирано описание на своите компоненти чрез методите localizedString(forRegionCode:), localizedString(forLanguageCode:), localizedString(forCurrencyCode:) и localizedString(forCalendarIdentifier:). Тези методи връщат имена на езика на локала, в който са извикани. Например Locale(identifier: "ru_RU").localizedString(forCountryCode: "DE") ще върне „Германия„.
Игнориране на Locale при парсване на числа — критична грешка в международни приложения. NumberFormatter без изричен локал използва текущия локал на устройството. Ако потребител в Русия въведе „1,5„, NumberFormatter.number(from: "1,5") правилно връща 1.5. Но ако същият код се изпълнява на устройство с en_US локал, парсването връща nil, защото за en_US десетичният разделител е точка.
Липса на en_US_POSIX за сървърни дати води до трудно откриваеми грешки. DateFormatter с dateFormat и locale = Locale.current може да се счупи при потребители от региони, където форматът на датата се различава от американския. Например в Германия DateFormatter може да очаква „21.07.2024„, докато сървърът изпраща „07/21/2024„. en_US_POSIX гарантира фиксиран формат за машинно парсване независимо от региона на потребителя.
Сравняване на текстови представяния на дати вместо използване на Date — друга често срещана грешка. Разработчиците понякога сравняват текстови представяния на дати от различни локали, получавайки неверни резултати. Locale променя само показването, но не и абсолютната стойност на датата. Винаги сравнявайте Date обекти, а не техните текстови представяния. За сравняване на компоненти на дати използвайте Calendar с изрично зададен locale.
Според WWDC 2023, около 30% от проблемите с интернационализацията в приложенията са свързани с неправилна конфигурация на Locale. Apple препоръчва винаги изрично да задавате locale за DateFormatter и NumberFormatter при работа със сървърни данни и да използвате Locale.current само за показване в UI. Тази проста практика елиминира повечето грешки, свързани с регионални настройки.
Често задавани въпроси
Locale — клас Foundation, представляващ културни и езикови правила за форматиране: формат на дата, числа, валута и мерни единици. Използва се заедно с DateFormatter, NumberFormatter и Calendar за локализирано показване на данни.
Locale определя формата на показване (език, регионални конвенции), докато TimeZone определя абсолютното отместване на времето спрямо UTC. Locale влияе на текстовото представяне, TimeZone — на числовата стойност на времето. И двете се използват заедно за пълно форматиране на дата.
en_US_POSIX — специален локал за техническо форматиране, който гарантира стабилен формат независимо от потребителските настройки. Задължителен за парсване на сървърни дати (ISO 8601) и работа с API, където форматът трябва да бъде предвидим.
Locale.availableIdentifiers връща масив от низове с идентификатори на всички поддържани локали. За филтриране по език използвайте filter с hasPrefix, за получаване на регион — Locale(identifier:).regionCode.
NumberFormatter използва Locale за определяне на десетичния разделител (запетая или точка), символа на валутата и разделителя за групиране. За фиксиран формат задайте locale на en_US_POSIX или изрично посочете свойствата на форматиращия.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също