Skip to content

Uses system default locale for text rendering when preferred locale isn't supported - #195997

Merged
auto-submit[bot] merged 9 commits into
flutter:masterfrom
gaaclarke:os-locale-font-fallback
Nov 12, 2026
Merged

auto-submit[bot] merged 9 commits into
flutter:masterfrom
gaaclarke:os-locale-font-fallback

Conversation

@gaaclarke

@gaaclarke gaaclarke commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

Description

Makes the engine render text with the system locale when the user's preferred locale isn't one of the app's supported locales.

fixes #194931

Technical details

When the user's preferred locale isn't one of the app's supportedLocales, WidgetsApp falls back to another locale (en_US by default) for localization. RichText and EditableText were passing that fallback locale to the engine for text rendering. With this PR they pass no locale in that case, so the platform's default locale is used (Skia's behavior). On Windows, an app that doesn't list Chinese now renders Han characters with Chinese fonts on a Chinese system, instead of Japanese fallback fonts.

Alternatives considered

1) Adding new TextLocale API

This PR adds widgets that are not publicly available. Deciding on a new public API requires more forethought and maintenance than keeping one private. I've decided to keep it private for now and we can consider promoting it or replacing it with a public API later. I think the sorts of usage of Flutter that would require it are very rare, something like making a whole new alternative framework on dart:ui.

2) Considering secondary preferred locales

This PR only checks the user's first preferred locale. Counting any preferred locale would help a [ja_JP, zh_CN] user of a zh_CN app: the app's Chinese strings would keep zh_CN glyphs instead of the OS default (likely Japanese). But it would bring back the original bug for the more common [zh_CN, en_US] user of an English-only app. That app resolves to en_US, its en_US locale would be passed to the engine, and Han text would render with Japanese fonts again. I chose to handle the common case and documented the trade-off in a test. Apps can opt out with an explicit WidgetsApp.locale or TextStyle.locale.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

@github-actions github-actions Bot added a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. labels Nov 4, 2026
@gaaclarke gaaclarke changed the title Starts using system default locale for text rendering when no locale is Uses system default locale for text rendering when no locale is specified Nov 4, 2026
@gaaclarke gaaclarke changed the title Uses system default locale for text rendering when no locale is specified Uses system default locale for text rendering when preferred locale isn't supported Nov 4, 2026
@gaaclarke
gaaclarke marked this pull request as ready for review November 4, 2026 22:43
@gaaclarke gaaclarke added the CICD Run CI/CD label Nov 4, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces TextLocaleScope and textLocaleOf to prevent text widgets from using the application's fallback locale when the user's preferred locale is unsupported, allowing the operating system's default locale to be used for font fallback. Feedback indicates a potential regression for multi-locale users and language-only matches because the implementation only checks the first preferred locale and requires an exact match. It is recommended to check all preferred locales for matching language codes and to add a test case verifying this multi-locale behavior.

@gaaclarke
gaaclarke requested a review from chunhtai November 4, 2026 23:14
@gaaclarke

Copy link
Copy Markdown
Member Author

@chunhtai can you give this a look please? I thought you you since you have experience in both CJK languages and the framework. I've tried to make the description clear, let me know if you want to chat about it.

@chunhtai chunhtai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I felt this is more like a documentation issue

If the app is rendered in the resolved locale, I see no issue passing that into textpainer as default. Either the developer needs to set a correct locale in supported locale or preferred locale, or they can use defaultTextStyle to change the locale of a sub tree.

The only issue I saw is that we default locale to en_US if not set, but in that case the APP is rendered in en_US.

If we really want to do something for the default, then probably we need a way to tell if the app is using default locale and only pass in null in that case, but I am not sure whether that worth it though.

@gaaclarke

Copy link
Copy Markdown
Member Author

If we really want to do something for the default, then probably we need a way to tell if the app is using default locale and only pass in null in that case, but I am not sure whether that worth it though.

Like keeping the locale null, and only resolving as the default (en_US) when it's used. We could do that but it would require a lot of distributed places to edit to make it resolves correctly. Or we could introduce a new type, OptionalLocale which would make it easier to make sure we got it correct.

Gut check: the fact that someone developing an app in Chinese, doesn't specify anything, runs it, and gets Japanese characters just seems bad to me. @chunhtai , am I overestimating what a problem that is? The screenshots kind of show the Japanese characters as being muddy. I wondered if they were muddy because they are more rare in Japanese.

@gaaclarke
gaaclarke requested a review from chunhtai November 5, 2026 21:15
@chunhtai

chunhtai commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

the fact that someone developing an app in Chinese, doesn't specify anything, runs it, and gets Japanese characters just seems bad to me

This is only because the OS decide the render chinese in en-US as japanese and Chinese if no locale supplied. To me that sounds like a platform bug, but I guess we probably need to do something here.

require a lot of distributed places to edit to make it resolves correctly.

I think your current approach is fine, just need a few adjustment.

We should make the API public since I imagine people who don't use Text widget will still need to access the new field, and we can probably put the new fields in existing class like Localization or WidgetsApp.

Locale locale = null;
if(! Localizations.isApplicationLevelOf(context) || !Localizations.noDefaults(context)) {
  locale = Localizations.maybeLocaleOf(context);
}

return RenderParagraph(
  locale: locale,
)

This example remove the condition where we check whether the current locale matches fallback because I worry that we may run into a opposite situation where people wants to send the resolved locale over to text painter because the app is rendered in that locale. I think it is probably better if the logic is consistence, and it will be up to developer to either use a Localization.override for a subtree or using DefaultTextStyle to override the locale in textstyle. WDYT?

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown

This pull request contains changes to Material or Cupertino, which are currently frozen in this repository.

Changes should be made in material_ui and/or cupertino_ui in the flutter/packages repository.

Please refer to #188444 for instructions.

@github-actions github-actions Bot added p: material_ui material_ui package in flutter/packages f: cupertino labels Nov 9, 2026
@github-actions github-actions Bot removed p: material_ui material_ui package in flutter/packages f: cupertino labels Nov 9, 2026
@gaaclarke

Copy link
Copy Markdown
Member Author

This example remove the condition where we check whether the current locale matches fallback because I worry that we may run into a opposite situation where people wants to send the resolved locale over to text painter because the app is rendered in that locale. I think it is probably better if the logic is consistence, and it will be up to developer to either use a Localization.override for a subtree or using DefaultTextStyle to override the locale in textstyle. WDYT?

@chunhtai I'm kind of out of my element here. I trust your opinion on the matter. I've updated it so that we explicitly keep track wether we are using the default locale or not. It can be overwritten with Localizations.override. Is this what you had in mind? It seems to mirror what we are doing with IsApplicationLevel. PTAL.

@chunhtai

chunhtai commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

I've updated it so that we explicitly keep track wether we are using the default locale or not. It can be overwritten with Localizations.override. Is this what you had in mind?

yup that sounds good to me.

i wrote the example very quick so it seems like we don't really need the isapplicationLevel. if you can do it with one new api then I think it is fine.

@gaaclarke

Copy link
Copy Markdown
Member Author

@chunhtai okay, sgtm. It's ready for review when you have a chance (I can't rerequest in the current state)

@chunhtai

Copy link
Copy Markdown
Contributor

left two comments, the approach LGTM

@gaaclarke
gaaclarke requested a review from chunhtai November 10, 2026 21:05
final RenderParagraph noLocale = paragraphs[0];
final RenderParagraph withLocale = paragraphs[1];
expect(noLocale.locale, isNull);
expect(withLocale.locale, enUS);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think this test is meaningful after this point, in unittest the only available font is ahem and doesn't have locale difference.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we can test this unless it is a integration test. my previous comment is just to ask for manual confirmation that code logic in skia won't do anything unexpected when putting null as locale for english text

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Gotcha removed. One way to think about that question is, what does it look like when you draw latin text when you specify zhCH as the locale? That's what it would look like. I think that can technically result in different rendering. However I would expect that to be more consistent with the rest of the experience on the machine. If you are asking about what skia does, that's kind of the limit of my familiarity. I don't maintain Skia, so I'm just relying on them to behave correctly. FWIW this remedy was suggested by someone on the skia team.

@gaaclarke
gaaclarke requested a review from chunhtai November 11, 2026 01:15

@chunhtai chunhtai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@gaaclarke gaaclarke added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 11, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Nov 11, 2026
Merged via the queue into flutter:master with commit ec93269 Nov 12, 2026
21 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 12, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Nov 12, 2026
flutter/flutter@b98afb5...9c427b5

2026-11-12 [email protected] Roll Skia from d67d0f22e3ec to 9aa2513a4ac9 (2 revisions) (flutter/flutter#196429)
2026-11-12 [email protected] [flutter_tools] Move CpuArch to flutter_tools_core and define HostPlatform by osName and cpuArch (flutter/flutter#195841)
2026-11-12 [email protected] Roll Skia from 23e7e63789b8 to d67d0f22e3ec (2 revisions) (flutter/flutter#196424)
2026-11-12 [email protected] Uber compactify (flutter/flutter#196392)
2026-11-12 [email protected] Roll Skia from 3d7e335da165 to 23e7e63789b8 (12 revisions) (flutter/flutter#196422)
2026-11-12 [email protected] Roll Dart SDK from 13af329ea4f2 to e5add7a25b32 (1 revision) (flutter/flutter#196413)
2026-11-12 [email protected] Don't terminate the shared EGL display when an engine is disposed (flutter/flutter#194520)
2026-11-11 [email protected] Adds manifest flag to turn on SDFs for Android. (flutter/flutter#196056)
2026-11-11 [email protected] Remove bringup from Linux_pixel_7pro integration_ui_keyboard_inset_jump (flutter/flutter#196334)
2026-11-11 [email protected] Uses system default locale for text rendering when preferred locale isn't supported (flutter/flutter#195997)
2026-11-11 [email protected] [android]: prewarms shaders when the context is created (flutter/flutter#196069)
2026-11-11 [email protected] iOS: Recomposite platform views only if their parameters change (flutter/flutter#196287)
2026-11-11 [email protected] [flutter_tools] Forward encoding in ErrorHandlingFile.readAsStringSync (flutter/flutter#196136)
2026-11-11 [email protected] Roll Skia from b89c24bbab8c to 3d7e335da165 (8 revisions) (flutter/flutter#196390)
2026-11-11 [email protected] Add Flutter Gardener instructions on how to disable a test (flutter/flutter#196312)
2026-11-11 [email protected] [Impeller] Enable malioc analysis for framebuffer_blend shaders (flutter/flutter#196343)
2026-11-11 [email protected] [flutter_tools] Migrate general.shard Android, asset, and build tests off globals (flutter/flutter#196125)
2026-11-11 [email protected] Roll Dart SDK from 8f15c4ca2d41 to 13af329ea4f2 (1 revision) (flutter/flutter#196386)
2026-11-11 [email protected] [Impeller] Do not clamp the local coverage limit to the root render target (flutter/flutter#195860)
2026-11-11 [email protected] [Impeller] Use separate uniform buffers on VMware SVGA3D (flutter/flutter#196022)
2026-11-11 [email protected] [flutter_tools] Introduce AndroidSdkProvider for lazy native_assets SDK lookup (flutter/flutter#196122)
2026-11-11 [email protected] Moves linux to ThinLTO. (flutter/flutter#196314)
2026-11-11 [email protected] [flutter_tools] Migrate bin/fuchsia_tester.dart off globals and runInContext (flutter/flutter#196074)
2026-11-11 [email protected] Changes the tolerances for frame_rate_main_test.dart. (flutter/flutter#196304)
2026-11-11 [email protected] Roll Packages from 08faf64 to 653628e (16 revisions) (flutter/flutter#196381)
2026-11-11 [email protected] Allow SliverCrossAxisGroup children to lay out with zero cross-axis extent instead of asserting (flutter/flutter#191287)
2026-11-11 [email protected] Roll Skia from cc4c54f752ba to b89c24bbab8c (4 revisions) (flutter/flutter#196375)
2026-11-11 [email protected] [Linux] Fix layer offset when compositing with glBlitFramebuffer (flutter/flutter#196189)
2026-11-11 [email protected] [flutter_tools] Inject CrashReportSender into SwiftPM migration (flutter/flutter#196315)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems CICD Run CI/CD framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

2 participants