
Flutter Localization: A Practical Guide to Supporting Multiple Languages
Keywords:
Flutter Localization: A Practical Guide to Supporting Multiple Languages
If you've ever shipped an app to more than one country, you already know localization isn't just "translate the strings and call it done." I've gone through this on a handful of production Flutter apps now — a calorie tracker with Arabic and English, a widget app in eight languages — and the gap between "it compiles" and "it actually reads right for a native speaker" is bigger than most tutorials let on. Here's what actually matters.
Why localization breaks apps that "technically" support it
The most common mistake I see is treating localization as a find-and-replace job for text. It's not. Once you add a second language, you're also dealing with:
- Text that expands or contracts (German strings run long, Chinese runs short)
- Right-to-left layouts for Arabic, Hebrew, Urdu
- Number, date, and currency formatting that differs by locale
- Pluralization rules that aren't just "singular vs plural" (Arabic has six plural forms)
If your app only handles the happy path — English, left-to-right, singular/plural — it'll look fine until a real user in Riyadh or Berlin opens it and something clips, mirrors wrong, or reads awkwardly.
Two real approaches: flutter_localizations vs easy_localization
Flutter gives you flutter_localizations with .arb files out of the box, generated through flutter gen-l10n. It's the "official" route, and it's solid once set up — strongly typed, compile-time checked, no runtime string lookups that can silently fail.
The alternative is a package like easy_localization, which uses JSON files and loads translations at runtime. It's faster to bolt onto an existing app and easier for non-developers (like a translator or a client) to edit directly, since it's just JSON instead of the .arb format with its metadata blocks.
Neither is objectively better. For a new app, I'd lean toward flutter_localizations — the compile-time safety catches missing keys before they hit production. For an app that already has hardcoded strings scattered everywhere and needs localization retrofitted fast, easy_localization gets you there with less refactoring.
.arb vs JSON, side by side
Same plural rule, two formats — this is roughly what you're choosing between:
1// app_en.arb (flutter_localizations) { "itemCount": "{count, plural, =0{No items} =1{One item} other{{count} items}}", "@itemCount": { "placeholders": { "count": { "type": "int" } } } }
2
3// en.json (easy_localization) { "itemCount": { "zero": "No items", "one": "One item", "other": "{} items" } }The .arb format carries metadata alongside the string (types, descriptions, placeholders), which is what powers the compile-time checks. JSON is plainer and easier to hand-edit, but you lose that safety net.
Setting up .arb files without the headaches
Your .arb files live in lib/l10n/, one per locale — app_en.arb, app_ar.arb, and so on. A key thing people miss: keep your English file as the single source of truth and never let translators edit keys, only values. I've seen translated .arb files with renamed keys that quietly broke the build because the generator expects matching keys across locales.
Also worth doing early: add ICU plural and gender syntax where it applies, even if you only support English at first. See the snippet above for what that looks like in practice. Retrofitting plural logic after the fact means touching every screen that displays a count — do it once, at the start.
RTL support is not optional if you have Arabic or Hebrew users
Flutter handles RTL mirroring automatically for most widgets when the locale is right-to-left — padding, alignment, icons with directional variants. But it doesn't catch everything.
RTL things that commonly break
- Custom
Row/Positionedwidgets using hardcoded left/right instead ofstart/end - Icons that imply direction (arrows, chevrons) not swapped for RTL
- Charts or custom painters that assume LTR drawing order
The fix is almost always the same: swap left/right for EdgeInsetsDirectional and Alignment.startEnd equivalents, and test the app with the device locale actually set to Arabic — not just a debug flag. Things look different once real Arabic text is filling the space instead of a placeholder.
(If you're documenting this for your own team, a side-by-side screenshot of the same screen in LTR and RTL is worth a thousand words — use alt text like "Flutter RTL layout comparison, English vs Arabic" so it's discoverable in image search too.)
Don't forget these three things
Pseudo-localization for QA. Before sending an app to real translators, run it with pseudo-localized strings (padded and accented English) to catch layout issues without waiting on translations.
Locale-aware formatting. Use the intl package's DateFormat and NumberFormat instead of manually formatting dates and currency. Manual formatting is the single most common source of "why does the date look wrong in France" bug reports. The official Flutter internationalization guide is worth bookmarking alongside it.
App store metadata. Localization isn't just in-app strings — your App Store and Play Store listings need translated titles, descriptions, and keywords too, or your install conversion in non-English markets stays flat even with a fully localized app.
FAQ
What's the difference between flutter_localizations and easy_localization? flutter_localizations is Flutter's official solution, built on .arb files and compile-time code generation via flutter gen-l10n. easy_localization is a third-party package that loads JSON translation files at runtime. The official route gives you compile-time safety; the third-party one is quicker to retrofit onto an existing codebase.
Does Flutter support RTL languages automatically? Partially. Built-in widgets mirror correctly out of the box, but custom layouts using hardcoded left/right values, directional icons, and custom painters need manual RTL handling.
How do I handle plurals correctly in Flutter? Use ICU plural syntax inside your .arb files (or the equivalent structure in JSON if using easy_localization), rather than manually checking if (count == 1). Some languages, like Arabic, have up to six plural forms, not just singular and plural.
Do I need separate app store listings for each language? Yes. In-app localization and store listing localization are separate systems — translating your UI doesn't automatically translate your App Store or Play Store title, description, or keywords.
Wrapping up
Localization done well is invisible — users just experience an app that feels native to them. Done poorly, it's a string of small annoyances that add up to distrust in the product. Start with a solid .arb or JSON structure, handle plurals and RTL from day one instead of retrofitting them, and always test with real translated content before shipping. It's tedious work, but it's also one of the more direct ways to grow an app's reach beyond its home market.
