skip to content

In Flutter 3.47, what does it take to set up gen-l10n so .arb files become an AppLocalizations class wired into MaterialApp?

level: middleimportance: must knowfreq 52%

answer

  1. two dependencies, one sdk
  2. flutter: generate: true
  3. l10n.yaml at the project root
  4. output lands next to the ARB files
  5. localizationsDelegates plus supportedLocales

basics

~10 s

Add flutter_localizations (from the SDK) and intl, set flutter: generate: true in pubspec.yaml, add l10n.yaml and the .arb files, then pass AppLocalizations.localizationsDelegates and AppLocalizations.supportedLocales to MaterialApp. The generated Dart lands in your source tree.

solid answer

~40 s

Four pieces. First, depend on `flutter_localizations` with `sdk: flutter` and on `intl`. Second, set `generate: true` under the `flutter:` section of `pubspec.yaml`; without it `flutter gen-l10n` exits with an error. Third, add `l10n.yaml` at the project root, typically `arb-dir: lib/l10n` and `template-arb-file: app_en.arb`, and put `app_en.arb`, `app_de.arb` and `app_pl.arb` there. `flutter pub get` and `flutter run` then regenerate the code, and since Flutter 3.32 it is written into the source tree (the `arb-dir` unless `output-dir` says otherwise), not into a synthetic `package:flutter_gen`. Fourth, import the generated `app_localizations.dart` and give `MaterialApp` `localizationsDelegates: AppLocalizations.localizationsDelegates` and `supportedLocales: AppLocalizations.supportedLocales`, which also brings in the Material, Cupertino and Widgets delegates.

code

yaml · 15 lines
yaml
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: any

flutter:
  generate: true

# l10n.yaml (project root)
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

go deeper

for a junior

Recall the four pieces: flutter_localizations plus intl, generate: true in pubspec, l10n.yaml with ARB files in lib/l10n, and the two AppLocalizations lists passed to MaterialApp.

for a middle

Explain what each l10n.yaml key controls, that l10n.yaml drives automatic regeneration on pub get and run, and that since 3.32 output is plain source instead of package:flutter_gen.

for a senior

Decide whether generated files are committed, keep CI regenerating and reporting untranslated messages, and migrate old flutter_gen imports without breaking the build.

for a principal

Set the team convention for where catalogs live, who owns the template file, and how translation vendors hand ARB files back without touching generated code.

