What regional defaults should you read from the Locale instead of assuming: units, 12 or 24 hours, ordinals and date order?

24.9.3 · L3 · Interview

Predict first

What do locales, date styles, units and ordinals print?

import Foundation

for id in ["en_US", "en_GB", "de_DE", "ja_JP", "ar_EG", "th_TH"] {
    let l = Locale(identifier: id)
    print(id, l.measurementSystem.identifier, l.hourCycle, l.currency?.identifier ?? "-", l.numberingSystem.identifier, l.calendar.identifier)
}
let date = Date(timeIntervalSince1970: 1_700_000_000)
for id in ["en_US", "en_GB", "de_DE", "ja_JP"] {
    var time = Date.FormatStyle(date: .omitted, time: .shortened).locale(Locale(identifier: id)); time.timeZone = TimeZone(identifier: "UTC")!
    var day = Date.FormatStyle().year().month(.abbreviated).day().locale(Locale(identifier: id)); day.timeZone = TimeZone(identifier: "UTC")!
    print(id, date.formatted(time), "|", date.formatted(day))
}
let temp = Measurement(value: 20, unit: UnitTemperature.celsius)
let mf = MeasurementFormatter(); mf.unitOptions = .naturalScale
mf.locale = Locale(identifier: "en_US"); print(mf.string(from: temp))
mf.locale = Locale(identifier: "de_DE"); print(mf.string(from: temp))
mf.unitOptions = .providedUnit; mf.locale = Locale(identifier: "en_US"); print(mf.string(from: temp))
let n = NumberFormatter(); n.locale = Locale(identifier: "en_US")
n.numberStyle = .ordinal; print([1, 2, 3, 11, 22].compactMap { n.string(from: NSNumber(value: $0)) })
n.locale = Locale(identifier: "fr_FR"); print([1, 2].compactMap { n.string(from: NSNumber(value: $0)) })
  • en_US ussystem oneToTwelve USD latn gregorian / … / 68°F / 20 °C / 20°C / ["1st", "2nd", "3rd", "11th", "22nd"] / ["1er", "2e"]
  • All locales print metric
  • 20°C for every locale

Mental model. Asking a shop for “a large coffee” means different volumes in different countries. The locale is the shop’s menu: you ask for the item and it knows the local size.

The Locale carries decisions you would otherwise guess wrong. The check lists them for six locales: the measurement system (United States uses its own system, Great Britain its mixed UK one, Germany, Japan, Egypt and Thailand metric), the hour cycle (English-US and Egyptian Arabic use 1 to 12, the others 0 to 23), the currency (USD, GBP, EUR, JPY, EGP, THB), the numbering system (Egyptian Arabic uses Arabic-Indic digits) and the calendar (Thailand uses the Buddhist one by default). Formatters apply them for you: the same instant prints “10:13 PM” and “Nov 14, 2023” in en_US, “22:13” and “14 Nov 2023” in en_GB, “14. Nov. 2023” in de_DE and “2023年11月14日” in ja_JP, so never hand-write “MM/dd/yyyy” for display; use FormatStyle with the components you want (Q83) and let the locale arrange them. Note that the user can override the hour cycle in Settings regardless of language, which a fixed format string ignores.

Units are the classic trap. A MeasurementFormatter with .naturalScale shows 20 degrees Celsius as “68°F” for en_US and “20 °C” for de_DE, because it converts to the unit the region uses; .providedUnit keeps Celsius for everyone. Choose deliberately: use natural scale for display to people, provided unit for scientific or fixed contexts, and store canonical units (SI) in your model. Ordinals (1st, 2nd, 3rd, 11th) are rule-based and language-specific (“1er” and “2e” in French), so use NumberFormatter with .ordinal rather than a suffix table, and keep sentences that include them in the catalog with a comment (Q110). Do not infer region from language (a user with an English language and a German region is normal, Q82), read the full structure from Locale.current or autoupdatingCurrent, and pin the locale in tests (Q447). Output was checked on macOS, not on a device with the user’s own overrides.

Regional defaults and natural-scale units come from the locale.

Common traps

  • Using one fixed date or unit format for display. It is right in one region only.
  • Converting units yourself with an “is metric” flag. The system already knows the region’s system and rounding.

They might follow up with

  • When do I want .providedUnit? When the unit is part of the content (a medical dose, a scientific reading).
  • How do I test other regions? Scheme App Region, or a formatter with an explicit locale as the check does.

Related questions

  • 2.2.6 What is a Locale, and how is it different from the user’s language?
  • 2.2.7 FormatStyle or DateFormatter: which do you reach for?
  • 2.8.4 How do you format numbers, currency and percentages correctly?
  • 2.8.6 Which Foundation formatter replaces hand-written string code for sizes, lists, names and units?
  • 22.4.2 What makes a test non-deterministic, and how do you control time zones, locales, randomness, UUIDs and the…
  • 24.4.2 What changes by locale beyond strings: sorting and searching, the first day of the week, calendars, names and…

← How do you split text into words, count words or search in languages…