Internationalization (i18n)
FLEX provides an internationalization (i18n) system that handles multiple locales, currency formatting, and date formatting for global commerce applications. The i18n feature automatically adapts to user preferences and device settings.
Overview
Section titled “Overview”The FLEX i18n system consists of three main components:
- LocaleManager: Handles locale selection, persistence, and change notifications
- CurrencyFormatManager: Formats monetary values according to locale conventions
- DateFormatManager: Formats dates and times with locale-specific patterns
All components work together seamlessly and automatically update when the user changes their locale preference.
Configuration
Section titled “Configuration”Basic Setup
Section titled “Basic Setup”Configure internationalization in your main.dart
:
await FlexCore.initialize([ I18nFeature( supportedLocales: [ const Locale('en', 'CA'), // English (Canada) const Locale('en', 'US'), // English (United States) const Locale('fr', 'CA'), // French (Canada) const Locale('es', 'MX'), // Spanish (Mexico) ], defaultLocale: const Locale('en', 'CA'), ), // Other features...]);
Supported Locales
Section titled “Supported Locales”Define which locales your app supports:
I18nFeature( supportedLocales: [ const Locale('en', 'US'), // English (US) const Locale('en', 'GB'), // English (UK) const Locale('en', 'CA'), // English (Canada) const Locale('fr', 'FR'), // French (France) const Locale('fr', 'CA'), // French (Canada) const Locale('es', 'ES'), // Spanish (Spain) const Locale('es', 'MX'), // Spanish (Mexico) const Locale('de', 'DE'), // German (Germany) const Locale('zh', 'CN'), // Chinese (Simplified) const Locale('ja', 'JP'), // Japanese ], defaultLocale: const Locale('en', 'US'),)
Locale Management
Section titled “Locale Management”LocaleManager
Section titled “LocaleManager”The LocaleManager
handles all locale-related operations:
final localeManager = FlexCore.container.get<LocaleManager>();
// Get current localefinal currentLocale = localeManager.currentLocale;
// Change localeawait localeManager.changeLocale(const Locale('fr', 'CA'));
// Listen to locale changeslocaleManager.onLocaleChanged.listen((newLocale) { print('Locale changed to: ${newLocale.toString()}');});
Locale Selection
Section titled “Locale Selection”Manual Locale Change
Section titled “Manual Locale Change”class LocaleService { final LocaleManager _localeManager = FlexCore.container.get<LocaleManager>();
Future<void> changeToFrench() async { await _localeManager.changeLocale(const Locale('fr', 'CA')); }
Future<void> changeToSpanish() async { await _localeManager.changeLocale(const Locale('es', 'MX')); }}
Locale Utilities
Section titled “Locale Utilities”Locale Validation
Section titled “Locale Validation”// Check if a locale is supportedbool isSupported = localeManager.isLocaleSupported(const Locale('fr', 'CA'));
// Find best matching localeLocale bestMatch = localeManager.findBestLocaleMatch(const Locale('fr'));
// Get locale by language codeLocale? frenchLocale = localeManager.getLocaleByCode('fr', 'CA');
Device Locale Handling
Section titled “Device Locale Handling”// Reset to device locale if supportedawait localeManager.resetToDeviceLocale();
// Reset to default localeawait localeManager.resetToDefault();
Currency Formatting
Section titled “Currency Formatting”CurrencyFormatManager
Section titled “CurrencyFormatManager”Format monetary values according to locale conventions:
final currencyFormatter = FlexCore.container.get<CurrencyFormatManager>();
// Format currency amountsString formatted = currencyFormatter.format(1234.56); // "$1,234.56" (en_US)String formatted = currencyFormatter.format(1234.56); // "1 234,56 €" (fr_FR)
// Format with specific currencyString usd = currencyFormatter.format(1234.56, currencyCode: 'USD');String eur = currencyFormatter.format(1234.56, currencyCode: 'EUR');String gbp = currencyFormatter.format(1234.56, currencyCode: 'GBP');
Currency Examples by Locale
Section titled “Currency Examples by Locale”class ProductPriceWidget extends StatelessWidget { final double price;
const ProductPriceWidget({required this.price});
@override Widget build(BuildContext context) { final currencyFormatter = FlexCore.container.get<CurrencyFormatManager>();
return Text( currencyFormatter.format(price), style: Theme.of(context).textTheme.headlineMedium, ); }}
Compact Currency Formatting
Section titled “Compact Currency Formatting”// For large numbers, use compact formattingString compact = currencyFormatter.formatCompact(1234567.89);// Results:// en_US: "$1.2M"// fr_FR: "1,2 M €"// ja_JP: "¥123万"
Currency Parsing
Section titled “Currency Parsing”// Parse currency strings back to numbersdouble? amount = currencyFormatter.parse("\$1,234.56"); // 1234.56double? amount = currencyFormatter.parse("€1.234,56"); // 1234.56
Custom Currency Display
Section titled “Custom Currency Display”class CurrencyDisplay extends StatelessWidget { final double amount; final String? currencyCode; final bool showCompact;
const CurrencyDisplay({ required this.amount, this.currencyCode, this.showCompact = false, });
@override Widget build(BuildContext context) { final formatter = FlexCore.container.get<CurrencyFormatManager>();
final formattedAmount = showCompact ? formatter.formatCompact(amount, currencyCode: currencyCode) : formatter.format(amount, currencyCode: currencyCode);
return Text(formattedAmount); }}
Date Formatting
Section titled “Date Formatting”DateFormatManager
Section titled “DateFormatManager”Format dates and times according to locale conventions:
final dateFormatter = FlexCore.container.get<DateFormatManager>();
final now = DateTime.now();
// Use predefined formatsString short = dateFormatter.format(now, format: 'short'); // "12/25/23"String medium = dateFormatter.format(now, format: 'medium'); // "Dec 25, 2023"String long = dateFormatter.format(now, format: 'long'); // "December 25, 2023"String time = dateFormatter.format(now, format: 'shortTime'); // "2:30 PM"String full = dateFormatter.format(now, format: 'fullDateTime'); // "December 25, 2023 2:30:45 PM"
Custom Date Patterns
Section titled “Custom Date Patterns”// Use custom patternsString custom = dateFormatter.format(now, format: 'EEEE, MMMM d, y');// Results:// en_US: "Monday, December 25, 2023"// fr_CA: "lundi 25 décembre 2023"// es_MX: "lunes, 25 de diciembre de 2023"
Relative Time Formatting
Section titled “Relative Time Formatting”// Format relative to current timefinal yesterday = DateTime.now().subtract(const Duration(days: 1));final lastWeek = DateTime.now().subtract(const Duration(days: 7));final lastMonth = DateTime.now().subtract(const Duration(days: 35));
String relative1 = dateFormatter.formatRelative(yesterday); // "1 day ago"String relative7 = dateFormatter.formatRelative(lastWeek); // "7 days ago"String relative35 = dateFormatter.formatRelative(lastMonth); // "1 month ago"
Date Parsing
Section titled “Date Parsing”// Parse date stringsDateTime? parsed = dateFormatter.parse("Dec 25, 2023", format: 'medium');DateTime? customParsed = dateFormatter.parse("2023-12-25", format: 'yyyy-MM-dd');
Order History Example
Section titled “Order History Example”class OrderHistoryItem extends StatelessWidget { final Order order;
const OrderHistoryItem({required this.order});
@override Widget build(BuildContext context) { final dateFormatter = FlexCore.container.get<DateFormatManager>(); final currencyFormatter = FlexCore.container.get<CurrencyFormatManager>();
return Card( child: ListTile( title: Text('Order #${order.id}'), subtitle: Text( dateFormatter.formatRelative(order.createdAt), ), trailing: Text( currencyFormatter.format(order.total), style: const TextStyle(fontWeight: FontWeight.bold), ), ), ); }}
Reactive UI Updates
Section titled “Reactive UI Updates”Automatic Updates
Section titled “Automatic Updates”All formatters automatically update when the locale changes:
class ProductCard extends StatelessWidget { final Product product;
@override Widget build(BuildContext context) { final localeManager = FlexCore.container.get<LocaleManager>(); final currencyFormatter = FlexCore.container.get<CurrencyFormatManager>(); final dateFormatter = FlexCore.container.get<DateFormatManager>();
return StreamBuilder<Locale>( stream: localeManager.onLocaleChanged, builder: (context, snapshot) { // This widget automatically rebuilds when locale changes return Card( child: Column( children: [ Text(product.name), Text(currencyFormatter.format(product.price)), Text(dateFormatter.formatRelative(product.createdAt)), ], ), ); }, ); }}
Custom Date Formats by Feature
Section titled “Custom Date Formats by Feature”class CustomDateFormats { static const Map<String, String> orderFormats = { 'en_US': 'MMM d, yyyy', 'en_GB': 'd MMM yyyy', 'fr_FR': 'd MMM yyyy', 'de_DE': 'd. MMM yyyy', };
static const Map<String, String> shipmentFormats = { 'en_US': 'EEE, MMM d', 'en_GB': 'EEE d MMM', 'fr_FR': 'EEE d MMM', 'de_DE': 'EEE, d. MMM', };
static String formatOrderDate(DateTime date) { final localeManager = FlexCore.container.get<LocaleManager>(); final dateFormatter = FlexCore.container.get<DateFormatManager>();
final locale = localeManager.currentLocale.toString(); final format = orderFormats[locale] ?? orderFormats['en_US']!;
return dateFormatter.format(date, format: format); }}
Testing
Section titled “Testing”Mocking Locale Changes
Section titled “Mocking Locale Changes”class MockLocaleManager extends Mock implements LocaleManager { final StreamController<Locale> _controller = StreamController<Locale>.broadcast();
@override Stream<Locale> get onLocaleChanged => _controller.stream;
@override Locale currentLocale = const Locale('en', 'US');
@override Future<void> changeLocale(Locale locale) async { currentLocale = locale; _controller.add(locale); }
void dispose() { _controller.close(); }}
Testing Currency Formatting
Section titled “Testing Currency Formatting”group('Currency Formatting Tests', () { late CurrencyFormatManager formatter; late MockLocaleManager mockLocaleManager;
setUp(() { mockLocaleManager = MockLocaleManager(); // Set up test container with mock FlexCore.container.registerSingleton<LocaleManager>(mockLocaleManager); formatter = CurrencyFormatManager(); });
test('formats USD correctly for US locale', () { mockLocaleManager.currentLocale = const Locale('en', 'US'); expect(formatter.format(1234.56), equals('\$1,234.56')); });
test('formats EUR correctly for French locale', () { mockLocaleManager.currentLocale = const Locale('fr', 'FR'); expect(formatter.format(1234.56, currencyCode: 'EUR'), contains('€')); });});
Best Practices
Section titled “Best Practices”Locale Selection UX
Section titled “Locale Selection UX”// ✅ Good - Show native language namesclass LocaleSelector extends StatelessWidget { @override Widget build(BuildContext context) { final localeManager = FlexCore.container.get<LocaleManager>();
return ListView.builder( itemCount: localeManager.supportedLocales.length, itemBuilder: (context, index) { final locale = localeManager.supportedLocales[index]; return ListTile( title: Text(localeManager.getLocaleName(locale)), subtitle: Text('${locale.languageCode}_${locale.countryCode}'), trailing: localeManager.currentLocale == locale ? const Icon(Icons.check) : null, onTap: () => localeManager.changeLocale(locale), ); }, ); }}
Formatting Consistency
Section titled “Formatting Consistency”// ✅ Good - Use centralized formattingclass PriceUtils { static String formatPrice(double price, {String? currency}) { final formatter = FlexCore.container.get<CurrencyFormatManager>(); return formatter.format(price, currencyCode: currency); }
static String formatDate(DateTime date, {String? format}) { final formatter = FlexCore.container.get<DateFormatManager>(); return formatter.format(date, format: format); }}
// ❌ Avoid - Manual formattingText('\$${price.toStringAsFixed(2)}'); // Doesn't respect locale
Resource Management
Section titled “Resource Management”// ✅ Good - Dispose resources properly@overridevoid dispose() { FlexCore.container.get<LocaleManager>().dispose(); FlexCore.container.get<DateFormatManager>().dispose(); FlexCore.container.get<CurrencyFormatManager>().dispose(); super.dispose();}
The FLEX i18n system provides a robust foundation for creating globally-accessible commerce applications with proper locale support, currency formatting, and date handling.