# Localization

Internationalization (i18n) patterns using flutter_localizations and intl package for Flutter applications.

## Setup

### Dependencies

```yaml
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: ^0.19.0

flutter:
  generate: true
```

### l10n Configuration

```yaml
# l10n.yaml
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations
nullable-getter: false
```

## ARB Files

### English (Template)

```json
// lib/l10n/app_en.arb
{
  "@@locale": "en",
  "appTitle": "My App",
  "@appTitle": {
    "description": "The application title"
  },
  "hello": "Hello",
  "welcome": "Welcome, {name}!",
  "@welcome": {
    "description": "Welcome message with user name",
    "placeholders": {
      "name": {
        "type": "String",
        "example": "John"
      }
    }
  },
  "itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
  "@itemCount": {
    "description": "Number of items",
    "placeholders": {
      "count": {
        "type": "int"
      }
    }
  },
  "lastUpdated": "Last updated: {date}",
  "@lastUpdated": {
    "description": "Last update timestamp",
    "placeholders": {
      "date": {
        "type": "DateTime",
        "format": "yMMMd"
      }
    }
  },
  "price": "Price: {amount}",
  "@price": {
    "description": "Product price",
    "placeholders": {
      "amount": {
        "type": "double",
        "format": "currency",
        "optionalParameters": {
          "symbol": "$",
          "decimalDigits": 2
        }
      }
    }
  },
  "gender": "{gender, select, male{He} female{She} other{They}} liked this",
  "@gender": {
    "description": "Gender-specific message",
    "placeholders": {
      "gender": {
        "type": "String"
      }
    }
  }
}
```

### Chinese

```json
// lib/l10n/app_zh.arb
{
  "@@locale": "zh",
  "appTitle": "我的应用",
  "hello": "你好",
  "welcome": "欢迎，{name}！",
  "itemCount": "{count, plural, =0{没有项目} other{{count} 个项目}}",
  "lastUpdated": "最后更新：{date}",
  "price": "价格：{amount}",
  "gender": "{gender, select, male{他} female{她} other{Ta}}喜欢了这个"
}
```

### Japanese

```json
// lib/l10n/app_ja.arb
{
  "@@locale": "ja",
  "appTitle": "マイアプリ",
  "hello": "こんにちは",
  "welcome": "ようこそ、{name}さん！",
  "itemCount": "{count, plural, =0{アイテムなし} other{{count}件}}",
  "lastUpdated": "最終更新：{date}",
  "price": "価格：{amount}",
  "gender": "{gender, select, male{彼} female{彼女} other{その人}}がいいねしました"
}
```

## App Configuration

```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',
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('en'),
        Locale('zh'),
        Locale('ja'),
      ],
      locale: const Locale('en'),
      home: const HomePage(),
    );
  }
}
```

## Using Translations

```dart
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

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: Column(
        children: [
          Text(l10n.hello),
          Text(l10n.welcome('John')),
          Text(l10n.itemCount(5)),
          Text(l10n.lastUpdated(DateTime.now())),
          Text(l10n.price(29.99)),
          Text(l10n.gender('female')),
        ],
      ),
    );
  }
}
```

### Extension for Convenience

```dart
extension LocalizationExtension on BuildContext {
  AppLocalizations get l10n => AppLocalizations.of(this);
}

// Usage
Text(context.l10n.hello)
```

## Dynamic Locale Switching

### With Riverpod

```dart
@riverpod
class LocaleNotifier extends _$LocaleNotifier {
  @override
  Locale build() {
    final saved = ref.watch(sharedPreferencesProvider).getString('locale');
    if (saved != null) {
      return Locale(saved);
    }
    return const Locale('en');
  }

  void setLocale(Locale locale) {
    ref.read(sharedPreferencesProvider).setString('locale', locale.languageCode);
    state = locale;
  }
}

class MyApp extends ConsumerWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final locale = ref.watch(localeNotifierProvider);

    return MaterialApp(
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      locale: locale,
      home: const HomePage(),
    );
  }
}
```

### Language Selector

```dart
class LanguageSelector extends ConsumerWidget {
  const LanguageSelector({super.key});

  static const languages = [
    (Locale('en'), 'English'),
    (Locale('zh'), '中文'),
    (Locale('ja'), '日本語'),
  ];

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final currentLocale = ref.watch(localeNotifierProvider);

    return PopupMenuButton<Locale>(
      initialValue: currentLocale,
      onSelected: (locale) {
        ref.read(localeNotifierProvider.notifier).setLocale(locale);
      },
      itemBuilder: (context) => languages.map((lang) {
        return PopupMenuItem(
          value: lang.$1,
          child: Row(
            children: [
              if (currentLocale == lang.$1)
                const Icon(Icons.check, size: 18),
              const SizedBox(width: 8),
              Text(lang.$2),
            ],
          ),
        );
      }).toList(),
      child: const Icon(Icons.language),
    );
  }
}
```

## Date and Number Formatting

