Skip to content

Fix NETSDK1187: Replace OS-dependent CultureInfo locale normalization with platform-independent BCP 47 algorithm#53817

Open
Copilot wants to merge 2 commits intomainfrom
copilot/fix-locale-normalization-issue
Open

Fix NETSDK1187: Replace OS-dependent CultureInfo locale normalization with platform-independent BCP 47 algorithm#53817
Copilot wants to merge 2 commits intomainfrom
copilot/fix-locale-normalization-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 10, 2026

CultureInfo.GetCultureInfo(locale).Name used for locale normalization is OS/runtime-dependent and can remap locale codes to different locales (e.g. ckbku), causing NETSDK1187 to fire incorrectly and with inconsistent "correct" values across SDK versions — creating an unresolvable flip-flop for package authors.

Changes

  • ResolvePackageAssets.cs: Replace CultureInfo.GetCultureInfo(locale).Name normalization with a new NormalizeBcp47LocaleCasing() method implementing RFC 5646 casing rules deterministically:
    • Language subtag (position 0): lowercase
    • Script subtag (4 alpha chars at position 1): Titlecase
    • Region subtag (2 alpha chars): UPPERCASE
    • All other subtags: lowercase
  • Separate validation from normalization: CultureInfo.GetCultureInfo() is retained only in the try/catch for NETSDK1188 (truly unrecognized locale) validation — its return value is no longer used.

Behavior

Locale Before After
ckb ⚠️ NETSDK1187: "should be ku" (wrong locale!) ✅ No warning
qps-ploc ⚠️ NETSDK1187: "should be qps-Ploc" or qps-ploc" (OS-dependent) ⚠️ NETSDK1187: "should be qps-Ploc" (consistent)
qps-Ploc ⚠️ NETSDK1187 on some SDK versions ✅ No warning
ru-ru ⚠️ NETSDK1187: "should be ru-RU" ⚠️ NETSDK1187: "should be ru-RU" (unchanged)
"what is this even" ⚠️ NETSDK1188 ⚠️ NETSDK1188 (unchanged)

Copilot AI linked an issue Apr 10, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Fix incorrect locale normalization in .NET SDK Fix NETSDK1187: Replace OS-dependent CultureInfo locale normalization with platform-independent BCP 47 algorithm Apr 10, 2026
Copilot AI requested a review from marcpopMSFT April 10, 2026 21:04
@marcpopMSFT
Copy link
Copy Markdown
Member

@baronfel doing a local review, copilot suggested we simplify the logic to fix the ckb to ku issue as that's the critical bug but this would leave in the qps-ploc casing warning. I'm kind of inclined to go with the recommendation that fixes the blocking issue but leaves the warning in for the specific case as it's way simpler and easier to maintain. I do see the ploc has a fair bit of usage across a quick GH search.

Thoughts on that compromise? I just saw the solution here and it looked like it was going to be overly complex.

var normalizedLocale = CultureInfo.GetCultureInfo(locale).Name;

// If OrdinalIgnoreCase comparison fails, CultureInfo remapped the locale
// entirely (e.g. ckb → ku) — skip normalization and warning.
if (string.Equals(normalizedLocale, locale, StringComparison.OrdinalIgnoreCase))
{
    // Pure casing difference — safe to use CultureInfo's normalization
    if (normalizedLocale != locale)
    {
        // ... emit NETSDK1187 warning (existing logic) ...
        locale = normalizedLocale;
    }
}

@baronfel
Copy link
Copy Markdown
Member

@marcpopMSFT I think I like your take a bit more for sure. In general this build warning is a heuristic - the actual behavior of loading the resources is going to rely on the locale information of the host that the .NET Runtime will read content from, so we can loosen the check in the way you describe fairly reasonably!

@marcpopMSFT marcpopMSFT marked this pull request as ready for review April 15, 2026 17:42
Copilot AI review requested due to automatic review settings April 15, 2026 17:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to prevent NETSDK1187 from producing OS/runtime-dependent locale “normalization” results by avoiding CultureInfo remapping behaviors (e.g., ckbku) and adding coverage for the ckb scenario.

Changes:

  • Updates ResolvePackageAssets to only apply CultureInfo normalization when the result differs by casing only.
  • Adds a new unit test ensuring ckb does not trigger NETSDK1187.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs Adjusts locale normalization logic to skip changes that are more than casing differences.
test/Microsoft.NET.Build.Tasks.Tests/GivenAResolvePackageAssetsTask.cs Adds a regression test for the ckb locale not producing NETSDK1187.

Comment on lines 1657 to +1663
var normalizedLocale = System.Globalization.CultureInfo.GetCultureInfo(locale).Name;
if (normalizedLocale != locale)

// Only apply CultureInfo's normalization when it is a pure casing change.
// CultureInfo can remap locale codes to entirely different locales
// (e.g. 'ckb' -> 'ku') depending on the OS/ICU version, so we skip
// normalization when the result differs beyond casing.
if (string.Equals(normalizedLocale, locale, StringComparison.OrdinalIgnoreCase))
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description/title says locale normalization is now done via a platform-independent BCP 47 casing algorithm, but this code still derives normalizedLocale from CultureInfo.GetCultureInfo(locale).Name. That keeps NETSDK1187’s suggested “correct” casing dependent on the OS/ICU/runtime (e.g., qps-ploc can still normalize differently across environments). Consider using the deterministic BCP 47 casing normalizer for NETSDK1187 and reserving GetCultureInfo() only for NETSDK1188 validation, as described in the PR metadata.

Copilot uses AI. Check for mistakes.
[Theory]
[InlineData("net7.0")]
[InlineData("net6.0")]
public void It_does_not_warn_for_ckb_locale(string tfm)
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test name suggests there should be no warnings at all for ckb, but the assertions only check that NETSDK1187 isn’t produced. If NETSDK1188 is logged on some platforms (where ckb isn’t recognized by CultureInfo), this test would still pass while contradicting its name/intent. Consider renaming the test to mention NETSDK1187 specifically (or assert on NETSDK1188 too, if the intent is truly “no warnings”).

Suggested change
public void It_does_not_warn_for_ckb_locale(string tfm)
public void It_does_not_warn_with_NETSDK1187_for_ckb_locale(string tfm)

Copilot uses AI. Check for mistakes.
@marcpopMSFT marcpopMSFT force-pushed the copilot/fix-locale-normalization-issue branch from 5895cb4 to 0d3c0da Compare May 1, 2026 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NETSDK1187: Incorrect locale normalization in .NET SDK

4 participants