Flutter localization: step-by-step
Having one codebase for Android and iOS app sounds attractive. In the past we didn’t have a great experience doing that, but lately, Flutter is attacking that problem in its own way. However, if you build an app for different markets, you will most likely need to support multiple languages for your end-users.
In this article, we’ll focus on manually enabling localization for your Flutter application. In our other article we explained how to streamline Flutter localization workflow with Flutter Intl IDE plugin which we highly recommend.
Flutter Intl IDE plugin exceeded 100K installations on VS Code and Android Studio! 🎉
Still some work to do. Thanks for your feedback!#flutter #flutterdev— Localizely (@localizely) September 22, 2020
Part 1: Setting up your Flutter app
Flutter has good documentation on how to get started with a sample app, so we will not go into that here. We will describe how to introduce localization to your sample app. You can use Android Studio or Visual Studio Code as IDE, it is the same.
Step 1: Add dependencies
Start by defining these localization dependencies in your pubspec.yaml
:
dependencies: flutter: sdk: flutter flutter_localizations: # added sdk: flutter # added dev_dependencies: flutter_test: sdk: flutter intl_translation: # added
Install packages by running following command from Terminal:
flutter pub get
Step 2: Create localization classes
We can do this by creating AppLocalizations
and AppLocalizationsDelegate
classes in a separate lib/localizations.dart
file.
import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'l10n/messages_all.dart'; class AppLocalizations { AppLocalizations(); static const AppLocalizationsDelegate delegate = AppLocalizationsDelegate(); static Future<AppLocalizations> load(Locale locale) { final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString(); final String localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; return AppLocalizations(); }); } static AppLocalizations of(BuildContext context) { return Localizations.of<AppLocalizations>(context, AppLocalizations); } // Your keys defined below String get title { return Intl.message( 'Hello World', name: 'title', desc: 'Title for the Localized application', ); } String welcome(String name) { return Intl.message( 'Welcome {name}', name: 'welcome', desc: 'Welcome message', args: [name], ); } } class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> { const AppLocalizationsDelegate(); // Override with your list of supported language codes List<Locale> get supportedLocales { return const <Locale>[ Locale('en', ''), Locale('de', ''), ]; } @override bool isSupported(Locale locale) => _isSupported(locale); @override Future<AppLocalizations> load(Locale locale) => AppLocalizations.load(locale); @override bool shouldReload(AppLocalizationsDelegate old) => false; bool _isSupported(Locale locale) { if (locale != null) { for (Locale supportedLocale in supportedLocales) { if (supportedLocale.languageCode == locale.languageCode) { return true; } } } return false; } }
At the end of AppLocalizations
class we define all string keys we will use inside our app.
AppLocalizationsDelegate
class has a list of supportedLocales
. We should put all locales we support for end-users into that list.
Step 3: Update app widgets
Now, that we have defined 2 string keys in AppLocalizations
class, we can simply use them in our widgets.
In the following example, we use them to print the title on the top of the app page, and a simple welcome message with a placeholder.
import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'localizations.dart'; // your previous file class DemoApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(AppLocalizations.of(context).title), ), body: Text(AppLocalizations.of(context).welcome("John"))); } } class Demo extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( onGenerateTitle: (BuildContext context) => AppLocalizations.of(context).title, localizationsDelegates: [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: AppLocalizations.delegate.supportedLocales, // Note: AppLocalizations.of() // will only find the app's Localizations widget if its // context is a child of the app. home: DemoApp()); } } void main() { runApp(Demo()); }
When you want programmatically to change the current locale in your app, you can do it in the following way:
AppLocalizations.load(Locale('en', ''));
Step 4: Generate localization files
The code is now ready, but we miss localization files for languages we support. Flutter officially supports *.arb localization files.
Flutter offers a CLI tool that generates a default intl_messages.arb
file by scanning your AppLocalizations
class.
Create lib/l10n
folder and run the following command from the project root:
flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/localizations.dart
That arb file contains all key-value pairs we use in our Widgets.
Initially, we can copy the content of that file and put it into intl_de.arb
file in the same directory, and just translate the values into the German language.
Once we are completed with translations, we should run the following command in order to generate *.dart
files which our app will directly load:
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/localizations.dart lib/l10n/intl_*.arb
Step 5: Run the app
You can now build and run your application.
Part 2: Managing your Flutter localizations
Easy to manage a couple of strings in two languages? Sure. But as you add more features and expand to more markets, your app will grow to thousands of strings in a dozen of languages. Here’s how to automate your text string localization:
Step 1: Create a project in Localizely
Once you sign up to Localizely, just go to My projects page and tap “+” button (or explore the Sample Project if you wish to get more familiar with Localizely features first). Give your project a name and set your main and other languages. You can change the main language later if needed.
Each project gets a unique ID (you can find it in My projects page), that is needed when using API.
Step 2: Upload your files
Import your main intl_messages.arb
file to Localizely. Go to Upload page, select the file, and confirm. Alternatively, you can start by adding string keys in Localizely first.
Step 3: Invite team members
Localization work is a team effort. Switch to the Contributors section by clicking the icon in the side menu and start adding teammates. Any user can be granted admin privileges, i.e. the same rights on the project as you. For non-admin contributors, you can specify per-language access to the project specifying some languages as reference (read-only) or contributable (read and update). Only admins have access to string key changes, importing, exporting, settings, etc.
Step 4: Translate
Localizely has an editor on Translations page that reminds of Excel tables commonly used for translations management. Feel free to explore the Sample project or just take a look at Getting started guide to grasp the basic concept and options.
Step 4: Download the files
The translation part is done. How to get the localization data out of Localizely and make it usable in your Flutter app?
There are 2 options:
- Option 1: Download manually
Click Download icon in your project side menu and select Flutter ARB as the exporting format. Click Download to get the file. Then move downloaded .arb file into your project replacing the existing localization, that’s all.
- Option 2: Download via API
Depending on your project deployment setup, you may want to use Localizely API – it gives you a simple way to automatically generate and download localization files.
Like we mentioned in the beginning, this workflow can be simplified further by using Flutter Intl plugin explained in this article.