```dart
import 'package:intl/intl.dart';

class FormattingUtils {
  static String formatDate(DateTime date, String locale) {
    return DateFormat.yMMMd(locale).format(date);
  }

  static String formatDateTime(DateTime dateTime, String locale) {
    return DateFormat.yMMMd(locale).add_jm().format(dateTime);
  }

  static String formatRelativeTime(DateTime dateTime, String locale) {
    final now = DateTime.now();
    final diff = now.difference(dateTime);

    if (diff.inDays > 7) {
      return DateFormat.yMMMd(locale).format(dateTime);
    } else if (diff.inDays > 0) {
      return '${diff.inDays}d ago';
    } else if (diff.inHours > 0) {
      return '${diff.inHours}h ago';
    } else if (diff.inMinutes > 0) {
      return '${diff.inMinutes}m ago';
    } else {
      return 'Just now';
    }
  }

  static String formatCurrency(double amount, String locale, {String? symbol}) {
    return NumberFormat.currency(
      locale: locale,
      symbol: symbol,
      decimalDigits: 2,
    ).format(amount);
  }

  static String formatNumber(num number, String locale) {
    return NumberFormat.decimalPattern(locale).format(number);
  }

  static String formatPercent(double value, String locale) {
    return NumberFormat.percentPattern(locale).format(value);
  }

  static String formatCompact(num number, String locale) {
    return NumberFormat.compact(locale: locale).format(number);
  }
}
```

### Usage with Locale

```dart
class FormattedContent extends StatelessWidget {
  const FormattedContent({super.key});

  @override
  Widget build(BuildContext context) {
    final locale = Localizations.localeOf(context).toString();

    return Column(
      children: [
        Text(FormattingUtils.formatDate(DateTime.now(), locale)),
        Text(FormattingUtils.formatCurrency(1234.56, locale, symbol: '\$')),
        Text(FormattingUtils.formatNumber(1234567, locale)),
        Text(FormattingUtils.formatPercent(0.75, locale)),
        Text(FormattingUtils.formatCompact(1500000, locale)),
      ],
    );
  }
}
```

## RTL Support

```dart
class RtlAwareWidget extends StatelessWidget {
  const RtlAwareWidget({super.key});

  @override
  Widget build(BuildContext context) {
    final isRtl = Directionality.of(context) == TextDirection.rtl;

    return Row(
      children: [
        Icon(isRtl ? Icons.arrow_back : Icons.arrow_forward),
        const Expanded(child: Text('Content')),
        Padding(
          padding: EdgeInsetsDirectional.only(start: 16),
          child: const Icon(Icons.settings),
        ),
      ],
    );
  }
}
```

### Directional Widgets

| Standard | Directional |
|----------|-------------|
| `EdgeInsets` | `EdgeInsetsDirectional` |
| `Padding` | `Padding` with `EdgeInsetsDirectional` |
| `Align` | `AlignmentDirectional` |
| `Positioned` | `PositionedDirectional` |
| `BorderRadius` | `BorderRadiusDirectional` |

```dart
// Use directional
Padding(
  padding: const EdgeInsetsDirectional.only(start: 16, end: 8),
  child: child,
)

Container(
  alignment: AlignmentDirectional.centerStart,
  child: child,
)

Container(
  decoration: const BoxDecoration(
    borderRadius: BorderRadiusDirectional.only(
      topStart: Radius.circular(8),
      bottomStart: Radius.circular(8),
    ),
  ),
)
```

## Organized Translations

### Split by Feature

```
lib/
  l10n/
    app_en.arb          # Common translations
    app_zh.arb
    features/
      auth_en.arb       # Auth feature translations
      auth_zh.arb
      settings_en.arb   # Settings feature translations
      settings_zh.arb
```

### Namespaced Keys

```json
// app_en.arb
{
  "auth_login": "Login",
  "auth_logout": "Logout",
  "auth_forgotPassword": "Forgot Password?",

  "settings_title": "Settings",
  "settings_language": "Language",
  "settings_theme": "Theme",

  "error_network": "Network error. Please try again.",
  "error_unknown": "An unknown error occurred."
}
```

## Testing

```dart
void main() {
  testWidgets('shows localized text', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        localizationsDelegates: AppLocalizations.localizationsDelegates,
        supportedLocales: AppLocalizations.supportedLocales,
        locale: const Locale('en'),
        home: const HomePage(),
      ),
    );

    expect(find.text('Hello'), findsOneWidget);
  });

  testWidgets('switches locale', (tester) async {
    await tester.pumpWidget(
      ProviderScope(
        child: MaterialApp(
          localizationsDelegates: AppLocalizations.localizationsDelegates,
          supportedLocales: AppLocalizations.supportedLocales,
          locale: const Locale('zh'),
          home: const HomePage(),
        ),
      ),
    );

    expect(find.text('你好'), findsOneWidget);
  });
}
```

## ARB Placeholders Reference

| Type | Format Options |
|------|----------------|
| `String` | None |
| `int` | `compact`, `compactCurrency`, `compactLong`, `compactSimpleCurrency` |
| `double` | `compact`, `compactCurrency`, `currency`, `decimalPattern`, `decimalPercentPattern`, `percentPattern`, `scientificPattern`, `simpleCurrency` |
| `DateTime` | Any `DateFormat` pattern (yMd, yMMMd, jm, etc.) |
| `num` | Same as `int` and `double` |

## Localization Checklist

| Item | Implementation |
|------|----------------|
| Dependencies | `flutter_localizations`, `intl` |
| l10n.yaml | Configure ARB paths and output |
| ARB files | Create for each supported locale |
| App config | Add delegates and supported locales |
| Generate | Run `flutter gen-l10n` |
| Use translations | `AppLocalizations.of(context)` |
| Date/number formatting | Use `intl` formatters with locale |
| RTL support | Use directional widgets |
| Persist preference | Save user's locale choice |
| Testing | Test with different locales |

---

*Flutter is a trademark of Google LLC. intl is an open-source package by the Dart team.*
