Zoran Luledzija
Zoran Luledzija
August 20, 2024
19 min read
August 20, 2024
19 min read

Flutter localization: step-by-step 

Flutter localization: step-by-step

The idea of maintaining a single codebase for both Android and iOS applications is quite appealing. In the past, our efforts to realize this concept met with limited success. Nonetheless, Flutter is confronting this challenge with an innovative approach. Moreover, its adaptability extends beyond just mobile platforms, enabling the development of applications for the web, desktop, and even embedded systems, all from a single codebase.

The benefits of crafting multiple applications from a single codebase cannot be overstated. However, the question arises: How does this benefit translate when addressing global markets that demand support for multiple languages? Initially, localization tools around Flutter were fairly rudimentary, but they have evolved significantly since then. Today, there is a plethora of robust localization solutions available. In the upcoming chapters, we'll delve into some of the most popular and effective tools for Flutter localization in depth.

Part 1: Setting up your Flutter app 

Flutter has recently introduced a new localization approach with the gen_l10n tool. This tool seamlessly integrates into the development workflow, automatically generating all necessary localization files. This integration enables developers to concentrate solely on crafting localization messages and implementing them in the app, while the gen_l10n tool takes care of the otherwise tedious task of producing the required localization code. This automation not only speeds up development but also significantly enhances the developer experience. In the upcoming chapters, we will guide you on how to effectively leverage this powerful localization tool.

All code samples used in this section are available on the GitHub repo.

Step 1: Add dependencies 

The first step towards supporting multiple languages in a Flutter application is to add the necessary dependencies into your Flutter project. Please run the commands provided below in your terminal to incorporate them:

flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl:any

After adding them, ensure the pubspec.yaml file is updated to reflect these changes.
Your dependencies section should look something like this:

dependencies:
  # Other dependencies...
  flutter_localizations:
    sdk: flutter
  intl: any

Step 2: Enable generation of localization files 

To enable automatic generation of localization files, please update the pubspec.yaml file by adding the following line to the flutter section.

flutter:
  generate: true
  # Other config...

Step 3: Configure localization tool 

Create a new l10n.yaml file in the root of the Flutter project. This file will hold the configuration for the gen_l10n tool. You can find the full list of configuration options in this document. However, in this guide, we will use just a few of them:

  • arb-dir – the path of the directory that contains the translation files.

  • template-arb-file – the name of the template arb file that will be used as basis for generating the Dart localization files.

  • output-localization-file – the name of the file for the output localization and localizations delegate classes.

Below, you can find the content of the l10n.yaml file used in this guide.

arb-dir: lib/l10n
template-arb-file: intl_en.arb
output-localization-file: app_localizations.dart

Step 4: Add translation files 

Next, let's add the l10n directory with three ARB files: intl_ar.arb, intl_en.arb, and intl_es.arb. These files are going to hold translations for Arabic, English, and Spanish, respectively. Below, you can see the project structure after adding these files and their content.

FLUTTER_PROJECT
|-- ...
|-- android
|-- ios
|-- lib
|   |-- l10n
|       |-- intl_ar.arb
|       |-- intl_en.arb
|       |-- intl_es.arb
|   |-- main.dart
|-- pubspec.yaml
|-- ...

The intl_ar.arb file:

{
  "helloWorld": "مرحبا بالعالم!"
}

The intl_en.arb file:

{
  "helloWorld": "Hello World!"
}

This intl_es.arb file:

{
  "helloWorld": "¡Hola Mundo!"
}

More info about ARB localization files can be found in a dedicated guide.

Note: If you're using VS Code, consider adding the ARB Editor extension. It enhances your ARB file editing experience with syntax highlighting, snippets, diagnostics, and quick fixes.

Note: The Flutter ARB file allows ICU syntax in messages. However, the gen_l10n tool is still in the active-development phase and does not yet fully support the entire ICU syntax. Reported issues include: multiple/nested plurals and selects in messages; the generator ignores plural forms: zero, one, and two.

