Repository navigation
Description
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
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
- I am willing to submit a pull request for this issue. (I had one in Fix
preferredLocaleListdropping object-form locale codes that differ in case #17700, closed pending this report; happy to reopen or resubmit.)
Activity
astro-factory commentedon Sep 10, 2026
Triage complete
triage: fix pendingpackages/astro/test/units/i18n/i18n-utils.test.tspreferredLocaleListshould match locales case-insensitively per BCP 47, andpreferredLocale(singular) already does. Any project using object-form locales withpreferredLocaleListis affected by default since browsers send mixed-caseAccept-Languageheaders (e.g.,en-US).In
computePreferredLocaleList(packages/astro/src/i18n/utils.ts, line 135), the object-form locale branch comparescode === browserLocale.localedirectly — a case-sensitive check. The adjacent string-form branch and the equivalent branch incomputePreferredLocale(singular) both usenormalizeTheLocale()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@cb47a6bIf 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:
An API endpoint returns
preferredLocaleandpreferredLocaleList. Results:Accept-LanguagepreferredLocalepreferredLocaleListen-US(bug case)"en-us"✅[]❌en-us(exact match)"en-us"✅["en-us"]✅FR(string-form)"fr"✅["fr"]✅Root Cause
File:
packages/astro/src/i18n/utils.ts, line 135In
computePreferredLocaleList, the object-form locale branch uses a case-sensitive comparison:But
computePreferredLocale(singular) correctly normalizes:Both were introduced in the same commit (
b4b851f5a4, PR #9200). The developer appliednormalizeTheLocale()to 3 of 4 locale-comparison branches but missed this one.Fix
One-line change in
packages/astro/src/i18n/utils.ts: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[])Tests added
Two new tests in
packages/astro/test/units/i18n/i18n-utils.test.ts:matches object-form locale codes case-insensitively—en-USheader matchesen-usconfigmatches object-form locale codes with underscore separator—en_USheader matchesen-usconfigChangeset
.changeset/pretty-heads-occur.md—astropatchThis 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.