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)")
}
// Провери 24h формат
let uses24h = current.uses24hClock(
for: .dateAndTime
)
print("Користи 24h: \(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 враћа низ strings са идентификаторима свих подржаних локала. За филтрирање по језику користите filter са hasPrefix, за добијање региона — Locale(identifier:).regionCode.
NumberFormatter користи Locale за одређивање децималног раздвајача (зарез или тачка), симбола валуте и раздвајача груписања. За фиксни формат постављајте locale на en_US_POSIX или експлицитно наводите својства форматтера.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође