Localizely logo
  • Home
  • Getting started
  • How it works
  • Pricing
  • FAQ
Sign up
Contents
  • Why we built Flutter Intl and intl_utils
Zoran Luledzija
Zoran Luledzija
October 12, 2026
6 min read
October 12, 2026
6 min read
codinglocalization

Migrating from Flutter Intl and intl_utils to gen-l10n 

Migrating from Flutter Intl and intl_utils to gen-l10n

As of October 2026, the intl_utils package is no longer maintained. The same applies to Flutter Intl, the plugin for Android Studio and the extension for VS Code built on top of it. Your projects keep working, and nothing changes on your side today. We do recommend migrating to Flutter's built-in gen-l10n, though, and this guide shows how to do it with minimal changes to your project.

gen-l10n has matured over the years and does the job Flutter Intl and intl_utils were created for. Below, we explain why we built them, what has changed since then, and how to migrate.

Why we built Flutter Intl and intl_utils 

At the beginning of 2020, the usual way to localize a Flutter app was the intl package together with intl_translation. It worked, but it asked a lot of the developer. Every string needed a hand-written Intl.message wrapper in Dart. Every time a string was added or changed, you ran the extract_to_arb command to pull the strings out of the code, and generate_from_arb to turn the translations back into Dart. The extraction itself is nothing unusual. Separating translatable strings from code is a practice that software localization established long before Flutter, for a simple reason: translators translate, they do not code. The problem was the amount of manual work around it.

The idea behind Flutter Intl and intl_utils was to generate that boilerplate for you, so that the repetitive wiring took no time at all. Both treated the ARB files as the single source of truth: every string in the app lived in an ARB file. Whenever one of those files changed, Flutter Intl noticed it in the IDE and ran intl_utils under the hood, which generated a Dart getter or method for every key. Developers got autocompletion for keys and a compile error instead of a runtime surprise when a key went missing, and translators got plain ARB files to work with.

Over time, Flutter Intl grew into a small localization workflow of its own: adding and removing languages in the project, extracting hard-coded strings from Dart code into ARB files, and exchanging ARB files with Localizely to streamline translation even further.

The tools reached far more developers than we ever expected, and we are grateful for every one of them. Many of the projects built with them are still around today and deserve a proper way forward, not just a notice. The rest of this post is exactly that.

What has changed since 

Not long after Flutter Intl and intl_utils came out, Flutter shipped the first version of gen-l10n, a tool built into the Flutter SDK with the same goal: generating the localization boilerplate for you. It has improved a lot since then. The early rough edges are gone, and it gained options such as deferred loading, escaping, relaxed syntax, and reports of untranslated messages. Google's ARB Editor extension for VS Code added syntax highlighting, diagnostics, and quick fixes for the ARB files themselves.

Localizely adapted every Flutter-related feature to work with gen-l10n as well: Over-the-Air updates, In-Context Editing, the CLI, and the GitHub, GitLab, and Bitbucket integrations for syncing ARB files.

Being part of the Flutter SDK gives gen-l10n a level of integration that a side tool can never match. There is nothing to install, no plugin or extension to set up, and the code is generated as part of flutter pub get, flutter run, and flutter build rather than by something watching your files from the outside. Keeping a second tool alive for something the Flutter SDK already covers no longer makes sense. Flutter Intl and intl_utils solved a problem that existed at the time. That problem is now solved better, at the source.

What this means for you 

  • No further releases. Flutter Intl and intl_utils no longer receive updates, including compatibility fixes for new Dart and Flutter releases. The published versions remain available on pub.dev and in the IDE marketplaces.
  • Existing projects keep working. At some point, though, a new Dart or Flutter release will be incompatible with the last intl_utils version, which is why we recommend migrating before that happens.
  • The repositories stay online. The GitHub repositories will be archived after a grace period. Archived repositories are read-only, but the code, the documentation, and the issue history remain accessible.
  • Localizely projects are not affected. Your ARB files, translations, and integrations stay as they are. Only the tooling in your project changes, and the guide below covers that.

Migration guide 

Our Flutter localization guide explains gen-l10n from scratch. This section covers only what changes when you move an existing Flutter Intl project to it.

Step 1: Move the configuration 

Flutter Intl kept its settings in the flutter_intl section of pubspec.yaml. gen-l10n reads its settings from an l10n.yaml file next to it.

In pubspec.yaml, remove the flutter_intl section and the intl_utils dev dependency, and add generate: true to the flutter section. Keep flutter_localizations and intl:

dependencies:
  flutter_localizations:
    sdk: flutter
  intl: any

flutter:
  generate: true
  uses-material-design: true

Then create l10n.yaml in the project root with the values from your old flutter_intl section. With the Flutter Intl defaults, it looks like this:

arb-dir: lib/l10n                # flutter_intl: arb_dir
template-arb-file: intl_en.arb   # flutter_intl: main_locale
output-dir: lib/generated        # flutter_intl: output_dir
output-localization-file: l10n.dart
output-class: S                  # flutter_intl: class_name
nullable-getter: false           # keeps S.of(context) non-nullable, as before

Your ARB files need no changes.

Note: On Flutter versions older than 3.32, also add synthetic-package: false to l10n.yaml. Generating into a synthetic package was removed in Flutter 3.35, so there is no point in starting with it.

Step 2: Generate the code 

Delete the old generated files first. intl_utils kept message tables in lib/generated/intl/, which gen-l10n does not use. Then generate the new ones:

rm -rf lib/generated
flutter pub get
flutter gen-l10n

You get lib/generated/l10n.dart with the S class and one l10n_<locale>.dart file per locale. From now on, Flutter regenerates them on every flutter pub get, flutter run, and flutter build.

gen-l10n reads the same ARB files, so this step usually goes through without errors. If it reports a problem, the error names the key and what is wrong with it. One difference the compiler cannot catch for you: when a message has several placeholders of the same type, the two tools may order the generated parameters differently, so check those call sites.

Step 3: Update your Dart code 

The generated/l10n.dart import and every S.of(context).someKey call stay as they are. Only the static API around the class changes:

  • S.delegate.supportedLocales becomes S.supportedLocales. In most apps, that is the only change in MaterialApp.
  • S.current and S.load() have no equivalent in gen-l10n, so code that relies on them needs rework.

Run flutter analyze to find anything else.

Note: This guide keeps the old S class and l10n.dart file names on purpose. If you prefer the gen-l10n defaults, AppLocalizations and app_localizations.dart, remove output-class and output-localization-file from l10n.yaml and rename the usages in your code.

Step 4: Adjust your workflow 

  • In CI scripts, replace dart run intl_utils:generate with flutter gen-l10n, or drop it, since flutter build generates the code anyway.
  • From now on, add new keys directly to the template ARB file. Nothing in the Flutter ecosystem currently offers an "extract to ARB" action, but the ARB Editor extension helps with the metadata.

Step 5: Update the Localizely setup 

Skip this step if you do not use Localizely. The localizely section of flutter_intl did two things, and both have a gen-l10n replacement.

Syncing ARB files. The upload and download commands and IDE actions are replaced by the Localizely CLI. Run localizely-cli init once to connect the project; localizely-cli push and localizely-cli pull then do what the old commands did. The configuration file it creates also drives the GitHub, GitLab, and Bitbucket integrations, if you prefer pull requests with fresh translations over running a command.

Over-the-Air updates and In-Context Editing. With Flutter Intl, ota_enabled: true made intl_utils generate the metadata the Localizely SDK needs. With gen-l10n, the SDK generates it itself. The Over-the-Air and In-Context Editing guides cover the setup.

Closing note 

Thank you to everyone who used these tools, reported bugs, suggested features, and sent pull requests over the years. We hope the migration goes smoothly.

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

How to translate WordPress website into multiple languages
June 11, 2025
In “Localization”
Navigating localization success with Localizely

Step into the world of easy localization

Try Localizely
Localizely logo
About
Free tools
Boring stuff
  • Privacy Policy
  • Terms of Service
  • Cookie Policy
Newsletter
Copyrights 2026 © Localizely