Flutter's built-in localization system uses Application Resource Bundle (ARB) files and the intl package to deliver fully native experiences in any language. This guide covers everything from project setup and ARB format details to language switching, pluralization, and automating translations with l10n.dev.
Flutter localization is the official approach to adapting your app for multiple languages and regions. It relies on ARB files to store translated strings and the flutter_gen code generator to produce type-safe Dart accessors. At runtime, Flutter selects the correct ARB file based on the device locale - no restart required when switching locales programmatically.
Flutter's localization pipeline is driven by two configuration files: pubspec.yaml and l10n.yaml. Enable code generation in pubspec.yaml and point Flutter to your ARB directory.
Add flutter_localizations and intl as dependencies, then enable the generate flag so Flutter auto-generates the Dart localization classes from your ARB files.
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: any
flutter:
generate: truePlace an l10n.yaml file at the root of your project to configure the ARB directory, the template file, and the generated output file name.
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dartARB (Application Resource Bundle) is a JSON-based format designed specifically for Flutter internationalization. Each key maps to a translatable string, and optional metadata keys (prefixed with @) carry descriptions, placeholder definitions, and context for translators.
{
"@@locale": "en",
"@@last_modified": "2026-01-15T10:30:00Z",
"appTitle": "My App",
"@appTitle": {
"description": "The title of the application"
},
"welcome": "Welcome, {name}!",
"@welcome": {
"description": "Welcome message shown on the home screen",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
},
"unreadMessages": "{count, plural, =0{No unread messages} =1{1 unread message} other{{count} unread messages}}",
"@unreadMessages": {
"description": "Number of unread messages",
"placeholders": {
"count": {
"type": "int"
}
}
}
}After translation, UI strings are localized while metadata structure remains intact. l10n.dev updates @@locale and @@last_modified automatically.
{
"@@locale": "fr",
"@@last_modified": "2026-01-15T10:30:01Z",
"appTitle": "Mon Application",
"@appTitle": {
"description": "The title of the application"
},
"welcome": "Bienvenue, {name} !",
"@welcome": {
"description": "Welcome message shown on the home screen",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
},
"unreadMessages": "{count, plural, =0{Aucun message non lu} =1{1 message non lu} other{{count} messages non lus}}",
"@unreadMessages": {
"description": "Number of unread messages",
"placeholders": {
"count": {
"type": "int"
}
}
}
}ARB files follow a straightforward naming pattern: a prefix (by default app_) followed by the locale code and the .arb extension. Flutter and l10n.dev use underscores to separate language and region codes.
# Default pattern (recommended)
app_en.arb
app_fr.arb
app_en_US.arb # Locale with region (underscore format)
app_zh_CN.arb # Chinese Simplified
# Custom prefix patterns also supported
my_app_en.arb
my_app_fr.arb
# Note: ARB files use underscores, not hyphens
# โ app_en_US.arb
# โ app_en-US.arbOrganize all your ARB files inside a dedicated l10n folder within lib/. Flutter's code generator picks them up automatically based on the arb-dir setting in l10n.yaml, and outputs ready-to-use Dart classes.
lib/
โโโ l10n/
โ โโโ app_en.arb โ Source (English)
โ โโโ app_fr.arb โ French
โ โโโ app_de.arb โ German
โ โโโ app_ja.arb โ Japanese
โ โโโ app_zh_CN.arb โ Chinese Simplified
โ โโโ app_es.arb โ Spanish
โโโ main.dart
โโโ ...
# Generated output (do not edit manually)
.dart_tool/
โโโ flutter_gen/
โโโ gen_l10n/
โโโ app_localizations.dartWire up the generated Dart localization class with MaterialApp by providing the localizationsDelegates and supportedLocales. Flutter will then resolve the correct ARB data for the device locale.
Pass the four required delegates - your generated AppLocalizations.delegate plus the three Flutter SDK delegates - and list every locale your app supports. Use AppLocalizations.of(context)! anywhere in the widget tree to access translated strings.
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
// Required delegates
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
// Supported locales
supportedLocales: AppLocalizations.supportedLocales,
home: const HomePage(),
);
}
}
// Use in a widget
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(title: Text(l10n.appTitle)),
body: Center(child: Text(l10n.welcome('Alice'))),
);
}
}Flutter allows you to switch the app locale at runtime by updating the locale property on MaterialApp. A common pattern is to hold a Locale in a StatefulWidget or a state management solution and expose a callback to change it. The widget tree automatically rebuilds with the new locale.
// Manage locale state at the app level
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Locale _locale = const Locale('en');
void _changeLocale(Locale locale) {
setState(() => _locale = locale);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
locale: _locale,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
home: SettingsPage(onLocaleChange: _changeLocale),
);
}
}
// Language selector widget
class LanguageSelector extends StatelessWidget {
final void Function(Locale) onLocaleChange;
const LanguageSelector({super.key, required this.onLocaleChange});
@override
Widget build(BuildContext context) {
return DropdownButton<Locale>(
value: Localizations.localeOf(context),
items: AppLocalizations.supportedLocales
.map((locale) => DropdownMenuItem(
value: locale,
child: Text(locale.toLanguageTag()),
))
.toList(),
onChanged: (locale) {
if (locale != null) onLocaleChange(locale);
},
);
}
}Flutter uses ICU message syntax for pluralization inside ARB files. The intl package handles all CLDR plural categories, automatically applying the correct form based on the target language rules.
{
"cartItems": "{count, plural, =0{Your cart is empty} =1{1 item in your cart} other{{count} items in your cart}}",
"@cartItems": {
"description": "Cart item count",
"placeholders": {
"count": { "type": "int" }
}
},
"daysLeft": "{days, plural, =1{1 day left} other{{days} days left}}",
"@daysLeft": {
"description": "Days remaining",
"placeholders": {
"days": { "type": "int" }
}
}
}l10n.dev is purpose-built to work seamlessly with Flutter's ARB workflow. Upload your source ARB file and receive accurate, correctly-structured translations back:
Use the ai-l10n npm package to translate your source ARB file from the command line or as part of a CI/CD pipeline. Install once, then translate to any number of languages in a single command.
# Install the CLI
npm install ai-l10n
# Translate your source ARB to multiple languages
npx ai-l10n translate lib/l10n/app_en.arb \
--languages fr,de,ja,zh_CN,es,koYou can wire ai-l10n directly into your Flutter build process so translations are always up to date before the app is compiled. Two common approaches are a package.json script or a Makefile.
Add a translate script and use npm's prebuild lifecycle hook to run it automatically before every build. Run npm run build:android (or build:ios / build:web) and ai-l10n will translate first, then hand off to Flutter.
{
"scripts": {
"translate": "ai-l10n translate lib/l10n/app_en.arb --languages fr,de,ja,zh_CN,es,ko --update",
"prebuild": "npm run translate",
"build:android": "flutter build apk",
"build:ios": "flutter build ios",
"build:web": "flutter build web"
}
}If your team uses Make, declare a translate target and make each build target depend on it. Running make build-android (or build-ios / build-all) will translate all target languages before invoking flutter build.
LANGUAGES = fr,de,ja,zh_CN,es,ko
translate:
npx ai-l10n translate lib/l10n/app_en.arb --languages $(LANGUAGES) --update
build-android: translate
flutter build apk
build-ios: translate
flutter build ios
build-web: translate
flutter build web
build-all: translate
flutter build apk
flutter build ios
flutter build webFor CI/CD integration, incremental updates, batch translation across multiple files, and GitHub Actions workflow examples, see the Localization Automation Guide.
The l10n.dev VS Code extension brings Flutter ARB translation directly into your editor. Right-click any ARB file and translate it to your target languages without leaving VS Code.
Here's a live example of translating a Flutter ARB file to Uzbek inside VS Code:

Ready to reach global users? You can translate ARB files directly in the l10n.dev workspace, automate with the npm CLI, or translate right inside VS Code:
Thank you for using l10n.dev to localize your Flutter app! ๐
If this guide helped you, share it with other Flutter developers who need to reach a global audience.
Together, we can make Flutter apps more inclusive and ready for the world.