///|
/// Parses and deduplicates locale candidates in preference order.
fn locales_from_raw_candidates(raw_candidates : Array[String]) -> Array[Locale] {
  let locales : Array[Locale] = []
  let seen : Map[String, Bool] = Map([])
  for raw in raw_candidates {
    for piece in split_locale_list(raw) {
      append_parsed_locale_if_new(locales, seen, piece)
    }
  }
  locales
}

///|
/// Appends a parsed locale only when its tag has not been seen yet.
fn append_parsed_locale_if_new(
  target : Array[Locale],
  seen : Map[String, Bool],
  raw : String,
) -> Unit {
  match parse_locale(raw) {
    Some(locale) if seen.get(locale.tag) is None => {
      seen[locale.tag] = true
      target.push(locale)
    }
    _ => ()
  }
}

///|
/// Returns locale-related environment variable keys in precedence order.
fn locale_env_keys() -> Array[String] {
  ["LC_ALL", "LC_MESSAGES", "LANGUAGE", "LANG"]
}

///|
/// Appends locale-related environment variable values in precedence order.
fn append_env_locale_candidates(target : Array[String]) -> Unit {
  for key in locale_env_keys() {
    match @env.get_env_var(key) {
      Some(value) => push_candidate(target, value)
      None => ()
    }
  }
}

///|
/// Trims and stores a non-empty raw locale candidate.
fn push_candidate(target : Array[String], raw : String) -> Unit {
  let trimmed = raw.trim().to_owned()
  if trimmed != "" {
    target.push(trimmed)
  }
}

///|
/// Splits a locale preference list on `:` and `,`.
fn split_locale_list(raw : String) -> Array[String] {
  let items : Array[String] = []
  let mut buf = StringBuilder::new(size_hint=raw.length())
  for ch in raw {
    if ch == ':' || ch == ',' {
      push_candidate(items, buf.to_string())
      buf = StringBuilder::new(size_hint=raw.length())
    } else {
      buf.write_char(ch)
    }
  }
  push_candidate(items, buf.to_string())
  items
}

///|
/// Parses a raw locale string into a normalized locale record.
fn parse_locale(raw : String) -> Locale? {
  let trimmed = raw.trim().to_owned()
  if trimmed == "" {
    return None
  }
  let cleaned = strip_locale_suffixes(trimmed)
  if cleaned == "" || cleaned == "C" || cleaned == "POSIX" {
    return None
  }

  let subtags = split_nonempty(normalize_separators(cleaned), "-")
  if subtags.length() == 0 {
    return None
  }

  guard parse_language_subtag(subtags[0]) is Some(language) else { return None }
  let parts : Array[String] = [language]
  let mut index = 1

  let script = if index < subtags.length() {
    match parse_script_subtag(subtags[index]) {
      Some(value) => {
        index += 1
        parts.push(value)
        Some(value)
      }
      None => None
    }
  } else {
    None
  }

  let region = if index < subtags.length() {
    match parse_region_subtag(subtags[index]) {
      Some(value) => {
        index += 1
        parts.push(value)
        Some(value)
      }
      None => None
    }
  } else {
    None
  }

  for i in index.. String {
  strip_after(strip_after(strip_after(raw, ";"), "."), "@")
}

///|
/// Returns the prefix before the first marker.
fn strip_after(raw : String, marker : StringView) -> String {
  match raw.split_once(marker) {
    Some((prefix, _)) => prefix.to_owned()
    None => raw
  }
}

///|
/// Replaces POSIX locale separators with BCP 47 separators.
fn normalize_separators(raw : String) -> String {
  let buf = StringBuilder::new(size_hint=raw.length())
  for ch in raw {
    if ch == '_' {
      buf.write_char('-')
    } else {
      buf.write_char(ch)
    }
  }
  buf.to_string()
}

///|
/// Splits a string and drops empty trimmed pieces.
fn split_nonempty(raw : String, sep : StringView) -> Array[String] {
  let parts : Array[String] = []
  for part in raw.split(sep) {
    push_candidate(parts, part.to_owned())
  }
  parts
}

///|
/// Validates and normalizes a language subtag.
fn parse_language_subtag(raw : String) -> String? {
  if raw.length() < 2 || raw.length() > 8 || !is_ascii_alpha(raw) {
    None
  } else {
    Some(raw.to_lower())
  }
}

///|
/// Validates and normalizes a script subtag.
fn parse_script_subtag(raw : String) -> String? {
  if raw.length() != 4 || !is_ascii_alpha(raw) {
    None
  } else {
    Some(titlecase_ascii(raw))
  }
}

///|
/// Validates and normalizes a region subtag.
fn parse_region_subtag(raw : String) -> String? {
  if raw.length() == 2 && is_ascii_alpha(raw) {
    Some(raw.to_upper())
  } else if raw.length() == 3 && is_ascii_digit(raw) {
    Some(raw)
  } else {
    None
  }
}

///|
/// Validates and normalizes trailing variant subtags.
fn canonicalize_extra_subtag(raw : String) -> String? {
  if raw.length() == 0 || raw.length() > 8 || !is_ascii_alnum(raw) {
    None
  } else {
    Some(raw.to_lower())
  }
}

///|
/// Reports whether every character is ASCII alphabetic.
fn is_ascii_alpha(raw : String) -> Bool {
  if raw == "" {
    return false
  }
  for ch in raw {
    if !ch.is_ascii_alphabetic() {
      return false
    }
  }
  true
}

///|
/// Reports whether every character is an ASCII digit.
fn is_ascii_digit(raw : String) -> Bool {
  if raw == "" {
    return false
  }
  for ch in raw {
    if !ch.is_ascii_digit() {
      return false
    }
  }
  true
}

///|
/// Reports whether every character is ASCII alphanumeric.
fn is_ascii_alnum(raw : String) -> Bool {
  if raw == "" {
    return false
  }
  for ch in raw {
    if !(ch.is_ascii_alphabetic() || ch.is_ascii_digit()) {
      return false
    }
  }
  true
}

///|
/// Title-cases an ASCII script subtag.
fn titlecase_ascii(raw : String) -> String {
  let buf = StringBuilder::new(size_hint=raw.length())
  let mut is_first = true
  for ch in raw {
    if is_first {
      buf.write_char(ch.to_ascii_uppercase())
      is_first = false
    } else {
      buf.write_char(ch.to_ascii_lowercase())
    }
  }
  buf.to_string()
}

///|
/// Joins normalized subtags with `-`.
fn join_subtags(parts : Array[String]) -> String {
  if parts.length() == 0 {
    return ""
  }
  let buf = StringBuilder::new()
  for i in 0..