Skip to content

Astro.preferredLocaleList drops object-form locale codes that differ in case (en-us vs en-US) #17969

Closed

Description

@winklemad

Astro Info

Astro                    v7.3.2
Node                     v22.17.1
System                   macOS (arm64)
Package Manager          pnpm
Output                   static
Adapter                  none
Integrations             none

Describe the Bug

Astro.preferredLocaleList (and context.preferredLocaleList in endpoints) drops a locale configured in object form ({ path, codes }) when the browser's Accept-Language code differs from the configured code only in case (or the -/_ separator).

In computePreferredLocaleList (packages/astro/src/i18n/utils.ts), the string-locale branch compares with normalizeTheLocale(...), but the object-form branch compares code === browserLocale.locale directly — case-sensitive. computePreferredLocale (singular) already normalizes the object-form code, so the two APIs disagree on the very same request.

Minimal config:

i18n: {
  defaultLocale: 'english',
  locales: [{ path: 'english', codes: ['en-us'] }, 'fr'],
}

Request with Accept-Language: en-US (differs in case from the configured en-us):

{
  "acceptLanguage": "en-US",
  "preferredLocale": "en-us",   // singular: resolves correctly (normalizes case)
  "preferredLocaleList": []     // plural: empty — the bug
}

Controls that behave correctly: exact en-us → ["en-us"]; and the string-form locale fr already matches case-insensitively (FR → ["fr"]).

What's the expected result?

preferredLocaleList should match object-form codes the same case-/separator-insensitive way that preferredLocale and string-form locales already do — Accept-Language: en-US → ["en-us"].

Link to Minimal Reproducible Example

https://github.com/winklemad/astro-i18n-preferred-locale-list-repro

Server-side i18n needs an actual request, so it's a small curl-driven repro rather than a StackBlitz preview — the README has the exact pnpm install / pnpm dev / curl steps and the observed vs expected output.

Participation

Activity

  1. astro-factory commented on Sep 10, 2026

    @astro-factory
    Contributor

    Triage complete

    • Prepare the workspace: ready
    • Install dependencies: complete
    • Build the project: complete
    • Reproduce the issue: confirmed
    • Diagnose the cause: high confidence
    • Verify the diagnosis: bug
    • Attempt a fix: verified
    • Publish the result: set triage: fix pending

    • Reproduced: Yes
    • Exploration: Yes — View branch
    • Unit Test: Yes — packages/astro/test/units/i18n/i18n-utils.test.ts
    • Priority: P4: important. This violates documented behavior — preferredLocaleList should match locales case-insensitively per BCP 47, and preferredLocale (singular) already does. Any project using object-form locales with preferredLocaleList is affected by default since browsers send mixed-case Accept-Language headers (e.g., en-US).

    In computePreferredLocaleList (packages/astro/src/i18n/utils.ts, line 135), the object-form locale branch compares code === browserLocale.locale directly — a case-sensitive check. The adjacent string-form branch and the equivalent branch in computePreferredLocale (singular) both use normalizeTheLocale() for case-insensitive matching. This was an oversight from the original "refined locales" feature (PR #9200), where normalization was applied to 3 of 4 comparison branches but missed this one.

    I found a potential fix for this issue. The fix is a one-line change: wrapping both sides of the comparison in normalizeTheLocale() to match the pattern used everywhere else. Two unit tests were added to cover case-insensitive and underscore-separator matching for object-form locales. All 400 i18n tests pass with no regressions.

    Try this fix

    You can test this fix right now without waiting for a release:

    # astro
    npm i https://pkg.pr.new/astro@cb47a6b

    If this fixes your issue, please leave a comment letting us know (for example
    "confirmed, this fixes it"). A pull request will then be opened to get it merged.

    Full Triage Report

    Reproduction

    Created a minimal Astro project with SSR output and the following i18n config:

    i18n: {
      defaultLocale: 'english',
      locales: [{ path: 'english', codes: ['en-us'] }, 'fr'],
    }

    An API endpoint returns preferredLocale and preferredLocaleList. Results:

    Accept-Language preferredLocale preferredLocaleList Status
    en-US (bug case) "en-us" ✅ [] ❌ Bug
    en-us (exact match) "en-us" ✅ ["en-us"] ✅ OK
    FR (string-form) "fr" ✅ ["fr"] ✅ OK

    Root Cause

    File: packages/astro/src/i18n/utils.ts, line 135

    In computePreferredLocaleList, the object-form locale branch uses a case-sensitive comparison:

    if (code === browserLocale.locale) {  // ❌ case-sensitive

    But computePreferredLocale (singular) correctly normalizes:

    if (normalizeTheLocale(currentCode) === normalizeTheLocale(firstResult.locale)) {  // ✅

    Both were introduced in the same commit (b4b851f5a4, PR #9200). The developer applied normalizeTheLocale() to 3 of 4 locale-comparison branches but missed this one.

    Fix

    One-line change in packages/astro/src/i18n/utils.ts:

    -  if (code === browserLocale.locale) {
    +  if (normalizeTheLocale(code) === normalizeTheLocale(browserLocale.locale)) {

    Verification after fix

    • Accept-Language: en-US → preferredLocaleList: ["en-us"] ✅ (was [])
    • Accept-Language: en-us → preferredLocaleList: ["en-us"] ✅ (still works)
    • Accept-Language: FR → preferredLocaleList: ["fr"] ✅ (still works)
    • Accept-Language: en_US → preferredLocaleList: ["en-us"] ✅ (was [])
    • All 400 i18n unit tests pass with no regressions.

    Tests added

    Two new tests in packages/astro/test/units/i18n/i18n-utils.test.ts:

    • matches object-form locale codes case-insensitively — en-US header matches en-us config
    • matches object-form locale codes with underscore separator — en_US header matches en-us config

    Changeset

    .changeset/pretty-heads-occur.md — astro patch

    This report was made by an LLM. The analysis may be wrong, and the potential fix might not work, but is intended as a starting point for exploring the issue.

  2. added
    - P4: importantViolate documented behavior or significantly impacts performance (priority)
    pkg: astroRelated to the core `astro` package (scope)
    triage: fix pendingReporter needs to verify the triage bot fix works
    and removed on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    - P4: importantViolate documented behavior or significantly impacts performance (priority)pkg: astroRelated to the core `astro` package (scope)triage: fix pendingReporter needs to verify the triage bot fix works

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions