DEVLOPN Audit

Your French user opens your app and it's in Japanese — locales in Flutter

PlatformDispatcher.locale vs Localizations.localeOf — two ways to get the locale in Flutter, and the supportedLocales order that decides silently.

Your French user opens your app. It's in Japanese. They never asked for that.

In Flutter, there are two ways to get the locale, and everyone mixes them up.

Device language — the locale trap between system preference and what the app actually displays: PlatformDispatcher → fr, localeOf → ja

The two locales

PlatformDispatcher.instance.locale is the phone's raw language. The system settings. It doesn't care what your app can actually display.

Localizations.localeOf(context) is the language your app really uses, after Flutter's resolution.

The surprise scenario

Your app supports Japanese and English, not French. The platform reports fr for your user — their real preference. localeOf returns ja. 😯

Why Japanese and not English? Because by default, when no language matches, Flutter picks the first entry of your supportedLocales list. And in yours, ja comes before en.

Nobody decides "this user gets Japanese." It's just the order of your list deciding, silently.

// Device in "fr", app that doesn't support French
supportedLocales: [Locale('ja'), Locale('en')],
// fr unsupported -> ja (first in the list)

supportedLocales: [Locale('en'), Locale('ja')],
// fr unsupported -> en (already more reasonable)

Takeaways

  • PlatformDispatcher.instance.locale = system preference, never guaranteed displayable.
  • Localizations.localeOf(context) = what the app actually shows.
  • The first entry of supportedLocales is your fallback language: choose it deliberately.