Skip to content

Conversation

matouskozak
Copy link
Member

@matouskozak matouskozak commented Jan 2, 2025

Problem Description

On iOS (and other apple mobile platforms), we are taking the system default culture and using it inside .NET as CultureInfo.CurrentCulture.

Currently, the system default culture is truncated into neutral culture format (only language). This behavior was set in #104071, where we had an incorrect assumption that other ICU platforms also return just neutral locale name. However, that isn't true because the ICU uloc_getBaseName

uloc_getBaseName(defaultLocale, localeNameBuffer, ULOC_FULLNAME_CAPACITY, &status);
only strips collation information as described in https://github.com/unicode-org/icu/blob/d9d09db2a7167eea2b5749836ef32cdd45303fdd/icu4c/source/common/unicode/uloc.h#L948.

The problem of using all Apple's system default locales is that some of the retrieved identifiers are not part of https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs (e.g., en-CZ), causing the culture LCID number to be 0x1000 (LOCALE_CUSTOM_UNSPECIFIED). This can cause issues where the default CultureInfo.CurrentCulture causes crashes when using certain CultureInfo APIs that accept LCID instead of culture name, such as,

public static CultureInfo GetCultureInfo(int culture)

used by e.g. SqlString.

Proposed Change

This PR reverts the previous change and preserves the full (specific = language-region) culture locale format when the retrieved locale is part of IcuLocaleData.cs. Upon retrieving the system locale name, we verify that the culture name is part of the ICU data and if not, we use the parent culture (neutral) instead.

Previous behavior (.net 9):

Apple system culture CurrentCulture IsCustomCulture?
en-US en no
en-CZ en no

New behavior:

Apple system culture CurrentCulture IsCustomCulture?
en-US en-US no
en-CZ en-CZ yes

This PR addresses the following issues:

Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-globalization
See info in area-owners.md if you want to be subscribed.

@matouskozak matouskozak requested a review from Copilot January 2, 2025 12:10
Copy link
Contributor

@Copilot 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.

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

Files not reviewed (1)
  • src/native/libs/System.Globalization.Native/pal_locale.m: Language not supported

@matouskozak
Copy link
Member Author

/azp run runtime-ioslike,runtime-ioslikesimulator,runtime-maccatalyst

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

Comment on lines -789 to -795
static NSString* GetBaseName(NSString *localeIdentifier)
{
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:localeIdentifier];
NSString *languageCode = [locale objectForKey:NSLocaleLanguageCode];
return languageCode;
}

Copy link
Member

Choose a reason for hiding this comment

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

Do we know what this was done before?
The PR description should be updated to describe this - what we did before (has that) and why, and what we're changing it to (and why it's OK to change given the previous reasoning).

Copy link
Member Author

@matouskozak matouskozak Jan 3, 2025

Choose a reason for hiding this comment

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

Indeed, I was trying this PR to see which test suit breaks if we switch to specific name because there are other dependencies such as SqlStrings that were previously affected. I'll update the issue description when I finalize the code changes.

Brief history what I found:


I'll try to make a middle way out of this, where we use system locale if it's recognized by .NET, otherwise we use parent culture which is neutral.

@matouskozak
Copy link
Member Author

/azp run runtime-ioslike,runtime-ioslikesimulator,runtime-maccatalyst

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@matouskozak matouskozak changed the title [iOS] Fix CurrentCulture locale to be in specific format [iOS] Set CurrentCulture locale to specific format based on system default when possible Jan 6, 2025
@matouskozak
Copy link
Member Author

/azp run runtime-ioslike,runtime-ioslikesimulator,runtime-maccatalyst

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@matouskozak matouskozak marked this pull request as ready for review January 6, 2025 11:05
@tarekgh
Copy link
Member

tarekgh commented Jan 6, 2025

I don't think we need to fix this. We encourage users to move away from LCIDs. Any users still using LCID should start migrating to use culture names instead. The change here is trying to work around the issue of using the parent which may benefit using the LCID but it can cause other issues when requesting any region-specific data from current culture.

This issue is not specific to iOS or mobile platforms. This can occur on Windows for example doing CultureInfo.CurrentCulture = new CultureInfo("en-AE");. Whoever is using LCID needs to fix their code and start using culture names or they need to make their code tolerant when getting custom LCID values.

Last, if IcuLocaleData.cs missing any data, we can try to update it as needed.

@matouskozak
Copy link
Member Author

matouskozak commented Jan 6, 2025

Any users still using LCID should start migrating to use culture names instead.

@tarekgh I know that using LCID is not recommended. What I was trying to work around here by using the parent is that any default culture set by .NET would be compatible with all .NET libraries code. We are currently relying on LCID inside SqlString codebase, causing failures if LCID is custom culture.

