Skip to content

Android - With set Turkish locale "i".ToUpper() is giving 'I" instead of 'İ' #106560

Description

@sowacx

Description

Hello,

There is long story with Turkish 'ı' and 'i'. Many details can be found here: http://www.i18nguy.com/unicode/turkish-i18n.html
Long story short - With Turkish locale ToUpper should transform text in this way:
i -> İ (dotted i)
ı -> I (dottless ı)

For investigation I am using phrase 'Anlıyorum bilgi edinin iİ'. Transformation TpUpper should give result 'ANLIYORUM BİLGİ EDİNİN İİ', but MAUI on Android platform is 'ANLIYORUM BILGI EDININ Iİ'

It's working fine with plain .NET8 console app, proof: https://dotnetfiddle.net/fIV4eQ

I found two issues reported at dotnet / runtime project that are describing that issue. Both were closed with comments that were pointing that root cause is Android ICU and dotnet familly can not do much about it.
#77689
#102622

But I started to dig a bit deeper. I found out that when I run Kotlin project with Android studio everything is fine. I forked some github project that is setting Locale to specific language. When I set 'en' it is giving me 'ANLIYORUM BILGI EDININ Iİ', but then setting 'tr' locale produces 'ANLIYORUM BİLGİ EDİNİN İİ'. Everything is proper at here. So why it's not with MAUI Android? Repository for my forked Kotlin project: https://github.com/sowacx/WizardOfLocale-maui-investigation
image

Then I tried to call setLocale the same way as in Kotlin App. That is possible by overriding MainActivity method AttachBaseContext. Code looks like that:

using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Content.Res;
using Android.OS;

namespace TurkishSandbox;

[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop,
    ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode |
                           ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
    protected override void AttachBaseContext(Context baseContext)
    {
        Java.Util.Locale locale = new Java.Util.Locale("tr");
        Java.Util.Locale.Default = locale;

        Resources res = baseContext.Resources;
        Configuration config = new Configuration(res.Configuration);
        if ((int)Build.VERSION.SdkInt >= 17)
        {
            config.SetLocale(locale);
            config.SetLayoutDirection(locale);
            baseContext = baseContext.CreateConfigurationContext(config);
        }
        else
        {
            config.Locale = locale;
            res.UpdateConfiguration(config, res.DisplayMetrics);
        }
        
        base.AttachBaseContext(baseContext);
    }
}

Additionally I set up Cultures to 'tr-TR' at MauiApplication c'tor. But that all did not fix problem.
image

My MAUI project to reproduce:
https://github.com/sowacx/maui-turkish-locale-issue

Steps to Reproduce

  1. Create MAUI Boilerplate app.
  2. Place Label at MainPage.xaml
    <Label Text="Anlıyorum bilgi edinin iİ" TextTransform="Uppercase" Style="{StaticResource Headline}" SemanticProperties.HeadingLevel="Level1" />
  3. Explicitly set up 'tr-TR' culture or change Android phone language to Turkish
  4. Run
  5. New label text should be 'ANLIYORUM BİLGİ EDİNİN İİ', but it is 'ANLIYORUM BILGI EDININ I'

Link to public reproduction project repository

https://github.com/sowacx/maui-turkish-locale-issue

Version with bug

8.0.40 SR5

Is this a regression from previous behavior?

No, this is something new

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android 14

Did you find any workaround?

Unfortunately no

Relevant log output

No response

