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.
What is Flutter Localization?
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.
Project Setup
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.
1. Update pubspec.yaml
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: true
2. Create l10n.yaml
Place 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.dart
The ARB File Format
ARB (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: Declares the locale for the file (e.g., "en", "fr", "zh_CN"). l10n.dev automatically updates this to the target language code.
- @@last_modified: Timestamp of the last modification. l10n.dev automatically sets this to the current UTC timestamp when generating translated files.
-
@keymetadata: Metadata entries such as description, example, and placeholders are NOT translated by default - they remain unchanged to preserve context for translators. Enable the translateMetadata setting if you want these translated. - Placeholders and ICU messages: Strings can contain named placeholders ({name}) and ICU message syntax for pluralization and selection - all of which are preserved correctly during translation.
Source ARB file (English)
{
"@@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"
}
}
}
}
Translated ARB file (French)
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"
}
}
}
}
File Naming Conventions
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.
ARB files use underscores, not hyphens - write app_en_US.arb and app_zh_CN.arb, not app_en-US.arb or app_zh-CN.arb.
# 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.arb
Project Structure
Organize 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.dart
Initialization
Wire 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.
MaterialApp Setup
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'))),
);
}
}
Language Switching at Runtime
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);
},
);
}
}
Pluralization
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.
- Use {count, plural, =0{...} =1{...} other{...}} syntax inside ARB string values.
- Declare the count placeholder with type int in the
@keymetadata. - l10n.dev generates all required plural forms for each target language - including complex languages such as Arabic, Russian, and Polish - without manual intervention.
{
"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" }
}
}
}
Full ARB Support in l10n.dev
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:
- Automatic Metadata Updates: @@locale is updated to the target language code and @@last_modified is set to the current UTC timestamp automatically.
-
Metadata Translation Control: By default,
@keymetadata entries (description, example, context) are NOT translated and remain unchanged, keeping translator notes intact. Enable the translateMetadata setting to translate them alongside UI strings. - Custom File Prefixes: Any naming pattern is supported - app_en.arb, my_app_en_US.arb, strings_fr.arb, and more.
- Underscore Locale Format: ARB locale codes use underscores (en_US, zh_CN) instead of hyphens - l10n.dev handles this correctly so generated files drop straight into your Flutter project.
- Pluralization Aware: ICU plural forms are generated for every target language according to CLDR rules, so no manual plural editing is needed after translation.
Automate Translations with npm
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,ko
Integration into a Flutter Build
You 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.
Via package.json scripts
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"
}
}
Via Makefile
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 web
For CI/CD integration, incremental updates, batch translation across multiple files, and GitHub Actions workflow examples, see the Localization Automation Guide.
VS Code Extension
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. Get the VS Code Extension for ARB files translation.
How It Works in VS Code:
- Open your source ARB file (e.g., app_en.arb) in the editor.
- Right-click in the editor and select "Translate JSON".
- Choose one or more target languages (e.g., French, Japanese, German).
- The extension creates localized ARB files alongside your source, with @@locale and @@last_modified updated automatically.
See It in Action
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 translation with the npm CLI, or translate right inside VS Code. Thanks for reading!
Top comments (0)