Note, this PR not only does the fallback to parent culture but also fixes the behavior that we didn't return specific system culture. I can remove the parent culture workaround and disable the failing tests until the affected library code migrates from LCID. However, because we need to backport this fix to .NET 9, we could be risking a breaking change to some customers using e.g., SqlStrings.

@tarekgh
Copy link
Member

tarekgh commented Jan 6, 2025

What I was trying to work around here by using the parent is that any default culture set by .NET would be compatible with all .NET libraries code. We are currently relying on LCID inside SqlString codebase, causing failures if LCID is custom culture.

I think the right thing to do is have SqlString get fixed. Either they migrate to use the culture name or to be tolerant to do the fallback themselves. As I mentioned, this issue is not specific to iOS. What happens if someone sets en-AE as a default culture on Windows? They will run into the exact same problem. Also, your fix can cause some different problems. Like someone is using the CurrentCulture name to create a RegionInfo object. If you are using the parent (which most likely be a neutral culture, RegionInfo creation will throw exception.

but also fixes the behavior that we didn't return specific system culture.

I am fine with the change in pal_locale.m if other reviewers see this is a correct fix.

@ivanpovazan
Copy link
Member

We encourage users to move away from LCIDs

Is there an official documentation about it?

We are currently relying on LCID inside SqlString codebase, causing failures if LCID is custom culture

Is SqlString codebase the only problem? Could we detect this in some way and produce a build warning, so that internally we can fix all the places relying on this?

@tarekgh
Copy link
Member

tarekgh commented Jan 7, 2025

Is there an official documentation about it?

https://learn.microsoft.com/en-us/globalization/locale/other-locale-names#lcid

@matouskozak
Copy link
Member Author

We are currently relying on LCID inside SqlString codebase, causing failures if LCID is custom culture

Is SqlString codebase the only problem? Could we detect this in some way and produce a build warning, so that internally we can fix all the places relying on this?

I took a look and there are a few other places where LCID is used but I'm unsure if they would cause the crash. I would need to run each library test suit locally to verify because the test might not fail on CI as the default CurrentCulture on lab device is likely en-US (non-custom, not causing the before-mentioned crash).

To trigger build warnings, we would need to mark the APIs obsolete/depracated. Not sure if this is something that is planned based on https://learn.microsoft.com/en-us/globalization/locale/other-locale-names#lcid @tarekgh ?

@matouskozak
Copy link
Member Author

/azp run runtime-ioslikesimulator

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@matouskozak matouskozak force-pushed the iOS-ICU-return-full-locale branch from b65a72f to 2cb6ca3 Compare January 16, 2025 16:51
@matouskozak
Copy link
Member Author

/azp run runtime-ioslike,runtime-ioslikesimulator,runtime-maccatalyst

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@matouskozak matouskozak changed the title [iOS] Set CurrentCulture locale to specific format based on system default when possible [iOS] Retrieve device locale in full (specific) format from preferredLanguages API Jan 17, 2025
@matouskozak matouskozak marked this pull request as ready for review January 17, 2025 12:18
@matouskozak
Copy link
Member Author

matouskozak commented Jan 17, 2025

I removed the fallback to neutral culture, thus retrieving the locale in full (specific) locale format.

I also added a test case to assert this behavior. However, for some reason the lab simulators are reporting only en instead of expected en-US. I verified locally that on simulators (down to iOS 15.5), the full locale identifier is retrieved. It is not the first time I've seen different behavior on lab simulators in comparison to local runs, so I opened an issue to investigate this further #111501.

Finally, I opened an issue to address the removal of LCID dependencies inside SqlString codebase issue for removal of LCID inside SqlString codebase #111395.

Please take a look and review the changes @tarekgh @ivanpovazan @akoeplinger @rolfbjarne @steveisok

@matouskozak matouskozak changed the title [iOS] Retrieve device locale in full (specific) format from preferredLanguages API [iOS] Retrieve device locale in full (specific) format from ObjectiveC APIs Jan 17, 2025
Copy link
Member

@ivanpovazan ivanpovazan left a comment

Choose a reason for hiding this comment

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

LGTM, nice work!
I left a small comment.

@matouskozak
Copy link
Member Author

/azp run runtime-ioslikesimulator

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@matouskozak matouskozak merged commit ffc68b8 into dotnet:main Jan 20, 2025
161 of 163 checks passed
@matouskozak
Copy link
Member Author

/backport to release/9.0-staging

Copy link
Contributor

Started backporting to release/9.0-staging: https://github.com/dotnet/runtime/actions/runs/12865114650

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants