← Back to Blog

Flutter l10n.yaml: The Complete Configuration Guide (2026)

flutterlocalizationconfigurationl10nyamlgen-l10n2026

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 tree
  • nullable-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:

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.