## The moving parts Flutter's built-in localization pipeline has three layers, and setting it up means touching each one: - **`flutter_localizations`**, a package shipped inside the Flutter SDK, which carries translated strings for Material and Cupertino widgets (date pickers, back-button tooltips, text-selection menus) in many languages, plus the text direction for each language. - **`intl`**, the Dart package whose `Intl.pluralLogic`, `Intl.selectLogic`, `NumberFormat` and `DateFormat` the generated code calls. - **gen-l10n**, the code generator inside the `flutter` tool, which reads your **App Resource Bundle** (`.arb`) files and writes the `AppLocalizations` class. ## Step by step 1. **Dependencies.** `flutter pub add flutter_localizations:"{sdk: flutter}" intl:any`, or edit `pubspec.yaml` by hand so it lists `flutter_localizations: {sdk: flutter}` and `intl`. 2. **The generate flag.** Under the `flutter:` section, set `generate: true`. The tool checks this before generating anything; without it, `flutter gen-l10n` stops with *"Attempted to generate localizations code without having the flutter: generate flag turned on."* 3. **`l10n.yaml`.** Create it next to `pubspec.yaml`. Its presence is also what makes `flutter pub get`, `flutter run` and hot reload regenerate the code automatically: the build step that runs gen-l10n is skipped when the file is missing. 4. **ARB files.** Put the template (`app_en.arb`) and one file per language (`app_de.arb`, `app_pl.arb`) in the `arb-dir`. The locale comes from the file-name suffix or from an `@@locale` key. 5. **Wire the app.** Import the generated file and pass its lists to `MaterialApp` (or `CupertinoApp`, or `WidgetsApp`). ## The `l10n.yaml` keys and their defaults | Key | Default | Meaning | |---|---|---| | `arb-dir` | `lib/l10n` | Where the template and translated `.arb` files live | | `template-arb-file` | `app_en.arb` | The file whose messages and metadata define the API | | `output-localization-file` | `app_localizations.dart` | Name of the main generated file | | `output-class` | `AppLocalizations` | Name of the generated class | | `output-dir` | same as `arb-dir` | Where the generated Dart is written | | `nullable-getter` | `true` | Whether `of(context)` returns a nullable type | | `untranslated-messages-file` | none | JSON report of messages missing per locale | When `l10n.yaml` exists, `flutter gen-l10n` ignores its command-line options and says so; delete the file if you want to drive the tool purely from flags. ## Where the output goes (and what changed in 3.32) Before Flutter 3.32 the tool generated a **synthetic package**, imported as `package:flutter_gen/gen_l10n/app_localizations.dart`, by rewriting the app's `package_config.json`. Flutter 3.32 made generation into source the default and required `generate: true`; the synthetic package has since been removed. In Flutter 3.47: - the generated files are ordinary Dart files in `arb-dir` (or `output-dir`), imported like any other file, for example `import 'l10n/app_localizations.dart';`; - `synthetic-package: true` in `l10n.yaml` is a tool error, and `synthetic-package: false` only prints a warning that the key no longer does anything. Because the files are real source, teams either commit them (reviewers see the generated API change) or ignore them and rely on `flutter pub get` regenerating them in CI. Pick one and apply it consistently. ## Wiring `MaterialApp` The generated class carries two ready-made lists: - **`AppLocalizations.localizationsDelegates`**: your `AppLocalizations.delegate` followed by `GlobalMaterialLocalizations.delegate`, `GlobalCupertinoLocalizations.delegate` and `GlobalWidgetsLocalizations.delegate`. - **`AppLocalizations.supportedLocales`**: one `Locale` per ARB file, in alphabetical order unless `preferred-supported-locales` reorders it. Passing both keeps the app's own strings and the framework's strings in step: every language you ship an ARB file for also gets translated Material widgets and the right text direction. If you need extra delegates, spread the list and append: `[...AppLocalizations.localizationsDelegates, MyExtraDelegate()]`. ## Day-to-day regeneration Once `generate: true` and `l10n.yaml` are in place, you rarely run the generator by hand: - `flutter pub get` regenerates the classes after dependency resolution. - `flutter run` regenerates them before the first build, and hot reload and hot restart run the same source generators again, so an edited ARB string appears without restarting the tool. - `flutter gen-l10n` remains useful in CI and when you want the console summary of untranslated messages without launching an app. A syntax error in an ARB file (an unbalanced brace, a plural without `other`) stops generation with the file name, message id and position, so the error appears before any Dart compiles against a stale class. ## Checking it works - Run `flutter gen-l10n` once; it reports untranslated messages per locale on the console. - Switch the device language to German and Polish and confirm both your strings and Material widgets such as a date picker change language. - On iOS, also list the supported languages in the Xcode project's localizations so the store listing shows them; Flutter's own localization does not do that for you.

  • You pass --arb-dir=assets/l10n to flutter gen-l10n but it still reads lib/l10n; why?
    When `l10n.yaml` exists, `flutter gen-l10n` takes every option from that file and ignores the command-line flags, printing a notice that it did so. Either change `arb-dir` in `l10n.yaml` or delete the file to drive the tool from flags; note that automatic regeneration on `pub get` and `run` needs `l10n.yaml`.
  • How do you migrate an older app that imports package:flutter_gen/gen_l10n/app_localizations.dart?
    Make sure `flutter: generate: true` is set, remove `synthetic-package` from `l10n.yaml`, regenerate, and change the import to the generated file inside your source tree, such as `import 'l10n/app_localizations.dart';`. Set `output-dir` if you want the Dart files somewhere other than the ARB folder.
  • Why pass AppLocalizations.localizationsDelegates instead of just AppLocalizations.delegate?
    The generated list also includes the Global Material, Cupertino and Widgets delegates from `flutter_localizations`. Without them, a Polish or German locale has no `MaterialLocalizations`, so Flutter logs that the locale is not supported by all delegates and Material widgets that need localized labels fail.

saying these in an interview costs you the question

  • gen-l10n needs build_runner and a watch command to regenerate strings.
  • Import the generated class from package:flutter_gen in Flutter 3.47.
  • flutter: generate: true is optional when l10n.yaml exists.
  • AppLocalizations.delegate alone also translates Material widgets like date pickers.
  • Command-line flags override the values in l10n.yaml.