Locale — what it is, Foundation class and localization

Author: IT Sectr Published: 2026-07-12 Reading time: 9 min

Locale is a Foundation class in iOS and macOS that encapsulates the user’s linguistic and cultural conventions: date format, number format, currency, and units of measurement. According to Apple Developer Documentation, 2024, Locale determines how DateFormatter displays a month (January or January), the decimal separator in a number (comma or dot), and the currency symbol (ruble, dollar, or euro). Each Locale instance is tied to an identifier like ru_RU or en_US, where the first part is the language code (ISO 639-1) and the second is the region code (ISO 3166-1). Unlike TimeZone, Locale does not affect the absolute time value, only its string representation.

Key Takeaways

  • Locale — a Foundation class for cultural and linguistic formatting settings in iOS and macOS
  • Identifier ru_RU — combines language code (ISO 639-1) and region code (ISO 3166-1)
  • DateFormatter and NumberFormatter automatically use Locale to localize output
  • Locale.current — the system locale of the user, can be overridden for specific operations
  • Fixed vs auto-updating — Locale(identifier:) creates a fixed locale, Locale.current tracks changes in settings

What is Locale in Foundation?

Locale is a value type in Swift (NSLocale in Objective-C) representing a set of formatting rules specific to a particular language and region. Unlike TimeZone, which determines the absolute time offset, Locale determines how time, numbers, and currency appear in string representation. The same date 2024-07-21 will be displayed as “21 July 2024” for ru_RU and “July 21, 2024” for en_US.

Each Locale instance consists of two components: Language (determines month names, day names, word order) and Region (determines number format, currency, calendar). The combination of these components is encoded in an identifier according to the BCP 47 standard: ru_RU (Russian language, Russia), en_US (English, USA), de_DE (German, Germany).

According to Unicode CLDR (2024), the number of supported locales in iOS exceeds 700 language-region combinations. Foundation uses data from CLDR (Common Locale Data Repository) — the most comprehensive repository of localization data maintained by the Unicode Consortium. This ensures consistent formatting across all Apple devices.

How does Locale affect date and number formatting?

DateFormatter uses Locale to select the correct month and day names, determine the order of date components (day/month/year or month/day/year), and separators. Without explicitly specifying Locale, DateFormatter uses the device locale — this is correct for UI but dangerous for server data where the format should be fixed.

Componentru_RUen_USde_DE
Date (medium)21 July 2024Jul 21, 202421.07.2024
Number (1000.5)1 000,51,000.51.000,5
Currency (100)100.00 ₽$100.00100.00 €
CalendarGregorianGregorianGregorian
List separator;,;

NumberFormatter uses Locale to determine the decimal separator (comma or dot), grouping separator (space, comma, dot), and currency symbol. Ignoring Locale when parsing numbers is one of the common causes of bugs in international applications: the number “1,5” means “one and a half” for ru_RU, but for en_US the digital parser will read it as “five” after the comma.

Important: Calendar created via Calendar.current inherits the device locale. Calendar(identifier: .gregorian) with an explicitly set locale is the recommended approach for predictable formatting. When working with ISO 8601 dates, always use Locale(identifier: “en_US_POSIX”) — a special locale for technical formatting that is not affected by regional settings.

Locale identifiers: basic formats

Locale identifier consists of a language code (ISO 639-1, two characters) and a region code (ISO 3166-1, two characters), separated by an underscore. Examples: ru_RU, en_US, fr_FR, zh_Hans_CN (Chinese, simplified script, China). Foundation also supports identifiers in BCP 47 format: ru-RU, en-US, used in web standards.

Besides full identifiers, Locale can be created by language only: Locale(identifier: “ru”) returns a locale with the Russian language and the default region for that language (usually Russia). Similarly for English: Locale(identifier: “en”) uses the US region. This approach is useful for setting the formatting language without tying to a specific region.

Special locales include en_US_POSIX — a technical locale for machine formatting of dates and numbers, ensuring a stable format regardless of user settings. This locale is mandatory for parsing dates from server APIs, especially for ISO 8601 format. It uses the Gregorian calendar, 24-hour time format, and dot as the decimal separator.

swift
import Foundation

// Available locale identifiers
let available: [String] = Locale.availableIdentifiers
print("Total locales: \(available.count)")

// Filter Russian locales
let russianLocales = available.filter { $0.hasPrefix("ru") }
print("Russian locales: \(russianLocales)")

// Locale components
let locale = Locale(identifier: "de_DE")
print("Language: \(locale.languageCode ?? "nil")")
print("Region: \(locale.regionCode ?? "nil")")
print("Currency: \(locale.currencyCode ?? "nil")")
print("Calendar: \(locale.calendar.identifier)")

Checking available locales via Locale.availableIdentifiers returns an array of all identifiers supported by the current iOS version. To filter by region, use Locale.availableIdentifiers.filter with regionCode check. This is useful for building a region selection UI without a hardcoded list.

Determining the current user locale in Swift

Locale.current is the primary way to get the device’s current locale set by the user in iOS settings (Settings > General > Language & Region). This property automatically updates when the language or region is changed in settings without restarting the application. However, it may not match the locale preferred for content display: a user may set the interface language to English but view dates in Russian format.

For a more precise determination of user preferences, use Locale.preferredLanguages — an array of languages ordered by user priority. The first element is the primary interface language. This list corresponds to the settings in Language & Region, including dragging languages in order of preference. Communication apps (messengers, email clients) should consider this order when selecting the content display language.

swift
import Foundation

// Current system locale
let current = Locale.current
print("Current locale: \(current.identifier)")
print("Language: \(current.language?.disjointName ?? "nil")")

// User preferred languages
let preferred = Locale.preferredLanguages
print("Preferred languages: \(preferred)")

// Get region from current locale
if let region = current.regionCode {
    let regionLocale = Locale(identifier: "en_\(region)")
    let countryName = regionLocale.localizedString(
        forRegionCode: region
    )
    print("Country: \(countryName ?? region)")
}

// Check 24h format
let uses24h = current.uses24hClock(
    for: .dateAndTime
)
print("Uses 24h: \(uses24h)")

Localization in UI: to display month and day names in the interface language, use Calendar with a set locale. Calendar.current.symbols(for: .month) returns month names in the current locale’s language. To display country names in the user’s language, use Locale.current.localizedString(forRegionCode:).

Locale in Swift: usage examples

Date formatting with locale awareness is a key task when displaying dates to the user. DateFormatter with a set locale automatically selects the correct date and time format for the user’s region. For dateStyle and timeStyle values of .short, .medium, .long, .full, the formatter uses locale rules to compose date components.

swift
import Foundation

let date = Date()

// Format with different locales
let formatter = DateFormatter()
formatter.dateStyle = .medium

formatter.locale = Locale(identifier: "ru_RU")
print("Russian: \(formatter.string(from: date))")

formatter.locale = Locale(identifier: "en_US")
print("English: \(formatter.string(from: date))")

formatter.locale = Locale(identifier: "ja_JP")
print("Japanese: \(formatter.string(from: date))")

// Currency formatting with locale
let numFormatter = NumberFormatter()
numFormatter.numberStyle = .currency

numFormatter.locale = Locale(identifier: "de_DE")
print("German currency: \(numFormatter.string(from: 1234.56) ?? "nil")")

numFormatter.locale = Locale(identifier: "en_US")
print("US currency: \(numFormatter.string(from: 1234.56) ?? "nil")")

Parsing dates from server APIs should always use Locale(identifier: “en_US_POSIX”) for a fixed format. Servers typically send dates in ISO 8601 format with English month names, and using the current device locale may cause parsing errors if the user is in a non-English region. en_US_POSIX ensures parsing does not depend on device settings.

swift
import Foundation

// Correct server date parsing
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 date: \(parsed)")
}

// Localized currency name
let usLocale = Locale(identifier: "en_US")
let currencyName = usLocale.localizedString(
    forCurrencyCode: "RUB"
)
print("Russian ruble in US locale: \(currencyName ?? "nil")")

Additional capabilities: Locale provides localized descriptions of its components through methods localizedString(forRegionCode:), localizedString(forLanguageCode:), localizedString(forCurrencyCode:), and localizedString(forCalendarIdentifier:). These methods return names in the language of the locale on which they are called. For example, Locale(identifier: “ru_RU”).localizedString(forCountryCode: “DE”) returns “Germany”.

Common mistakes when working with Locale

Ignoring Locale when parsing numbers is a critical error in international applications. NumberFormatter without an explicit locale uses the current device locale. If a user in Russia enters “1,5”, NumberFormatter.number(from: “1,5”) correctly returns 1.5. But if the same code runs on a device with en_US locale, parsing returns nil because for en_US the decimal separator is a dot.

Missing en_US_POSIX for server dates leads to subtle bugs. DateFormatter with dateFormat and locale = Locale.current may break for users from regions where the date format differs from the American one. For example, in Germany DateFormatter may expect “21.07.2024” while the server sends “07/21/2024”. en_US_POSIX ensures a fixed format for machine parsing regardless of the user’s region.

Comparing date strings instead of using Date is another common mistake. Developers sometimes compare string representations of dates from different locales, getting incorrect results. Locale only changes the display, not the absolute date value. Always compare Date objects, not their string representations. For comparing date components, use Calendar with an explicitly set locale.

According to WWDC 2023, about 30% of internationalization problems in applications are related to incorrect Locale configuration. Apple recommends always explicitly setting the locale for DateFormatter and NumberFormatter when working with server data and using Locale.current only for UI display. This simple practice eliminates most bugs related to regional settings.

Frequently Asked Questions

What is Locale in Foundation?

Locale is a Foundation class representing cultural and linguistic formatting rules: date format, number format, currency, and units of measurement. It is used together with DateFormatter, NumberFormatter, and Calendar for localized data display.

What is the difference between Locale and TimeZone?

Locale determines the display format (language, regional conventions), while TimeZone determines the absolute time offset relative to UTC. Locale affects the string representation, TimeZone affects the numerical time value. Both are used together for complete date formatting.

What is en_US_POSIX and why is it needed?

en_US_POSIX is a special locale for technical formatting that ensures a stable format regardless of user settings. It is mandatory for parsing server dates (ISO 8601) and working with APIs where the format must be predictable.

How to get a list of all available locales?

Locale.availableIdentifiers returns an array of strings with identifiers of all supported locales. To filter by language, use filter with hasPrefix; to get the region, use Locale(identifier:).regionCode.

How does Locale affect NumberFormatter?

NumberFormatter uses Locale to determine the decimal separator (comma or dot), currency symbol, and grouping separator. For a fixed format, set the locale to en_US_POSIX or explicitly set the formatter properties.

Summary

  • Locale — a fundamental class for localizing date, number, and currency formatting in iOS and macOS
  • Identifier ru_RU encodes language (ISO 639-1) and region (ISO 3166-1) for precise formatting settings
  • DateFormatter and NumberFormatter automatically use Locale.current for localized output
  • en_US_POSIX — mandatory locale for parsing server dates and fixed machine formatting
  • Locale.current tracks changes in settings, Locale(identifier:) creates a fixed locale
  • Parsing numbers requires explicit locale setting — decimal separator depends on the user’s region
  • Calendar.current inherits the device locale; for predictable calculations, set the locale explicitly

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also