How does the Locale type work in EK9 and how do I format values for different regions?

← Getting Started · Ref: Q44

Locale is EK9's unified formatting type for presenting values according to regional conventions. One type handles formatting for Integer, Float, Date, Time, DateTime, Money, Boolean, and Dimension.

CONSTRUCTION

  enGB <- Locale("en_GB")        single string
  skSK <- Locale("sk", "SK")    language, country

FORMATTING

Unified API: locale.format(value) for all types. Four named levels for Date/Time/Money: shortFormat(), mediumFormat(), longFormat(), fullFormat(). Day of week: enGB.dayOfWeek(date).

Use 'ek9 -h Locale' to see the full API.

See Q31 for Date/Time/DateTime. See Q35 for Money. See Q36 for Dimension. See Q37 for string interpolation. See Q39 for Integer/Float formatting.

Example

defines module qa.locale

  defines program
    LocaleDemo()
      stdout <- Stdout()

      // === LOCALE CONSTRUCTION ===

      // Single string with underscore separator
      enGB <- Locale("en_GB")

      // Dash separator also works
      enUS <- Locale("en-US")

      // Two-argument form: language, country
      deutsch <- Locale("de_DE")
      skSK <- Locale("sk", "SK")

      // Access language and country parts
      stdout.println(`Language: ${enGB.language()}, Country: ${enGB.country()}`)

      // === INTEGER FORMATTING ===

      bigNumber <- 675807
      negative <- -92208

      // Thousand separators differ by locale
      stdout.println(`GB: ${enGB.format(bigNumber)}`)
      stdout.println(`DE: ${deutsch.format(bigNumber)}`)
      stdout.println(`SK: ${skSK.format(negative)}`)

      // === FLOAT FORMATTING ===

      pi <- 3.14159265358979
      bigFloat <- -1.797693134862395E12

      // Decimal separators differ by locale
      stdout.println(`GB pi: ${enGB.format(pi)}`)
      stdout.println(`DE pi: ${deutsch.format(pi)}`)

      // Control decimal places with second argument
      stdout.println(`GB pi (2dp): ${enGB.format(pi, 2)}`)
      stdout.println(`DE big (1dp): ${deutsch.format(bigFloat, 1)}`)

      // === DATE FORMATTING — four named levels ===

      wedding <- 2020-10-03

      stdout.println(`GB short: ${enGB.shortFormat(wedding)}`)
      stdout.println(`US medium: ${enUS.mediumFormat(wedding)}`)
      stdout.println(`SK long: ${skSK.longFormat(wedding)}`)
      stdout.println(`DE full: ${deutsch.fullFormat(wedding)}`)

      // === TIME FORMATTING ===

      lunchTime <- 12:00:01

      stdout.println(`GB short: ${enGB.shortFormat(lunchTime)}`)
      stdout.println(`DE medium: ${deutsch.mediumFormat(lunchTime)}`)

      // === DATETIME FORMATTING ===

      event <- 2020-10-03T12:00:00Z

      stdout.println(`US short: ${enUS.shortFormat(event)}`)
      stdout.println(`GB medium: ${enGB.mediumFormat(event)}`)
      stdout.println(`DE long: ${deutsch.longFormat(event)}`)
      stdout.println(`SK full: ${skSK.fullFormat(event)}`)

      // === MONEY FORMATTING ===

      tenPounds <- 10#GBP
      thirtyDollars <- 30.89#USD

      // Default format — symbol + full amount
      stdout.println(`GB: ${enGB.format(tenPounds)}`)
      stdout.println(`DE: ${deutsch.format(tenPounds)}`)
      stdout.println(`SK: ${skSK.format(thirtyDollars)}`)

      // Named levels for money
      stdout.println(`Medium (no fraction): ${enGB.mediumFormat(tenPounds)}`)
      stdout.println(`Short (no symbol/fraction): ${enGB.shortFormat(thirtyDollars)}`)
      stdout.println(`Long (no symbol): ${deutsch.longFormat(tenPounds)}`)

      // Custom control: format(money, withSymbol, withFraction)
      stdout.println(`Symbol, no fraction: ${enGB.format(arg0: thirtyDollars, showSymbol: true, showFractionalPart: false)}`)
      stdout.println(`No symbol, with fraction: ${enGB.format(arg0: thirtyDollars, showSymbol: false, showFractionalPart: true)}`)

      // === BOOLEAN FORMATTING ===

      stdout.println(`True in GB: ${enGB.format(true)}`)
      stdout.println(`True in DE: ${deutsch.format(true)}`)

      // === DIMENSION FORMATTING ===

      dist <- 42.5km
      stdout.println(`Dimension GB: ${enGB.format(dist)}`)
      stdout.println(`Dimension GB (1dp): ${enGB.format(dist, 1)}`)

      // === DAY OF WEEK ===

      stdout.println(`Day: ${enGB.dayOfWeek(wedding)}`)
      stdout.println(`Tag: ${deutsch.dayOfWeek(wedding)}`)

      // === LOCALE OPERATORS ===

      // Comparison
      require enGB <> deutsch
      require enGB < enUS
      stdout.println(`GB <=> US: ${enGB <=> enUS}`)

      // Hashcode
      stdout.println(`Hash: ${#? enGB}`)

      // To-string and to-JSON
      stdout.println(`String: ${enGB}`)
      stdout.println(`JSON: ${$enGB}`)

      // Copy
      copied <- Locale()
      copied :=: enGB
      require copied == enGB

      // Unset
      unsetLocale <- Locale()
      require ~unsetLocale?
      stdout.println(`Unset isSet: ${unsetLocale?}`)

      // Matches
      matchResult <- enGB matches /en.*/
      stdout.println(`Matches /en.*/: ${matchResult}`)

      // === USING LOCALE WITH INTERPOLATION ===

      // Locale formatting combines naturally with string interpolation
      stdout.println(`The wedding was on ${enGB.longFormat(wedding)} at ${enGB.shortFormat(lunchTime)}`)
      stdout.println(`Cost: ${enGB.format(tenPounds)} (${deutsch.format(tenPounds)} in German format)`)

Common mistakes

E50060 — EK9 Locale uses short method names: language(), country() not getLanguage(), getCountry(). Triggers E50060 — method not resolved. See ek9 -h Locale for the full API.

Incorrect:

enGB.getLanguage()

Correct:

enGB.language()

E50060 — EK9 Locale uses short method names: language(), country() not getLanguage(), getCountry(). Triggers E50060 — method not resolved. See ek9 -h Locale for the full API.

Incorrect:

enGB.getCountry()

Correct:

enGB.country()
Other ways to ask this
  • How do I format numbers, dates, and currency for different locales in EK9?
  • What is the difference between shortFormat, mediumFormat, longFormat, and fullFormat in EK9?
  • How do I display money amounts with locale-specific formatting in EK9?
  • How does EK9 handle internationalization and localization?

Coming from another language?

Java: separate NumberFormat/DateTimeFormatter/Currency classes. Python: locale module with global state. Rust/Go: no built-in locale. EK9: unified locale.format(value) for all types, named format levels.

Keywords: country, date, longFormat, first, shortFormat, language, time, beginner, localization, locale, migrate, mediumFormat, fullFormat, format, start, currency, number, money, internationalization, region, intro