Claude Code Plugins

Community-maintained marketplace

Feedback

custom-plugin-flutter-skill-localization

@pluginagentmarketplace/custom-plugin-flutter
1
0

Production-grade Flutter localization mastery - ARB files, flutter_localizations, intl package, pluralization, RTL support, dynamic locale switching with comprehensive code examples

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name custom-plugin-flutter-skill-localization
description Production-grade Flutter localization mastery - ARB files, flutter_localizations, intl package, pluralization, RTL support, dynamic locale switching with comprehensive code examples
sasmp_version 1.3.0
bonded_agent 01-flutter-ui-development
bond_type PRIMARY_BOND

custom-plugin-flutter: Localization Skill

Quick Start - Production Localization Setup

# pubspec.yaml
dependencies:
  flutter_localizations:
    sdk: flutter
  intl: ^0.19.0

flutter:
  generate: true

# l10n.yaml (create in project root)
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations
nullable-getter: false
// lib/l10n/app_en.arb
{
  "@@locale": "en",
  "appTitle": "My App",
  "@appTitle": {
    "description": "The app title"
  },
  "welcomeMessage": "Welcome, {name}!",
  "@welcomeMessage": {
    "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"
      }
    }
  }
}

// lib/l10n/app_tr.arb
{
  "@@locale": "tr",
  "appTitle": "Uygulamam",
  "welcomeMessage": "Hoş geldin, {name}!",
  "itemCount": "{count, plural, =0{Öğe yok} =1{1 öğe} other{{count} öğe}}"
}

// main.dart
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      locale: Locale('en'),
      home: HomePage(),
    );
  }
}

// Usage in widgets
Text(AppLocalizations.of(context).welcomeMessage('John'))
Text(AppLocalizations.of(context).itemCount(5))

1. ARB File Syntax

Basic Messages

{
  "@@locale": "en",

  "greeting": "Hello",
  "@greeting": {
    "description": "A simple greeting"
  },

  "pageTitle": "Settings Page",
  "@pageTitle": {
    "description": "Title of the settings page",
    "context": "SettingsPage"
  }
}

Placeholders

{
  "welcomeUser": "Welcome, {userName}!",
  "@welcomeUser": {
    "placeholders": {
      "userName": {
        "type": "String",
        "example": "Alice"
      }
    }
  },

  "priceLabel": "Price: {price}",
  "@priceLabel": {
    "placeholders": {
      "price": {
        "type": "double",
        "format": "currency",
        "optionalParameters": {
          "symbol": "$",
          "decimalDigits": 2
        }
      }
    }
  },

  "dateLabel": "Date: {date}",
  "@dateLabel": {
    "placeholders": {
      "date": {
        "type": "DateTime",
        "format": "yMMMd"
      }
    }
  }
}

Pluralization

{
  "cartItems": "{count, plural, =0{Your cart is empty} =1{1 item in cart} other{{count} items in cart}}",
  "@cartItems": {
    "placeholders": {
      "count": {
        "type": "int"
      }
    }
  },

  "remainingAttempts": "{count, plural, =0{No attempts remaining} =1{1 attempt remaining} other{{count} attempts remaining}}",
  "@remainingAttempts": {
    "placeholders": {
      "count": {
        "type": "int"
      }
    }
  }
}

Gender Selection

{
  "userGreeting": "{gender, select, male{Mr. {name}} female{Ms. {name}} other{{name}}}",
  "@userGreeting": {
    "placeholders": {
      "gender": {
        "type": "String"
      },
      "name": {
        "type": "String"
      }
    }
  }
}

2. Date & Number Formatting

import 'package:intl/intl.dart';

// Date formatting
final dateFormatter = DateFormat.yMMMd(locale);
Text(dateFormatter.format(DateTime.now()))

// Number formatting
final numberFormatter = NumberFormat.decimalPattern(locale);
Text(numberFormatter.format(1234567.89))

// Currency formatting
final currencyFormatter = NumberFormat.currency(
  locale: locale,
  symbol: '\$',
  decimalDigits: 2,
);
Text(currencyFormatter.format(99.99))

// Percentage
final percentFormatter = NumberFormat.percentPattern(locale);
Text(percentFormatter.format(0.75)) // 75%

// Compact notation
final compactFormatter = NumberFormat.compact(locale: locale);
Text(compactFormatter.format(1500000)) // 1.5M

3. Dynamic Locale Switching

class LocaleProvider extends ChangeNotifier {
  Locale _locale = Locale('en');

  Locale get locale => _locale;

  void setLocale(Locale locale) {
    if (!AppLocalizations.supportedLocales.contains(locale)) return;
    _locale = locale;
    notifyListeners();
  }

  Future<void> loadSavedLocale() async {
    final prefs = await SharedPreferences.getInstance();
    final languageCode = prefs.getString('languageCode');
    if (languageCode != null) {
      _locale = Locale(languageCode);
      notifyListeners();
    }
  }

  Future<void> saveLocale(Locale locale) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('languageCode', locale.languageCode);
    setLocale(locale);
  }
}

// Usage with Provider
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<LocaleProvider>(
      builder: (context, localeProvider, child) {
        return MaterialApp(
          locale: localeProvider.locale,
          localizationsDelegates: AppLocalizations.localizationsDelegates,
          supportedLocales: AppLocalizations.supportedLocales,
          home: HomePage(),
        );
      },
    );
  }
}

// Language selector
class LanguageSelector extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final localeProvider = context.watch<LocaleProvider>();

    return DropdownButton<Locale>(
      value: localeProvider.locale,
      items: AppLocalizations.supportedLocales.map((locale) {
        return DropdownMenuItem(
          value: locale,
          child: Text(_getLanguageName(locale)),
        );
      }).toList(),
      onChanged: (locale) {
        if (locale != null) {
          localeProvider.saveLocale(locale);
        }
      },
    );
  }

  String _getLanguageName(Locale locale) {
    switch (locale.languageCode) {
      case 'en': return 'English';
      case 'tr': return 'Türkçe';
      case 'es': return 'Español';
      default: return locale.languageCode;
    }
  }
}

4. RTL (Right-to-Left) Support

// Automatic direction based on locale
MaterialApp(
  locale: Locale('ar'), // Arabic - RTL
  localizationsDelegates: [
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
)

// Check directionality
final isRtl = Directionality.of(context) == TextDirection.rtl;

// Force direction
Directionality(
  textDirection: TextDirection.rtl,
  child: MyWidget(),
)

// Directional padding
Padding(
  padding: EdgeInsetsDirectional.only(start: 16, end: 8),
  child: Text('Directional padding'),
)

// Positioned directionally
PositionedDirectional(
  start: 0,
  end: null,
  child: widget,
)

// Row with automatic alignment
Row(
  textDirection: Directionality.of(context),
  children: [...],
)

// Icons that should flip
Transform.flip(
  flipX: isRtl,
  child: Icon(Icons.arrow_forward),
)

5. Testing Localization

void main() {
  testWidgets('displays localized text in Turkish', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        locale: Locale('tr'),
        localizationsDelegates: AppLocalizations.localizationsDelegates,
        supportedLocales: AppLocalizations.supportedLocales,
        home: HomePage(),
      ),
    );

    expect(find.text('Hoş geldin!'), findsOneWidget);
  });

  testWidgets('handles pluralization correctly', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        locale: Locale('en'),
        localizationsDelegates: AppLocalizations.localizationsDelegates,
        supportedLocales: AppLocalizations.supportedLocales,
        home: Builder(
          builder: (context) {
            return Column(
              children: [
                Text(AppLocalizations.of(context).itemCount(0)),
                Text(AppLocalizations.of(context).itemCount(1)),
                Text(AppLocalizations.of(context).itemCount(5)),
              ],
            );
          },
        ),
      ),
    );

    expect(find.text('No items'), findsOneWidget);
    expect(find.text('1 item'), findsOneWidget);
    expect(find.text('5 items'), findsOneWidget);
  });

  testWidgets('RTL layout is correct for Arabic', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        locale: Locale('ar'),
        localizationsDelegates: [
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
        ],
        supportedLocales: [Locale('ar')],
        home: HomePage(),
      ),
    );

    final direction = Directionality.of(tester.element(find.byType(HomePage)));
    expect(direction, TextDirection.rtl);
  });
}

6. slang Package Alternative

// Alternative: slang package for type-safe translations
// slang.yaml
base_locale: en
input_directory: lib/i18n
output_directory: lib/gen

// lib/i18n/strings_en.i18n.json
{
  "greeting": "Hello $name",
  "items": {
    "zero": "No items",
    "one": "One item",
    "other": "$n items"
  }
}

// Usage
Text(t.greeting(name: 'John'))
Text(t.items(n: 5))

Troubleshooting Guide

Issue: Localization not loading

1. Run: flutter gen-l10n
2. Verify l10n.yaml in project root
3. Check ARB file syntax (valid JSON)
4. Ensure flutter: generate: true in pubspec.yaml
5. Import: flutter_gen/gen_l10n/app_localizations.dart

Issue: Locale not recognized

1. Verify locale is in supportedLocales
2. Check locale code format (en vs en_US)
3. Ensure localizationsDelegates includes your delegate
4. Check @@locale in ARB file matches

Issue: Placeholders not working

1. Verify placeholder name matches exactly
2. Check placeholder type in @message
3. Ensure placeholder syntax: {name}
4. Run flutter gen-l10n to regenerate

Issue: RTL not applying

1. Add GlobalWidgetsLocalizations.delegate
2. Use Directional variants (EdgeInsetsDirectional)
3. Check Directionality widget wrapping
4. Verify locale is RTL language

Localization Best Practices

1. Never hardcode strings in widgets
2. Use descriptive keys (settingsPageTitle vs title)
3. Include context in @descriptions
4. Test all supported locales
5. Handle long text gracefully (overflow)
6. Use ICU message format for complex cases
7. Keep translations organized by feature
8. Review with native speakers

Build globally accessible Flutter apps with proper localization.