Localizing a Flutter app is not just translating a couple of texts: it's adapting the complete experience to different languages, regions, formats, and user preferences.
In my case, working with real projects led me to try several alternatives, and easy_localization ended up being my favorite for its speed, simplicity, and because it "just works" without turning every change into open-heart surgery.
In this guide, I'll show you how to professionally implement localization in Flutter, where problems usually appear, and how to solve them with a simple stack: JSON + Easy Localization + good practices.
What is Localization in Flutter and Why It Matters
i18n vs l10n explained simply
- i18n (internationalization): preparing the app to support multiple languages.
- l10n (localization): truly adapting it to those languages: texts, formats, reading direction, assets...
For example, when I changed an app that was only in English to French, I didn't just translate texts: I had to adapt dates, regional codes, and, on iOS, add the locales in Info.plist, something many forget.
Typical Problems When Working with Multiple Languages
- Duplicate texts in the code.
- Missing translations that break the UI.
- Dates and numbers in the incorrect format.
- Ignored RTL and broken layouts.
- Users who change the language but the app doesn't persist their selection.
I've suffered all of that myself, which is why I'm showing you how to avoid it.
Adding Translations to a Flutter Project Step-by-Step
In this section, we'll look at each of the steps we need to cover so that our Flutter application supports multiple languages.
Initial Project Configuration
Translating your app into multiple languages is a good idea when you have a service that everyone can benefit from and where the language barrier doesn't have to be a limitation.
The process in Flutter is simple, but there are several steps we need to cover so your app can support multiple languages.
Necessary dependencies:
In pubspec.yaml:
dependencies:
easy_localization:We configure the folders where the translation files will be:
flutter:
assets:
- assets/lang/The folder structure looks like this:
assets/
└── lang/
├── en.json
└── fr.json
As you can see, it uses the BCP-47 convention (IETF Language Tag); for example:
- en English (no specific region)
- en_US English of the United States
- en_GB English of the United Kingdom
- es Generic Spanish
- es_MX Spanish of Mexico
- fr_FR French of France
Your way of using it:
- en.json, fr.json, es.json → language only
- en_US.json, es_MX.json → language + region
Required Configuration on iOS (Info.plist)
On iOS, an additional configuration must be made:
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>fr</string>
</array>Example Translations
Here go ALL the fixed texts of your app; example:
en.json
{
"title": "Title",
"app_local_demo": "Application localization example!",
"demo_details": "This is a sample project to change the language."
}fr.json
{
"title": "Titre",
"app_local_demo": "Exemple de localisation d'application!",
"demo_details": "Ceci est un exemple de projet pour changer la langue."
}These are simply JSONs where the value is the translation and the key is the way we access that key.
Wrapping the App in EasyLocalization
Next, we edit our main.dart like this.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
runApp(
EasyLocalization(
supportedLocales: const [
Locale('en','US'),
Locale('fr','FR')
],
path: 'assets/lang',
fallbackLocale: const Locale('en','US'),
child: const MyApp(),
),
);
}To retrieve the words, simply import the package:
'title'.tr()Creating a Professional Language Selector in Flutter
You can create any type of selection widget to change the language:
class Language {
Locale locale;
String langName;
Language({
required this.locale,
required this.langName,
});
}We add the languages:
List<Language> languageList = [
Language(
langName: 'English - UK',
locale: const Locale('en'),
),
Language(
langName: 'French - FR',
locale: const Locale('fr'),
)
];Example DropdownButton:
DropdownButton<Language>(
value: selectedLang,
onChanged: (newValue) {
setState(() => selectedLang = newValue!);
context.setLocale(newValue.locale);
},
items: languageList.map((Language value) {
return DropdownMenuItem(
value: value,
child: Text(value.langName),
);
}).toList(),
);How to Persist the User's Language
To save the user's language selection, you can use, for example, Hive or SharedPreferences:
saveLocale(context.locale.toString());Frequently Asked Questions (FAQ)
- Can I use easy_localization together with intl?
- Yes, and in fact, that's what I recommend for dates and numbers.
- JSON or ARB?
- JSON = simple and fast.
- ARB = mandatory for very large apps or when using automatic generation.
- How do I avoid missing key errors?
- Activate the errorWidget and check logs during development.
- How to add more languages?
- Just add more JSON files in assets/lang and include them in supportedLocales
Conclusion
Localization is one of those tasks that seems simple, but if not done well, the app ends up showing mixed texts, poorly formatted dates, and frustrated users.
After trying multiple approaches, easy_localization became the fastest and most practical way for me to implement languages in real apps without sacrificing quality.
The key is to combine it with:
- Good file structure
- An intuitive selector
- Language persistence
- Good RTL and format practices
- Do that and you'll have a truly global app.
I agree to receive announcements of interest about this Blog.
App localization is very common in mobile app development, and I thought I'd write about one of my favorite Flutter packages. Internationalizing apps is quick and easy with the easy_localization package.