
Flutter Internationalization
- 497 installs
- 105 repo stars
- Updated May 1, 2026
- madteacher/mad-agents-skills
flutter-internationalization is a Claude Code skill at version 2.0 that adds, fixes, audits, and maintains Flutter i18n using gen-l10n, ARB files, and AppLocalizations for developers building multi-locale mobile apps.
About
flutter-internationalization is a madteacher/mad-agents-skills implementer skill (version 2.0) for Flutter localization with gen-l10n, ARB translation files, AppLocalizations, flutter_localizations, and intl number and date formatting. Developers reach for flutter-internationalization when adding l10n or i18n, translating Flutter UI strings, configuring l10n.yaml, managing ARB files, fixing plural and select messages, enabling RTL support, or troubleshooting generated localization build errors. The skill covers migration away from deprecated package:flutter_gen imports and locale-specific formatting rules. It ensures localized Flutter apps compile, generate correct AppLocalizations classes, and pass locale switching in production builds.
- Handles gen-l10n, ARB files, AppLocalizations, flutter_localizations, and intl formatting
- Supports plural/select messages, RTL, locale-specific number/date formatting
- Fixes localization build errors and migrates away from package:flutter_gen imports
- Decision guide for choosing between gen-l10n, legacy intl_translation, and custom map-based approaches
- Enforces Principle 0: generated code must match current Flutter project and SDK
Flutter Internationalization by the numbers
- 497 all-time installs (skills.sh)
- +4 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #626 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/madteacher/mad-agents-skills --skill flutter-internationalizationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 497 |
|---|---|
| repo stars | ★ 105 |
| Last updated | May 1, 2026 |
| Repository | madteacher/mad-agents-skills ↗ |
How do you set up Flutter gen-l10n with ARB files?
Add, fix, audit, and maintain Flutter internationalization using gen-l10n, ARB files, and AppLocalizations.
Who is it for?
Flutter developers adding or maintaining multi-language support with gen-l10n, ARB files, and AppLocalizations in production apps.
Skip if: Backend translation management platforms or web-only i18n with react-i18next rather than Flutter localization tooling.
When should I use this skill?
A developer asks to add Flutter l10n, translate ARB files, fix AppLocalizations errors, configure l10n.yaml, or enable RTL locales.
What you get
Configured l10n.yaml, ARB translation files, generated AppLocalizations classes, and working locale switching with RTL and plural support.
- l10n.yaml config
- ARB translation files
- Generated AppLocalizations code
By the numbers
- Skill version 2.0 in madteacher/mad-agents-skills metadata
Files
Flutter Internationalization
You are a Flutter localization implementer. Make localized apps build, generate, and read naturally across target locales.
Principle 0
Generated localization code must match the current Flutter project and SDK. Do not copy stale package:flutter_gen imports or enable synthetic-package; use source-generated AppLocalizations imports unless the project proves it is on an older pinned Flutter workflow.
Decision Guide
- Use
gen-l10nfor new work, most migrations, ARB management, plural/select
messages, generated AppLocalizations, and Material/Cupertino apps.
- Use legacy
intl_translationonly when the project already uses
Intl.message() plus generated messages_all.dart, or the user explicitly asks to keep that workflow. Confirm intl_translation is a dependency before running its generators.
- Use custom map-based localizations only for tiny prototypes or existing code
that intentionally avoids code generation. Name this limitation in the final response.
- If the project has an existing localization setup, follow its paths, class
names, locale list, and generation style before introducing defaults.
Workflow
1. Inspect pubspec.yaml, l10n.yaml, existing *.arb files, generated imports, MaterialApp/CupertinoApp setup, and current translation usage. 2. Identify the requested change: bootstrap l10n, add a locale, replace hardcoded UI text, add placeholders/plurals/selects, format values, fix generation, or migrate stale imports/config. 3. Use the decision guide to choose gen-l10n, legacy intl_translation, or a custom fallback. Prefer the smallest change that fits the existing project. 4. Read only the routed references needed for the task. 5. Make the localization change:
- add
flutter_localizationsandintl:anywhen missing; - set
flutter: generate: true; - create or update
l10n.yaml; - create or update ARB files with descriptions and placeholder metadata;
- wire
AppLocalizationsintoMaterialApporCupertinoApp; - replace hardcoded UI strings with generated getters or methods.
6. Generate and validate with flutter gen-l10n. Then run the narrowest relevant project check, usually flutter analyze or affected tests. 7. Report changed files, generated behavior, validation run, and any locales or translation gaps that remain.
Resource Routing
| Task | Read or use | Why |
|---|---|---|
Configure or debug l10n.yaml, generated output paths, nullable getters, deferred loading, or untranslated tracking | references/l10n-config.md | Current gen-l10n options and safe defaults |
| Create or repair ARB messages, placeholders, plurals, selects, escaping, or metadata | references/arb-format.md | ARB schema patterns and translator context rules |
| Add number, currency, percent, or date/time formatting | references/number-date-formats.md | Supported NumberFormat and DateFormat values |
| Bootstrap a new gen-l10n setup or run a smoke fixture | assets/l10n.yaml, assets/app_en.arb | Reusable minimal templates |
gen-l10n Contract
For new gen-l10n setup, the minimum current configuration is:
flutter:
generate: truearb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dartUse source imports that match the generated location, commonly:
import 'l10n/app_localizations.dart';Prefer generated lists when possible:
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,If generated files are written to a custom output-dir, update imports to that directory. Do not import package:flutter_gen/gen_l10n/app_localizations.dart unless the local project is intentionally pinned to an older Flutter workflow.
Legacy Intl Fallback
When preserving Intl.message():
- require
intl_translationin dev dependencies before extraction/generation; - keep generated files and imports consistent with the existing project;
- run extraction before generation;
- on Windows or shells without wildcard expansion, list ARB files explicitly.
Typical commands:
dart run intl_translation:extract_to_arb --output-dir=lib/l10n lib/main.dart
dart run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/main.dart lib/l10n/intl_*.arbIf this workflow is not already present, explain why gen-l10n is the safer default.
Constraints
- Do not invent translations. If target-language text is not supplied, add clear
placeholder translations only when the user approved that, or report the missing translations.
- Do not concatenate localized strings. Use placeholders, plural, or select
messages.
- Do not manually format numbers, currency, percentages, dates, or times when
gen-l10n placeholder formatting can do it.
- Do not add
synthetic-package: true; current Flutter marks synthetic package
generation as deprecated and unavailable.
- Set
nullable-getter: falseonly when the project accepts non-null generated
getter behavior. Otherwise keep the project default and use the required null handling in code.
- Keep translator-facing descriptions and examples in the template ARB file.
- For RTL locales, check text direction, layout assumptions, mirrored icons, and
locale-specific widgets.
Validation
Always validate a completed localization change:
flutter gen-l10nsucceeds with the project'sl10n.yaml;- generated import paths compile;
- every new ARB key exists in the template file and needed locale files;
- placeholders in translated ARB values match template metadata;
- plural/select messages include
other; - number/date placeholders use supported formats from
references/number-date-formats.md;
- relevant
flutter analyzeor tests pass, or blockers are reported.
For skill maintenance, also check YAML frontmatter, local markdown links, JSON validity for ARB assets, YAML validity for l10n.yaml, resource routing, and layer coherence between SKILL.md, references, and assets.
{
"appTitle": "My App",
"@appTitle": {
"description": "Application title shown in app bar"
},
"welcome": "Welcome to our app!",
"@welcome": {
"description": "Welcome message on home screen"
},
"hello": "Hello {userName}!",
"@hello": {
"description": "Personalized greeting message",
"placeholders": {
"userName": {
"type": "String",
"example": "John"
}
}
},
"itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
"@itemCount": {
"description": "Number of items in cart/list",
"placeholders": {
"count": {
"type": "int"
}
}
},
"price": "Price: {value}",
"@price": {
"description": "Price display with formatting",
"placeholders": {
"value": {
"type": "double",
"format": "simpleCurrency"
}
}
},
"eventDate": "Event on {date}",
"@eventDate": {
"description": "Event date display",
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMMMd"
}
}
},
"save": "Save",
"@save": {
"description": "Save button label"
},
"cancel": "Cancel",
"@cancel": {
"description": "Cancel button label"
},
"delete": "Delete",
"@delete": {
"description": "Delete button label"
},
"settings": "Settings",
"@settings": {
"description": "Settings menu item"
},
"about": "About",
"@about": {
"description": "About menu item"
},
"logout": "Logout",
"@logout": {
"description": "Logout button label"
},
"login": "Login",
"@login": {
"description": "Login button label"
},
"signup": "Sign Up",
"@signup": {
"description": "Sign up button label"
}
}
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
ARB File Format Reference
Complete reference for App Resource Bundle (.arb) files used by Flutter's gen-l10n tool.
ARB File Basics
ARB files are JSON files containing localization resources with metadata.
Basic Structure
{
"messageKey": "Message value",
"@messageKey": {
"description": "Message description for translators"
}
}Template File
The template file (typically app_en.arb) contains all keys with their English translations and metadata:
{
"helloWorld": "Hello World!",
"@helloWorld": {
"description": "The conventional newborn programmer greeting"
}
}Translation File
Translation files (e.g., app_es.arb) only contain translated values:
{
"helloWorld": "¡Hola Mundo!"
}Message Types
Simple Messages
Basic text without parameters:
{
"welcome": "Welcome to our app",
"@welcome": {
"description": "Welcome message shown on home screen",
"type": "text"
}
}Placeholder Messages
Messages with dynamic values:
{
"hello": "Hello {userName}",
"@hello": {
"description": "A message with a single parameter",
"placeholders": {
"userName": {
"type": "String",
"example": "Bob"
}
}
}
}Plural Messages
Messages that change based on count:
{
"nWombats": "{count, plural, =0{no wombats} =1{1 wombat} other{{count} wombats}}",
"@nWombats": {
"description": "A plural message",
"placeholders": {
"count": {
"type": "num",
"format": "compact"
}
}
}
}Plural forms: =0, =1, =2, zero, one, two, few, many, other
Select Messages
Messages that change based on a string value:
{
"pronoun": "{gender, select, male{he} female{she} other{they}}",
"@pronoun": {
"description": "A gendered message",
"placeholders": {
"gender": {
"type": "String"
}
}
}
}Select matching is case-sensitive. For example, Male does not match male and falls back to other, so normalize values before passing them to generated localization methods.
Placeholder Types
String
{
"greeting": "Hello {name}",
"@greeting": {
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
}
}Number Formatting
{
"price": "Price: {value}",
"@price": {
"placeholders": {
"value": {
"type": "int",
"format": "compactCurrency",
"optionalParameters": {
"decimalDigits": 2,
"symbol": "$"
}
}
}
}
}Date Formatting
{
"eventDate": "Event on {date}",
"@eventDate": {
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMd"
}
}
}
}Number Format Options
See number-date-formats.md for the complete list of number and date format options.
Date Format Options
Common DateFormat patterns:
| Pattern | Example (en_US) | Description |
|---|---|---|
y | 2024 | Year |
yM | 1/2024 | Year/Month |
yMd | 1/15/2024 | Year/Month/Day |
yMMMd | Jan 15, 2024 | Month name, Day, Year |
yMMMMd | January 15, 2024 | Full month name, Day, Year |
MMMMd | January 15 | Full month name, Day |
EEE, MMM d | Mon, Jan 15 | Day name, Month abbr, Day |
Metadata Attributes
Required Attributes for Complex Messages
For messages with placeholders, plurals, or selects:
{
"message": "Text with {placeholder}",
"@message": {
"description": "Message description",
"placeholders": {
"placeholder": {
"type": "String"
}
}
}
}Optional Attributes
{
"message": "Text",
"@message": {
"description": "Message description",
"type": "text",
"context": "Context for disambiguation"
}
}Escaping Syntax
Enable escaping in l10n.yaml:
use-escaping: trueThen use single quotes to escape special characters:
{
"escaped": "Hello! '{Isn''t}' this a wonderful day?"
}Results in: Hello! {Isn't} this a wonderful day?
Complete Example
{
"appTitle": "My App",
"@appTitle": {
"description": "Application title"
},
"greeting": "Hello {userName}!",
"@greeting": {
"description": "Personalized greeting message",
"placeholders": {
"userName": {
"type": "String",
"example": "John"
}
}
},
"itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
"@itemCount": {
"description": "Number of items in cart",
"placeholders": {
"count": {
"type": "int"
}
}
},
"pronoun": "{gender, select, male{he} female{she} other{they}}",
"@pronoun": {
"description": "Gender-based pronoun",
"placeholders": {
"gender": {
"type": "String"
}
}
},
"orderDate": "Ordered on {date}",
"@orderDate": {
"description": "Order confirmation with date",
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMMMd"
}
}
}
}Best Practices
1. Use descriptive keys: userProfileTitle instead of title1 2. Provide examples: Help translators understand context 3. Use type annotations: Improves generated code type safety 4. Add descriptions: Provide context for translators 5. Keep messages short: Make them easier to translate 6. Use placeholders: Don't concatenate strings 7. Format numbers/dates: Let Flutter handle localization
l10n.yaml Configuration
Reference for l10n.yaml options used by Flutter's gen-l10n tool. Treat flutter gen-l10n --help in the user's project as the final source of truth when an installed SDK differs from these notes.
Basic Configuration
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart| Option | Description | Default |
|---|---|---|
arb-dir | Directory containing template and translated ARB files | lib/l10n |
template-arb-file | Template ARB file used for generated Dart APIs | app_en.arb |
output-localization-file | Filename for generated localization and delegate classes | app_localizations.dart |
output-class | Dart class name for generated localizations | AppLocalizations |
Output Location
By default, current Flutter generates localization source into the ARB directory or the configured output-dir. Import the generated file from that source location:
import 'l10n/app_localizations.dart';Use a custom output directory only when the project wants generated files outside arb-dir:
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-dir: lib/generated/l10nThen import from that directory:
import 'generated/l10n/app_localizations.dart';Do not configure synthetic-package: true. Current Flutter marks the synthetic package flag as deprecated and it cannot be enabled. Migrate stale imports like package:flutter_gen/gen_l10n/app_localizations.dart to source imports that match the generated output location.
Locale Options
preferred-supported-locales:
- en_US
- es_ESpreferred-supported-locales changes the generated supported-locale order. Use it when the app should prefer a specific regional locale instead of alphabetical ordering.
Code Generation Options
| Option | Description | Default |
|---|---|---|
use-escaping | Enable single quote escaping syntax for literal braces and quotes | false |
nullable-getter | Whether AppLocalizations.of(context) returns nullable | true |
use-named-parameters | Generate named parameters for message methods | false |
format | Run dart format after generation | enabled by default in current Flutter |
Use nullable-getter: false only when the project accepts non-null generated getter behavior:
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
nullable-getter: falseWith the default nullable getter, user code usually needs:
AppLocalizations.of(context)!.titleWith nullable-getter: false, the generated getter performs the null check and call sites can omit !:
AppLocalizations.of(context).titleTracking And Warnings
| Option | Description | Example |
|---|---|---|
untranslated-messages-file | JSON file that lists messages missing from translations | l10n_untranslated.json |
gen-inputs-and-outputs-list | Directory for gen_l10n_inputs_and_outputs.json | . |
project-dir | Root Flutter project directory for generation | /path/to/project |
required-resource-attributes | Require metadata entries for all resource ids | true |
suppress-warnings | Suppress generator warnings | true |
relax-syntax | Treat unmatched braces as literal text in relaxed cases | true |
Track untranslated messages when adding or auditing locale coverage:
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
untranslated-messages-file: l10n_untranslated.jsonDeferred Loading
Deferred loading can reduce initial JavaScript bundle size for web apps with many locales and many messages. It can add overhead for small locale sets, and it does not affect mobile or desktop.
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
use-deferred-loading: trueUse the normal generated source import:
import 'l10n/app_localizations.dart';
Future<AppLocalizations> loadLocale(String localeCode) {
return AppLocalizations.delegate.load(Locale(localeCode));
}Header Configuration
header: "/// Generated localization files."For a longer header, place the file in arb-dir:
lib/l10n/header.txtThen configure:
header-file: header.txtValidation
After changing l10n.yaml:
1. Run flutter gen-l10n. 2. Confirm generated files appear in arb-dir or output-dir. 3. Confirm app imports match the generated location. 4. Run the narrowest relevant compile check, usually flutter analyze.
For placeholder number and date formats, see number-date-formats.md.
Number and Date Formatting
Complete reference for number and date formatting in Flutter internationalization.
Number Formatting
Basic Number Formats
Supported placeholder types: int, double, num
| Format | Example Input | Example Output (en_US) |
|---|---|---|
compact | 1200000 | 1.2M |
compactCurrency | 1200000 | $1.2M |
compactSimpleCurrency | 1200000 | $1.2M |
compactLong | 1200000 | 1.2 million |
currency | 1200000 | USD1,200,000.00 |
decimalPattern | 1200000 | 1,200,000 |
decimalPatternDigits | 1200000 | 1,200,000 |
decimalPercentPattern | 1.2 | 120% |
percentPattern | 1.2 | 120% |
scientificPattern | 1200000 | 1E6 |
simpleCurrency | 1200000 | $1,200,000 |
Currency Formats
Currency with Default Symbol
{
"price": "Price: {value}",
"@price": {
"placeholders": {
"value": {
"type": "int",
"format": "currency"
}
}
}
}Currency with Custom Symbol
{
"price": "Price: {value}",
"@price": {
"placeholders": {
"value": {
"type": "int",
"format": "simpleCurrency",
"optionalParameters": {
"symbol": "€"
}
}
}
}
}Compact Currency
{
"followers": "{count} followers",
"@followers": {
"placeholders": {
"count": {
"type": "int",
"format": "compactCurrency"
}
}
}
}Decimal Places Control
{
"price": "Price: {value}",
"@price": {
"placeholders": {
"value": {
"type": "double",
"format": "compactCurrency",
"optionalParameters": {
"decimalDigits": 2
}
}
}
}
}Percentage
{
"progress": "Progress: {value}",
"@progress": {
"placeholders": {
"value": {
"type": "double",
"format": "percentPattern"
}
}
}
}Date Formatting
DateFormat Patterns
Supported placeholder type: DateTime
| Pattern | Example (en_US) | Description |
|---|---|---|
y | 2024 | Year |
M | 1 | Month |
d | 15 | Day |
E | Mon | Day name (short) |
EEEE | Monday | Day name (long) |
LLL | Jan | Month name (short) |
LLLL | January | Month name (long) |
Common Date Formats
{
"eventDate": "Event on {date}",
"@eventDate": {
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMd"
}
}
}
}Full Date Formats
| Format | Example (en_US) | Example (ru_RU) |
|---|---|---|
yMd | 1/15/2024 | 15.01.2024 |
yMMMd | Jan 15, 2024 | 15 янв. 2024 |
yMMMMd | January 15, 2024 | 15 января 2024 |
yMMMMEEEEd | Monday, January 15, 2024 | понедельник, 15 января 2024 |
Hm | 14:30 | 14:30 |
Hms | 14:30:45 | 14:30:45 |
j | 2:30 PM | 14:30 |
jm | 2:30 PM | 14:30 |
Relative Time
For relative time (e.g., "2 minutes ago"), use intl package directly:
import 'package:intl/intl.dart';
final timeAgo = DateFormat('jm').format(DateTime.now().subtract(Duration(minutes: 2)));Platform-Specific Formatting
Flutter automatically formats numbers and dates according to locale. Examples:
English (en_US)
- Numbers: 1,234,567.89
- Currency: $1,234.57
- Date: January 15, 2024
Spanish (es_ES)
- Numbers: 1.234.567,89
- Currency: 1.234,57 €
- Date: 15 de enero de 2024
French (fr_FR)
- Numbers: 1 234 567,89
- Currency: 1 234,57 €
- Date: 15 janvier 2024
German (de_DE)
- Numbers: 1.234.567,89
- Currency: 1.234,57 €
- Date: 15. Januar 2024
Russian (ru_RU)
- Numbers: 1 234 567,89
- Currency: 1 234,57 ₽
- Date: 15 января 2024
Best Practices
1. Use locale-aware formatting: Always specify format for numbers/dates 2. Don't format manually: Let Flutter handle locale-specific formatting 3. Test across locales: Verify formatting in target locales 4. Use compact for large numbers: Makes UI more readable 5. Consider context: Use currency symbol appropriate for audience 6. Handle special cases: Some languages have unique formatting rules
Common Patterns
Price Display
{
"itemPrice": "Price: {price}",
"@itemPrice": {
"placeholders": {
"price": {
"type": "double",
"format": "simpleCurrency"
}
}
}
}Count Display
{
"itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
"@itemCount": {
"placeholders": {
"count": {
"type": "int",
"format": "compact"
}
}
}
}Date Range
{
"dateRange": "From {start} to {end}",
"@dateRange": {
"placeholders": {
"start": {
"type": "DateTime",
"format": "yMMMd"
},
"end": {
"type": "DateTime",
"format": "yMMMd"
}
}
}
}Related skills
How it compares
Pick this skill for hands-on Flutter gen-l10n implementation rather than generic i18n advice that ignores ARB and AppLocalizations specifics.
FAQ
What Flutter i18n tools does flutter-internationalization cover?
flutter-internationalization covers Flutter gen-l10n, ARB translation files, AppLocalizations, flutter_localizations, intl formatting, l10n.yaml configuration, and plural, select, and RTL message patterns.
What version is flutter-internationalization?
flutter-internationalization is version 2.0 in the madteacher/mad-agents-skills repository, authored by Stanislav Chernyshev (MADTeacher), with expanded build-error and migration guidance.