Convenient internationalization of Flutter apps

4 minute read

There are many ways to have multiple languages in your app in Flutter. Some folks use canonical Flutter arb files. Other use some json equivalents or statically typed constant resources like r_flutter. In this post I’m going to show you the way I find the most convenient to setup and use.

🚨 Warning: This guide was valid until Flutter 1.22. Please check the updated version here.

See the short video version:

Foreword

While researching the i18n1 topic I found several different ways of adding multiple languages support in Flutter:

As you can see there’s no single best way to handle this right now. All of them share some common steps, though. Together with our colleagues we also have been using a custom localization solution with some bash scripts to fetch and convert localization json files. Unfortunately this causes delays when onboarding new people and starting new projects.

My goal was to find a solution that:

  • is relatively quick to setup in new project (either is single time manual setup or fully automatic),
  • supports {placeholders} and preferably plural forms,
  • would be nice to rely on arb files which are native to Flutter.

After few days of hacking I think I found something that I can share with you.

General overview

Simply speaking my solution is as follows:

  1. Use some tool to translate your app (POEditor, Localizely, Loco, CrowdIn…).
    • Not all of them support exporting arb files. In that case export json and convert it to arb with arb-converter-cli2
  2. Fetch translation files from API directly in VS Code (and convert them to arb if necessary)
  3. Automatically generate translation classes with Flutter Intl extension
  4. Be happy with statically typed translations with plurals and placeholder support

This approach requires some configuration steps once but later is mostly automatic due to VS Code extension and tasks in VS Code.

Translation services and arb files

Only few translation services support arb files out of the box. This is the list I came up with:

  • Localizely - arb supported ✔️
  • Crowdin - arb supported ✔️ but a bit tricky (e.g. names not changed automatically and source file has to be arb too)
  • POEditor - not supported, but json can be converted to arb without plural support ✔️
  • Loco - arb supported, but without placeholders and genders ✔️
  • Lingohub - not supported
  • Weblate - not supported
  • Transifex - not supported
  • Pootle - not supported

There are also some standalone editors like this web-based (without plurals support) and BabelEdit which is a desktop editor.

In my case the most convenient to use was POEditor, but the json file couldn’t be easily converted to arb2. Thus, I stayed with Localizely for now. Both have free plans that should be enough for smaller apps.

By the way, a typical arb file can look like this:

{
  "@@locale" : "en",
  "page_home_counter" : "{count, plural, one{You have pushed the button once} other{You have pushed the button {count} times}}",
  "@page_home_counter" : {
    "type" : "text",
    "description" : ""
  },
  "page_home_title" : "Hello {name}",
  "@page_home_title" : {
    "type" : "text",
    "description" : ""
  }
}

VS Code/Android Studio extension - Flutter Intl

One of the best tools to streamline internationalization of Flutter apps is Flutter Intl extension (also available for Android Studio). It generates the boilerplate getters for translation keys. It will also generate special methods if translation term has placeholder or plural version. Unfortunately, it’s closed source.

The setup is pretty straightforward and I recommend you to read the README.

In short, if you have enabled Flutter Intl extension, then you just put your arb files in l10n directory and the extension generates Dart files for you. The extension automatically updates available locales and generates localization delegate. You just need to set them in your MaterialApp:

MaterialApp(
    // these are generated by the Flutter Intl extension
    localizationsDelegates: [S.delegate],
    supportedLocales: S.delegate.supportedLocales,
    title: 'Flutter Demo',
    home: MyHomePage(title: 'Flutter Demo Home Page'),
);

Then you can reference translation terms in you widgets via:

Text(
    S.of(context).page_home_counter(_counter),
    // 'You have pushed the button {count} times',
),

And if you take a look at the source of the generated method you’ll see typical Flutter intl package handling:

String page_home_counter(dynamic count) {
    return Intl.plural(
        count,
        one: 'You have pushed the button once',
        other: 'You have pushed the button $count times',
        name: 'page_home_counter',
        desc: '',
        args: [count],
    );
}

Fetching translation updates with VS Code

VS Code allows to set custom tasks which are arbitrary shell commands. That’s what I wanted to utilize to semi-automatically fetch translation files from Localizaly or POEditor API. I prepared simple bash script to download arb files from Localizely and json files from POEditor. You can take a look at the tasks.json file in this gist if you want to use them from VS Code. The script is available here.

Wrap up

I hope this short tutorial helped you to improve your internationalization workflow. The arb files are very powerful, but at the same time almost no translation services support it. There is one issue in the Flutter GitHub project that holds the discussion about the simplified i18n process. I really recommend you to read it.

The app code and download script are available in the project repository.


  1. i18n stands for internationalization which is just adding multiple languages to the app, whereas l10n stands for localization which is adapting your app to given locale/culture/region (e.g. different layout or navigation). 

  2. Unfortunately json conversion works for very simple terms without plural version but works OK with placeholders.  2

Updated: