The Flutter Intl extension made one thing easy: put the cursor on a string, extract it to an ARB file, done. It was last updated in 2023, and the generator behind it, intl_utils, is now marked discontinued on pub.dev. Projects moving to gen-l10n lose that workflow.
We built a replacement for gen-l10n: Flutter Localization (ARB, gen-l10n). It's free and open source, and it works in VS Code, Cursor, Windsurf and other VS Code-based editors.
Install
Search for "Flutter Localization" in the Extensions view, or:
code --install-extension flutterlocalisation.flutter-l10n
It's on the Visual Studio Marketplace and on Open VSX, which Cursor and Windsurf use. The source is on GitHub.
What it does
Reports the text you forgot to translate. Strings the user reads show up in the Problems panel: the text of Text, SelectableText and TextSpan, and arguments like labelText, hintText, errorText, tooltip and title. Map keys, routes, asset paths, logs and identifiers are not reported. // l10n-ignore skips a line on purpose, for a brand name for example.
Extracts a string in one step. Use the quick fix, the context menu, or Cmd+Alt+E (Ctrl+Alt+E on Windows and Linux). Accept the suggested key and
const Text('Hello $name')
becomes
Text(AppLocalizations.of(context)!.helloName(name))
The extension also adds "helloName": "Hello {name}" to your template ARB, with the placeholder declared. Three details make the result compile without a second pass:
- Interpolations become placeholders, and the expressions are passed back in the call.
${user.name}becomes{userName}. - A
constthat would no longer compile is removed, including one on an enclosing list such aschildren: const [...]. - The import of your localizations file is added once.
If the same text already has a key, the extension reuses it instead of creating a duplicate.
Regenerates on save. Saving an ARB file runs flutter gen-l10n, so the new getter exists before you switch back to the Dart file.
Shows translations on hover. Hover over .helloName to see its text in every locale, with missing ones marked.
Flags missing translations. Each locale's ARB file lists the keys the template has and it doesn't.
Previews a widget in every language. On a widget class, Preview in all locales adds a @LocalePreview() function, and the Flutter Widget Previewer shows the widget in each locale, right to left, and in a pseudo-locale that makes every string 40% longer. See Flutter widget previews in every language.
Your setup, not ours
The extension reads your configuration instead of assuming one:
| Project | Code it writes |
|---|---|
| gen-l10n | AppLocalizations.of(context)!.key, with the class name, output folder and nullable-getter read from l10n.yaml |
The flutter_localisation package |
context.tr.key |
| intl_utils / Flutter Intl | S.of(context).key |
If you wrap the call in your own extension, such as context.l10n, set flutterL10n.accessor to context.l10n.{key}.
An intl_utils project keeps working while you migrate. The extension points you to the migration guide to gen-l10n, which keeps your S class and your ARB files.
Tested before release
Each release runs in a real VS Code against a sample app: it checks the diagnostics, the quick fix, extracting with placeholders, reusing a key, the hover and the missing-translation warning. We also extracted every string of a fresh Flutter app, including ones inside a const list and a nested const widget, then ran flutter gen-l10n and flutter analyze. Both came back clean.
Translating the keys
The extension never sends your text anywhere. When you want the new keys translated, the Translate and Sync command runs our free fl command line tool. It AI-translates new keys into every locale of your FlutterLocalisation project and pulls the ARB files back. Use any workflow you like; the extension doesn't depend on it.