Localization in the iOS apps 

According to the official documentation, for iOS apps, it is necessary to update the Info.plist file with the list of supported locales. However, there are some indications that automation of this process may be possible in the future. Until then, manually add the following snippet to support localization in iOS apps.

The ios/Runner/Info.plist file:

<key>CFBundleLocalizations</key>
<array>
  <string>ar</string>
  <string>en</string>
  <string>es</string>
</array>

Step 5: Run the app to trigger code generation 

To trigger the generation of the localization files, you need to run your application. After that, you will be able to see the generated code in the .dart_tool folder.

FLUTTER_PROJECT
|-- .dart_tool
|   |-- ...
|   |-- flutter_gen
|       |-- gen_l10n
|           |-- app_localizations.dart
|           |-- app_localizations_ar.dart
|           |-- app_localizations_en.dart
|           |-- app_localizations_es.dart
|       |-- ...
|-- android
|-- ios
|-- lib
|-- ...

Note: Each time you run flutter pub get or flutter run command, you will trigger code generation. For generating the same files independently of the application, you can use the flutter gen-l10n command as an alternative.

By default, the gen_l10n tool generates localization files as a synthetic package. Therefore, these files are not checked into the version control system (i.e., Git). However, if for some reason you want to track this code with the version control system, you will need to update your l10n.yaml file with synthetic-package and output-dir config parameters.

The l10n.yaml file:

# Other config...
synthetic-package: false
output-dir: lib/l10n

Step 6: Update the app 

The next step involves updating the MaterialApp widget with the localizationsDelegates and supportedLocales properties. So, let's import the generated app_localizations.dart file and set the needed values.

The main.dart file:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // ...
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      // ...
    );
  }
}

The AppLocalizations.localizationsDelegates represents the list of:

  • generated localizations delegate – provides localized messages from ARB files.
  • GlobalMaterialLocalizations.delegate – provides localized messages for the Material widgets.
  • GlobalCupertinoLocalizations.delegate – provides localized messages for Cupertino widgets.
  • GlobalWidgetsLocalizations.delegate – provides text direction for widgets.

These delegates allow us to use different languages throughout the app without much hassle. They also translate the interface of both Material and Cupertino widgets into around 115 languages (for instance, the Material date picker) and configure text directions within the app.

The AppLocalizations.supportedLocales represents the list of supported locales.

Finally, paste the snippet shown below somewhere in the code and hot-reload the app to see how everything works.

Text(AppLocalizations.of(context)!.helloWorld)

If you have followed all the steps from the beginning, you should be able to see the translated message. Note that whenever you change the language of the device to Arabic, English, or Spanish, the message will be updated.

Text direction: Left-to-Right and Right-to-Left 

Although most languages are written Left-to-Right (LTR), it is important to be aware that there are also Right-to-Left (RTL) languages. In this guide, we have included Arabic, which is an RTL language. Whenever the app is opened in Arabic, the layout should change accordingly. Generally, the Flutter framework updates the text direction within the app to correspond to the selected locale. In most cases, no adjustments are necessary. However, if the layout is designed exclusively for LTR languages or uses some hard-coded values, there is a possibility it may not display correctly. Be aware that adding an RTL language like Arabic to your app might also necessitate changes to your layout.

Escaping syntax 

Typically, the content of localization messages is straightforward. Nevertheless, on occasion, there is a need to incorporate messages with special characters, such as curly braces { and }. To prevent these tokens from being parsed, it is necessary to enable the use-escaping flag within the l10n.yaml file.

The l10n.yaml file:

use-escaping: true

Once escaping is enabled, utilize single quotes to escape special characters in your localization messages.

{
  "escapingExample": "In math, '{1, 2, 3}' denotes a set."
}

Avoid null checking 

By default, getters in the generated localizations class are nullable. To eliminate the need for repeated null checks, you can turn off the nullable-getter option in the l10n.yaml file.

The l10n.yaml file:

nullable-getter: false

After disabling nullable getters, you will be able to access the localization messages as shown below.

AppLocalizations.of(context).yourMessage

Get the selected locale 

To get the selected locale of the widget tree, use the snippet shown below.

Locale selectedLocale = Localizations.localeOf(context);

Override the locale 

In some rare cases, you might want to override the locale of one part of the widget tree. For that purpose, you should use the Localizations.override factory constructor. Below, you can see an example where the helloWorld message is shown regularly and with an overridden locale.

@override
Widget build(BuildContext context) {
  return Column(
    children: [
      Text(AppLocalizations.of(context)!.helloWorld),
      Localizations.override(
        context: context,
        locale: const Locale('es'),
        // Using a Builder here to get the correct BuildContext.
        child: Builder(
          builder: (BuildContext context) {
            return Text(AppLocalizations.of(context)!.helloWorld);
          },
        ),
      ),
    ],
  );
}

Custom locale resolution logic 

It's worth noting that Flutter supports custom locale resolution logic. If you need such thing for any reason, you simply need to provide a localeResolutionCallback to the MaterialApp. This callback will be called each time the Flutter app starts or when the user changes the language of the device.

MaterialApp(
  // ...
  localeResolutionCallback: (locale, supportedLocales) {
    // Add your custom resolution logic
    return locale;
  },
);

Change app language programmatically 

We've observed how Flutter can identify the device's language settings, which is the user's preferred language, and tailor the app accordingly. We've also explored methods for overriding the language in specific areas of the app. What about incorporating a manual language switcher within the app, though? Fortunately, this isn't a complex task. It simply requires setting the selected language as a locale property to the MaterialApp widget. For more details, please refer to the provided example.


Part 2: Managing your Flutter localizations 

Managing a couple of strings in two languages? Sure, that's easy. But as you add more features and expand into more markets, your app will grow to include thousands of strings across dozens of languages. Here's how to automate your Flutter localization:

Step 1: Create a project in Localizely 

Once you sign up to Localizely, just go to the My projects page and tap the “+” 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.


Flutter localization create project


Each project receives a unique ID, which can be found on the My projects page and is required when using the API.

Step 2: Upload your files 

Import your main ARB file into Localizely by navigating to the Upload page. Select the file and click confirm. Alternatively, you can begin by adding string keys directly in Localizely.


Flutter localization arb file upload


Step 3: Invite team members 

Localization work is a team effort. Click the icon in the side menu to switch to the Contributors section and add teammates. Any user can be granted admin privileges, i.e., the same rights on the project as you have. For non-admin contributors, you can specify access on a per-language basis. Assign some languages as reference (read-only) and others as contributable (read and update). Only admins have access to string key changes, importing, exporting, settings, and so on.


Flutter localization invite team member


Step 4: Translate 

Localizely has an editor on the Translations page that resembles Excel tables commonly used for translation management. Feel free to explore the Sample project or take a look at the Getting started guide to grasp the basic concepts and options.


Flutter localization translate


Step 5: 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 the Download icon in your project's side menu and select Flutter ARB as the exporting format. Click the Download button to obtain the file. Then move the downloaded .arb file into your project, replacing the existing localization; that’s all.


    Flutter localization download arb file


  • Option 2: Download via API

    Depending on your project deployment setup, you may want to use the Localizely API. It gives you a simple way to automatically generate and download localization files.

Like this article? Share it!


Zoran Luledzija
Zoran Luledzija

Zoran is a Software Engineer at Localizely. His primary interest is web development, but he also has a solid background in other technologies. For the last few years, he has been involved in the development of software localization tools.

Enjoying the read?

Subscribe to the Localizely blog newsletter for quality product content in your inbox.

Related

Navigating localization success with Localizely

Step into the world of easy localization

Copyrights 2024 © Localizely