Activity

  1. github-actions commented on Aug 16, 2024

    @github-actions
    Contributor

    Hi I'm an AI powered bot that finds similar issues based off the issue title.

    Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!

    Closed similar issues:

    Note: You can give me feedback by thumbs upping or thumbs downing this comment.

  2. transferred this issue fromdotnet/mauion Aug 16, 2024
  3. transferred this issue fromdotnet/androidon Aug 16, 2024
  4. ghost added
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Aug 16, 2024
  5. dotnet-policy-service commented on Aug 16, 2024

    @dotnet-policy-service
    Contributor

    Tagging subscribers to 'arch-android': @vitek-karas, @simonrozsival, @steveisok, @akoeplinger
    See info in area-owners.md if you want to be subscribed.

  6. dotnet-policy-service commented on Aug 16, 2024

    @dotnet-policy-service
    Contributor

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

  7. tarekgh commented on Aug 16, 2024

    @tarekgh
    Member
  8. added this to the 10.0.0 milestone on Aug 22, 2024
  9. matouskozak commented on Aug 22, 2024

    @matouskozak
    Member

    @sowacx thank you for this issue and the comparison with Kotlin. This is something we need to investigate and reconsider if there is a possibility for us to get the necessary culture information from Android ICU.

  10. removed
    untriagedNew issue has not been triaged by the area owner
    on Aug 22, 2024
  11. removed their assignment
    on Jan 28, 2025
  12. added
    good first issueIssue should be easy to implement, good for first-time contributors
    on Jul 18, 2025
  13. modified the milestones: 10.0.0, Future on Jul 18, 2025
  14. murathanoktem commented on Feb 10, 2026

    @murathanoktem

    Hi! I'd like to take a look at this issue and try to reproduce it on Android with tr-TR locale. If I can confirm the behavior, I'll propose a fix and open a PR. Is there any preferred repro project or test you’d like me to use?

  15. matouskozak commented on Feb 10, 2026

    @matouskozak
    Member

    Hi! I'd like to take a look at this issue and try to reproduce it on Android with tr-TR locale. If I can confirm the behavior, I'll propose a fix and open a PR. Is there any preferred repro project or test you’d like me to use?

    HI @murathanoktem, thanks for taking a look. For reproducing with MAUI, I would use the steps described in the issue body. However, if you want to reproduce it only in runtime, I would recomend using the sample app in src/mono/samples/Android. There are also bunch of globalization-related test disabled due to this so enabling those should verify the functionality: e.g.

    [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
    [ActiveIssue("https://github.com/dotnet/runtime/issues/60568", TestPlatforms.Android | TestPlatforms.LinuxBionic)]
    public static void IndexOf_TurkishI_TurkishCulture()

  16. murathanoktem commented on Feb 10, 2026

    @murathanoktem

    Hi @matouskozak, thanks! I’ll try the sample app approach first (src/mono/samples/Android) to reproduce on Android with tr-TR locale.

    Quick question: I’m building on Windows. Is it expected that I need VS 2022 “Desktop development with C++” (MSVC) to run this sample, or is there a way to reproduce using an existing Android workload without building the full native runtime?

    Once I can reproduce, I’ll report back with exact device/OS/.NET version and results, and then work on a fix/PR if it’s actionable.

  17. matouskozak commented on Feb 10, 2026

    @matouskozak
    Member

    I’m building on Windows. Is it expected that I need VS 2022 “Desktop development with C++” (MSVC) to run this sample

    I'm not sure. I remember we had issues with building Mono runtime reliably on Windows so I would recommend going through WSL if on Windows. You can try either Mono instructions https://github.com/dotnet/runtime/blob/main/docs/workflow/testing/mono/testing.md#android-1 or CoreCLR https://github.com/dotnet/runtime/blob/main/docs/workflow/building/coreclr/android.md. The used runtime should not have effect on the globalization behavior.

    or is there a way to reproduce using an existing Android workload without building the full native runtime?

    Yes, you should be able to reproduce this with the workloads in dotnet new maui (or dotnet new android) sample app. However, if the potential fix is supposed to be part of dotnet/runtime, you will need to build your version of runtime to be able to test it.

  18. murathanoktem commented on Feb 13, 2026

    @murathanoktem

    Repro (Android, tr-TR casing):

    Environment

    • Windows host, running Android emulator: Medium_Phone_API_36.0
    • Repro project created with: dotnet new android -n TrUpperRepro
    • Run: dotnet build -t:Run -c Debug
    • ADB: %LOCALAPPDATA%\Android\Sdk\platform-tools\adb.exe
    • Repro path: C:\Users\portal\tr_upper_repro\TrUpperRepro
    • Code: MainActivity.cs sets CurrentCulture/CurrentUICulture to tr-TR and logs ToUpper/ToLower results (tag: TRTEST)

    Observed (logcat)

    • When CurrentCulture=tr-TR, "i".ToUpper(tr-TR) returns "I" (U+0049)
    • Expected: "İ" (U+0130) for Turkish locale rules

    Relevant log lines:
    02-13 11:39:52.768 6464 6464 I TRTEST : CurrentCulture=tr-TR input='i' ToUpperInvariant='I' ToUpper(tr-TR)='I'
    02-13 11:39:52.809 6464 6464 I TRTEST : dotless upper: tr='I' (U+0049) inv='─▒' (U+0131)
    02-13 11:39:52.810 6464 6464 I TRTEST : dotted lower: tr='i' (U+0069) inv='─░' (U+0130)

    Note: Windows console shows garbled glyphs for U+0130/U+0131, so the repro logs the codepoints (U+XXXX) as well.

  19. murathanoktem commented on Feb 13, 2026

    @murathanoktem

    Extra env info:

    • .NET SDK: 9.0.310 (Host: 9.0.12)
    • Android emulator API level: 36 (ro.build.version.sdk=36)
  20. added a commit that references this issue on Aug 11, 2026
    4dc24c7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions