home / skills / madteacher / mad-agents-skills / flutter-internationalization
This skill helps Flutter developers implement localization with gen-l10n and intl, enabling multilingual UI, locale-aware formatting, and custom language
npx playbooks add skill madteacher/mad-agents-skills --skill flutter-internationalizationReview the files below or copy the command above to add this skill to your agents.
---
name: flutter-internationalization
description: Complete guide for internationalizing Flutter apps using gen-l10n and intl packages. Use when Claude needs to add localization support to Flutter applications, translate UI text, format numbers/dates for different locales, or configure multi-language support for Material/Cupertino apps.
---
# Flutter Internationalization
## Overview
Comprehensive guide for adding internationalization (i18n) to Flutter applications. Covers setup, configuration, message management, number/date formatting, and advanced topics like locale override and custom language support.
## Quick Start
Choose approach based on app needs:
**gen-l10n (Recommended)** - Modern, automated, code generation
- Best for: Most new projects, teams, complex apps
- Uses: ARB files, automated code generation
- See: [Setup gen-l10n](#setup-gen-l10n)
**intl package** - Manual control, code-based
- Best for: Simple apps, legacy projects, full control
- Uses: `Intl.message()` code, manual translation files
- See: [Setup intl package](#setup-intl-package)
**Manual/Custom** - Maximum flexibility
- Best for: Very simple apps, custom workflows
- Uses: Direct Map-based lookups
- See: [Custom localizations](#custom-localizations)
## Setup gen-l10n
### 1. Add Dependencies
Update `pubspec.yaml`:
```yaml
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: any
```
Run:
```bash
flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl:any
```
### 2. Enable Code Generation
Add to `pubspec.yaml`:
```yaml
flutter:
generate: true
```
### 3. Configure l10n.yaml
Create `l10n.yaml` in project root:
```yaml
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
```
For advanced options, see [l10n-config.md](references/l10n-config.md).
### 4. Create ARB Files
Create directory `lib/l10n/`.
**Template file** `lib/l10n/app_en.arb`:
```json
{
"helloWorld": "Hello World!",
"@helloWorld": {
"description": "Greeting message"
}
}
```
**Translation file** `lib/l10n/app_es.arb`:
```json
{
"helloWorld": "¡Hola Mundo!"
}
```
For complete ARB format, see [arb-format.md](references/arb-format.md).
### 5. Generate Code
Run:
```bash
flutter gen-l10n
```
Or run app to trigger auto-generation:
```bash
flutter run
```
### 6. Configure MaterialApp
Import and setup:
```dart
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'l10n/app_localizations.dart';
MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
Locale('en'),
Locale('es'),
],
home: MyHomePage(),
)
```
### 7. Use Localizations
Access in widgets:
```dart
Text(AppLocalizations.of(context)!.helloWorld)
```
## Message Types
### Simple Messages
No parameters:
```json
{
"welcome": "Welcome to our app",
"@welcome": {
"description": "Welcome message"
}
}
```
### Placeholder Messages
With parameters:
```json
{
"greeting": "Hello {userName}!",
"@greeting": {
"description": "Personalized greeting",
"placeholders": {
"userName": {
"type": "String",
"example": "Alice"
}
}
}
}
```
Use in code:
```dart
Text(AppLocalizations.of(context)!.greeting('Alice'))
```
### Plural Messages
Based on count:
```json
{
"itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
"@itemCount": {
"placeholders": {
"count": {
"type": "int"
}
}
}
}
```
Use in code:
```dart
Text(AppLocalizations.of(context)!.itemCount(5))
```
### Select Messages
Based on string value:
```json
{
"pronoun": "{gender, select, male{he} female{she} other{they}}",
"@pronoun": {
"placeholders": {
"gender": {
"type": "String"
}
}
}
}
```
Use in code:
```dart
Text(AppLocalizations.of(context)!.pronoun('male'))
```
## Number and Date Formatting
### Numbers
Format numbers automatically:
```json
{
"price": "Price: {value}",
"@price": {
"placeholders": {
"value": {
"type": "int",
"format": "simpleCurrency"
}
}
}
}
```
Format options: `compact`, `currency`, `simpleCurrency`, `decimalPattern`, etc.
### Dates
Format dates automatically:
```json
{
"eventDate": "Event on {date}",
"@eventDate": {
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMMMd"
}
}
}
}
```
Format options: `yMd`, `yMMMd`, `yMMMMd`, `Hm`, etc.
For complete formatting options, see [number-date-formats.md](references/number-date-formats.md).
## Advanced Topics
### Locale Override
Override locale for specific widgets:
```dart
Localizations.override(
context: context,
locale: const Locale('es'),
child: CalendarDatePicker(...),
)
```
### Custom Locale Definitions
For complex locales (Chinese, French regions):
```dart
supportedLocales: [
Locale.fromSubtags(languageCode: 'zh'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'),
]
```
### Locale Resolution Callback
Control locale fallback:
```dart
MaterialApp(
localeResolutionCallback: (locale, supportedLocales) {
// Always accept user's locale
return locale;
},
)
```
### Access Current Locale
Get current app locale:
```dart
Locale myLocale = Localizations.localeOf(context);
```
## Setup intl Package
### Manual Setup
1. Add dependencies (same as gen-l10n)
2. Create localization class:
```dart
class DemoLocalizations {
DemoLocalizations(this.localeName);
static Future<DemoLocalizations> load(Locale locale) {
final String name = Intl.canonicalizedLocale(locale.toString());
return initializeMessages(name).then((_) => DemoLocalizations(name));
}
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;
}
String get title {
return Intl.message(
'Hello World',
name: 'title',
desc: 'Title',
locale: localeName,
);
}
}
```
3. Create delegate:
```dart
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
@override
bool isSupported(Locale locale) => ['en', 'es'].contains(locale.languageCode);
@override
Future<DemoLocalizations> load(Locale locale) => DemoLocalizations.load(locale);
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
}
```
4. Generate ARB files:
```bash
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 lib/main.dart lib/l10n/intl_*.arb
```
## Custom Localizations
For maximum simplicity:
```dart
class DemoLocalizations {
DemoLocalizations(this.locale);
final Locale locale;
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;
}
static const _localizedValues = <String, Map<String, String>>{
'en': {'title': 'Hello World'},
'es': {'title': 'Hola Mundo'},
};
String get title {
return _localizedValues[locale.languageCode]!['title']!;
}
}
```
## Best Practices
1. **Use gen-l10n** for new projects - simpler, safer, better tooling
2. **Add descriptions** to ARB entries - provides context for translators
3. **Format numbers/dates** with format types - automatic locale handling
4. **Test all locales** - verify formatting, RTL, and translations
5. **Use pluralization** - handle count variations correctly
6. **Keep messages short** - easier to translate, more consistent
7. **Don't concatenate strings** - use placeholders instead
8. **Enable nullable-getter** to reduce null checks in user code
## Resources
### references/
**l10n-config.md** - Complete reference for `l10n.yaml` configuration options, including output directories, code generation settings, and locale handling.
**arb-format.md** - Comprehensive guide to ARB file format, covering simple messages, placeholders, plurals, selects, and metadata.
**number-date-formats.md** - Number and date formatting reference with format types, patterns, and locale-specific examples.
### assets/
Example templates and boilerplate code can be added here for common internationalization patterns.
## When to Use This Skill
Use this skill when:
- Adding localization support to a new Flutter app
- Translating existing Flutter app to multiple languages
- Configuring number/date formatting for different locales
- Setting up RTL (right-to-left) language support
- Implementing locale-specific layouts or widgets
- Managing ARB files and translations
- Troubleshooting localization issues
- Adding custom language support beyond built-in locales
- Optimizing app bundle size with deferred loading
This skill is a complete guide for internationalizing Flutter apps using gen-l10n and the intl package. It shows how to configure code generation, create ARB message files, and wire localization into Material and Cupertino apps. The guide also covers number and date formatting, plural/select messages, locale overrides, and manual/custom strategies.
The skill explains two main approaches: gen-l10n (recommended) for automated ARB-based code generation, and the intl package for manual control. It walks through adding dependencies, creating l10n.yaml and ARB files, running flutter gen-l10n, and integrating generated delegates into MaterialApp. It also describes message types (placeholders, plurals, selects), formatting options, and advanced locale resolution and overrides.
Which approach should I choose: gen-l10n or intl?
Use gen-l10n for most new projects because it automates ARB handling and generates type-safe APIs. Choose intl when you need manual control or are maintaining a legacy codebase.
How do I format currency and dates per locale?
Declare placeholder types and formats in your ARB entries (e.g., format: "simpleCurrency" or "yMMMd"). The generated code uses intl formatting tied to the current locale.
How can I test RTL languages?
Add RTL locales to supportedLocales, then run the app with those locales or wrap widgets with a Localizations.override to preview RTL behavior.