Flutter l10n.yaml: The Complete Configuration Guide (2026)
The l10n.yaml file is the single source of truth for how Flutter generates your localization code. Get it wrong and you'll spend hours debugging missing translations, null errors, and broken builds. Get it right and localization becomes effortless.
This is the most comprehensive l10n.yaml reference available — every option explained with real examples, copy-paste production configs, and solutions for every common error. Whether you're setting up localization for the first time or optimizing an existing project, this guide has you covered.
Why l10n.yaml Matters
Without l10n.yaml, you'd need to pass flags to flutter gen-l10n every time. The YAML file makes your configuration:
- Reproducible — same output on every machine
- Version controlled — changes tracked in git
- CI/CD friendly — no manual flags needed
- Team consistent — everyone uses the same settings
Basic l10n.yaml Setup
Here's the minimal configuration to get started:
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
Place this file in your project root (same directory as pubspec.yaml). Flutter automatically detects it during flutter gen-l10n and flutter build.
This generates code in .dart_tool/flutter_gen/gen_l10n/. But there's much more you can configure.
Complete Configuration Reference
# === Required Settings ===
# Directory containing .arb files
arb-dir: lib/l10n
# The .arb file used as the template (source of truth)
template-arb-file: app_en.arb
# === Output Settings ===
# Name of the generated Dart file
output-localization-file: app_localizations.dart
# Name of the generated class
output-class: AppLocalizations
# Directory for generated files (if not using synthetic-package)
output-dir: lib/generated/l10n
# Generate as real package vs synthetic
synthetic-package: false
# === Behavior Settings ===
# Make all translations required (fail build on missing)
required-resource-attributes: false
# Make translations nullable when missing
nullable-getter: true
# Generate additional helper methods
use-deferred-loading: false
# === Advanced Settings ===
# Header added to generated files
header: |
/// Generated file. Do not edit.
/// Generated by flutter gen-l10n
# Add imports to generated file
preferred-supported-locales:
- en
- es
- de
# Untranslated messages file
untranslated-messages-file: lib/l10n/untranslated.txt
Configuration Options Deep Dive
arb-dir
Purpose: Specifies where your .arb files live.
# Standard location
arb-dir: lib/l10n
# Alternative: assets folder
arb-dir: assets/translations
# Nested structure
arb-dir: lib/src/localization/arb
Best practice: Use lib/l10n for consistency with Flutter conventions.
template-arb-file
Purpose: The source .arb file that defines all keys. Other locales are validated against this.
template-arb-file: app_en.arb
How it works:
- All keys must exist in this file
- Metadata (
@key) is read from here - Placeholders and plural rules defined here apply to all locales
Common patterns:
# English as template (most common)
template-arb-file: app_en.arb
# Using a "key" file (keys as values)
template-arb-file: app_keys.arb
# Regional variant
template-arb-file: app_en_US.arb
output-localization-file
Purpose: Name of the generated Dart file containing your localizations.
output-localization-file: app_localizations.dart
Custom naming:
# For multiple localization files in large apps
output-localization-file: my_app_strings.dart
# Match your app's naming convention
output-localization-file: localized_strings.dart
output-class
Purpose: The class name used to access translations in code.
output-class: AppLocalizations
Usage in code:
// With default name
Text(AppLocalizations.of(context)!.welcomeMessage)
// With custom name
output-class: S
// Usage becomes:
Text(S.of(context)!.welcomeMessage)
// Or more descriptive
output-class: Strings
Text(Strings.of(context)!.welcomeMessage)
Popular conventions:
| output-class | Usage | Why |
|---|---|---|
| AppLocalizations | Verbose but clear | Flutter default |
| S | Short, easy to type | Popular in Android |
| L10n | Balanced | Clear + short |
| Strings | Descriptive | Self-documenting |
synthetic-package
Purpose: Controls whether generated files live in .dart_tool or your project.
# Generated in .dart_tool (default)
synthetic-package: true
# Generated in your project (recommended)
synthetic-package: false
Why use false:
- Files visible in IDE
- Can commit to version control
- Easier debugging
- No surprises in CI/CD
Required with false:
synthetic-package: false
output-dir: lib/generated/l10n # Must specify output location
output-dir
Purpose: Where to generate files when synthetic-package: false.
synthetic-package: false
output-dir: lib/generated/l10n
Directory structure created:
lib/
├── generated/
│ └── l10n/
│ ├── app_localizations.dart
│ ├── app_localizations_en.dart
│ ├── app_localizations_es.dart
│ └── app_localizations_de.dart
└── l10n/
├── app_en.arb
├── app_es.arb
└── app_de.arb
nullable-getter
Purpose: Controls null safety behavior when translations are missing.
# Default: Returns null if translation missing for locale
nullable-getter: true
# Strict: Throws if translation missing
nullable-getter: false
With nullable-getter: true:
// May return null, requires null check
final text = AppLocalizations.of(context)?.welcomeMessage ?? 'Default';
With nullable-getter: false:
// Never null, but throws if locale not supported
final text = AppLocalizations.of(context)!.welcomeMessage;
Recommendation: Use true during development, consider false for production with complete translations.
required-resource-attributes
Purpose: Enforces that every key has metadata.
# Optional metadata (default)
required-resource-attributes: false
# Required metadata
required-resource-attributes: true
With true, every key needs this:
{
"welcomeMessage": "Welcome!",
"@welcomeMessage": {
"description": "Required description"
}
}
Benefits of requiring:
- Better translator context
- Self-documenting code
- Catches keys without descriptions
Build fails without metadata when enabled.
use-deferred-loading
Purpose: Enables lazy loading of locale data.
use-deferred-loading: true
What it does:
- Locale files loaded on demand
- Reduces initial bundle size
- Useful for apps with many languages
Generated code difference:
// Without deferred loading
import 'app_localizations_es.dart';
// With deferred loading
import 'app_localizations_es.dart' deferred as es;
Future<void> loadLocale(Locale locale) async {
if (locale.languageCode == 'es') {
await es.loadLibrary();
}
}
Use when:
- 10+ languages supported
- Large translation files
- Web app with bundle size concerns
preferred-supported-locales
Purpose: Orders locales for fallback behavior.
preferred-supported-locales:
- en
- es
- de
How it affects fallback:
User locale: es_MX (Mexican Spanish)
Lookup order:
1. es_MX (exact match) - not found
2. es (first in preferred list) - found!
Without this setting: Order is undefined (usually alphabetical).
untranslated-messages-file
Purpose: Outputs a file listing missing translations.
untranslated-messages-file: lib/l10n/untranslated.txt
Generated content:
The following messages are not translated in app_de.arb:
- newFeatureTitle
- newFeatureDescription
- settingsPrivacyPolicy
The following messages are not translated in app_ja.arb:
- newFeatureTitle
- newFeatureDescription
- settingsPrivacyPolicy
- settingsTermsOfService
Use cases:
- CI/CD translation coverage checks
- Tracking translation progress
- Handoff lists for translators
header
Purpose: Adds a comment header to generated files.
header: |
// GENERATED CODE - DO NOT MODIFY BY HAND
// Generated by: flutter gen-l10n
// Source: lib/l10n/*.arb
//
// To regenerate, run: flutter gen-l10n
Best practice headers:
# Simple
header: "// Generated file. Do not edit manually."
# With instructions
header: |
// This file is generated from .arb files in lib/l10n/
// To add or modify translations, edit the .arb files
// Then run: flutter gen-l10n
# With timestamp (useful but regenerates file constantly)
header: "// Generated on: ${DateTime.now()}" # Note: This won't work - just example
Complete Production Configuration
Here's a recommended configuration for production apps:
# l10n.yaml - Production Configuration
# Source files
arb-dir: lib/l10n
template-arb-file: app_en.arb
# Output configuration
output-localization-file: app_localizations.dart
output-class: L10n
synthetic-package: false
output-dir: lib/generated/l10n
# Locale ordering (affects fallback)
preferred-supported-locales:
- en
- es
- de
- fr
- ja
- zh
# Strict mode for production quality
required-resource-attributes: true
nullable-getter: false
# Track missing translations
untranslated-messages-file: lib/l10n/untranslated.txt
# Generated file header
header: |
// GENERATED CODE - DO NOT MODIFY
// Run "flutter gen-l10n" to regenerate
Configuration for Different Project Types
Small App (5 languages or fewer)
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
Medium App (5-15 languages)
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: S
synthetic-package: false
output-dir: lib/generated/l10n
required-resource-attributes: true
untranslated-messages-file: lib/l10n/untranslated.txt
Large App (15+ languages)
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: L10n
synthetic-package: false
output-dir: lib/generated/l10n
use-deferred-loading: true
required-resource-attributes: true
nullable-getter: false
preferred-supported-locales:
- en
- es
- zh
- hi
- ar
# ... more languages
untranslated-messages-file: lib/l10n/untranslated.txt
header: |
// Generated localization file
// Do not edit manually
Troubleshooting Common Issues
"Missing required resource attribute"
Error: Missing required resource attribute for "welcomeMessage"
Solution: Add metadata to your .arb file:
{
"welcomeMessage": "Welcome!",
"@welcomeMessage": {
"description": "Main greeting text"
}
}
"arb-dir does not exist"
Error: The specified arb-dir (lib/l10n) does not exist
Solution: Create the directory:
mkdir -p lib/l10n
"output-dir required when synthetic-package is false"
Error: output-dir must be specified when synthetic-package is false
Solution: Add output-dir:
synthetic-package: false
output-dir: lib/generated/l10n
Generated files not updating
# Clear the cache and regenerate
flutter clean
flutter pub get
flutter gen-l10n
Integration with Build Runner
You can integrate gen-l10n with build_runner:
# build.yaml
targets:
$default:
builders:
flutter_gen_l10n:
enabled: true
Then run with:
flutter pub run build_runner build
CI/CD Configuration
GitHub Actions Example
- name: Generate localizations
run: flutter gen-l10n
- name: Check for untranslated strings
run: |
if [ -s lib/l10n/untranslated.txt ]; then
echo "Missing translations found:"
cat lib/l10n/untranslated.txt
exit 1
fi
Common Mistakes to Avoid
Mistake 1: Forgetting generate: true in pubspec.yaml
# pubspec.yaml — this is REQUIRED
flutter:
generate: true
Without this, Flutter ignores your l10n.yaml completely. No error, no warning — just no generated files.
Mistake 2: Wrong arb-dir Path
# Wrong — relative to lib, not project root
arb-dir: l10n
# Correct — relative to project root
arb-dir: lib/l10n
Mistake 3: Mismatched Template and Actual Files
# l10n.yaml says:
template-arb-file: app_en.arb
# But your actual file is named:
# lib/l10n/en.arb ← MISMATCH!
The template file name must exactly match the filename in your arb-dir.
Mistake 4: Using output-dir Without Disabling synthetic-package
# This will fail:
output-dir: lib/generated/l10n
# You need BOTH:
synthetic-package: false
output-dir: lib/generated/l10n
Mistake 5: Not Running gen-l10n After Changes
After editing l10n.yaml, always regenerate:
flutter gen-l10n
Or restart your IDE — hot reload doesn't pick up configuration changes.
Migration Guide: Updating Your l10n.yaml for 2026
If you're upgrading from an older Flutter project, here's what to update:
From Flutter 3.x to Latest
# Old approach (still works but not recommended)
arb-dir: lib/l10n
template-arb-file: app_en.arb
synthetic-package: true
# Modern approach (recommended for 2026)
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations
synthetic-package: false
output-dir: lib/generated/l10n
nullable-getter: false
required-resource-attributes: true
untranslated-messages-file: lib/l10n/untranslated.txt
Key changes:
synthetic-package: false— Generated files visible in your project treenullable-getter: false— Stricter null safety (catches bugs at compile time)required-resource-attributes: true— Forces metadata for every key (better for teams)
Quick Reference Card
| Option | Type | Default | Purpose |
|---|---|---|---|
| arb-dir | path | required | Location of .arb files |
| template-arb-file | string | required | Source .arb file |
| output-localization-file | string | auto | Generated file name |
| output-class | string | AppLocalizations | Generated class name |
| output-dir | path | - | Output location |
| synthetic-package | bool | true | Use .dart_tool |
| nullable-getter | bool | true | Allow null returns |
| required-resource-attributes | bool | false | Require metadata |
| use-deferred-loading | bool | false | Lazy load locales |
| preferred-supported-locales | list | - | Fallback order |
| untranslated-messages-file | path | - | Missing translations output |
| header | string | - | File header comment |
Frequently Asked Questions
Can I have multiple l10n.yaml files for different modules?
No. Flutter only reads one l10n.yaml from the project root. For multi-module apps, each module needs its own l10n.yaml and its own flutter gen-l10n run.
Does l10n.yaml work with Flutter Web?
Yes. All options work identically across iOS, Android, Web, macOS, Windows, and Linux. The use-deferred-loading option is especially useful for web to reduce initial bundle size.
How do I use l10n.yaml with flavors/environments?
You can't have different l10n.yaml per flavor directly. Instead, use different arb-dir paths and swap them in your build scripts, or use a single set of translations with conditional keys.
What's the difference between flutter gen-l10n and build_runner?
flutter gen-l10n is the built-in command that reads l10n.yaml. build_runner is a separate code generation system. You can integrate gen-l10n with build_runner, but for most projects the built-in command is simpler and faster.
Related Resources:
- Complete Guide to Flutter .arb Files
- Flutter intl Package Guide
- Flutter Localization Packages Comparison
- Flutter Internationalization Documentation
Master your l10n.yaml configuration and your localization workflow becomes predictable and maintainable. Need help managing the .arb files that feed into this configuration? FlutterLocalisation streamlines the entire process — visual ARB editing, AI-powered translations, and team collaboration built in. Try it free.