Flutter Localization for Mobile Apps: Strings, Numbers, and More

Maxim Gorin
4 min readOct 23, 2024

--

When building an app for a global audience, localization ensures that text, numbers, dates, and currencies are presented in a way that feels natural to users based on their locale. Localization isn’t just about translating strings — it’s about formatting data in a way that conforms to regional conventions.

Localization. Expanding your app to new markets

In the previous article, titled Boosting Flutter App Efficiency: Best Practices for Performance Optimization, we explored how to enhance your app’s performance. This series is designed specifically for those new to Flutter, and all examples are prepared for use in DartPad to save you from the hassle of setting up an IDE.

In this article, we’ll walk through how to use the intl package in Flutter to localize strings, numbers, currencies, and dates for multiple locales, including correct handling of the Indian numbering system for currencies.

Let’s dive into how you can localize your Flutter apps efficiently!

Localizing Strings

In Flutter, string localization can be handled by storing translations for each supported language in a map and dynamically loading the correct translation based on the user’s locale.

Introduction to app localization in iOS and Android

Here’s an example of a translation map:

Map<String, Map<String, String>> translations = {
'en': {
'greeting': 'Hello, {name}!',
},
'es': {
'greeting': '¡Hola, {name}!',
},
'hi': {
'greeting': 'नमस्ते, {name}!',
}
};

// Helper function to get translated text
String getTranslatedText(String key, Locale locale) {
return translations[locale.languageCode]![key]!;
}

To display a translated greeting, we can dynamically fetch the correct string based on the user’s locale:

Text(getTranslatedText('greeting', _locale).replaceFirst('{name}', 'Flutter Developer')),

This function retrieves the appropriate greeting for the selected language and replaces the {name} placeholder with the user’s name.

Localizing Numbers

Different regions format numbers differently. For example:

  • US (en): 247,884,528.51
  • Spain (es): 247.884.528,51
  • Germany (de): 247.884.528,51
  • France (fr): 247 884 528,51
  • India (hi): 247,884,528.51

Here’s how to localize a number in Flutter:

final number = 247884528.51;
final formattedNumber = NumberFormat("#,##0.00", _locale.toLanguageTag()).format(number);

Localizing Currencies

Currency formats can vary even more than numbers. In Here’s how currencies are formatted in different locales:

  • US (en): $720,361,177.62
  • Spain (es): 720.361.177,62 €
  • Germany (de): 720.361.177,62 €
  • France (fr): 720 361 177,62 €
  • India (hi): ₹72,03,61,177.62

To localize currency values:

final amount = 720361177.62;
final formattedCurrency = NumberFormat.simpleCurrency(locale: _locale.toLanguageTag()).format(amount);

Localizing Dates

Date formats also vary greatly between locales. In the US, a date like October 22, 2024 would be formatted as 22 अक्टूबर 2024 in Hindi.

To format dates correctly in Flutter:

final formattedDate = DateFormat.yMMMMd(_locale.toLanguageTag()).format(DateTime.now());

This ensures that dates are displayed in the correct format and language based on the locale.

Complete Example Code

Example: Flutter Localization

Here is the full example that localizes strings, numbers, currencies, and dates based on the user’s locale:

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';

void main() async {
// Initializes date formatting for all locales
await initializeDateFormatting();
runApp(MyApp());
}

class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
// Default locale is English
Locale _locale = Locale('en');

void _changeLanguage(Locale locale) {
setState(() {
_locale = locale;
});
}

// Translation map for English, Spanish, German, French, and Hindi
Map<String, Map<String, String>> translations = {
'en': {
'title': 'Localization Demo',
'greeting': 'Hello, {name}!',
'date_label': 'Date: {date}',
'number': 'Number: {value}',
'currency': 'Price: {amount}',
},
'es': {
'title': 'Demostración de Localización',
'greeting': '¡Hola, {name}!',
'date_label': 'Fecha: {date}',
'number': 'Número: {value}',
'currency': 'Precio: {amount}',
},
'de': {
'title': 'Lokalisierungsdemo',
'greeting': 'Hallo, {name}!',
'date_label': 'Datum: {date}',
'number': 'Nummer: {value}',
'currency': 'Preis: {amount}',
},
'fr': {
'title': 'Démonstration de localisation',
'greeting': 'Bonjour, {name}!',
'date_label': 'Date: {date}',
'number': 'Numéro : {value}',
'currency': 'Prix : {amount}',
},
'hi': {
'title': 'स्थानीयकरण डेमो',
'greeting': 'नमस्ते, {name}!',
'date_label': 'तारीख: {date}',
'number': 'संख्या: {value}',
'currency': 'मूल्य: {amount}',
}
};

// Helper function to get translated text
String getTranslatedText(String key) {
return translations[_locale.languageCode]![key]!;
}

@override
Widget build(BuildContext context) {
final name = 'Flutter Developer';
final number = 247884528.5136995904;
final amount = 720361177.6184808451;

// Format the number according to the locale
final formattedNumber = NumberFormat("#,##0.00", _locale.toLanguageTag()).format(number);

// Format the currency according to the locale
final formattedCurrency = NumberFormat.simpleCurrency(locale: _locale.toLanguageTag()).format(amount);

// Format the date according to the locale
final formattedDate = DateFormat.yMMMMd(_locale.toLanguageTag()).format(DateTime.now());

return MaterialApp(
debugShowCheckedModeBanner: false,
locale: _locale,
home: Scaffold(
appBar: AppBar(
title: Text(getTranslatedText('title')),
actions: [
DropdownButton<Locale>(
value: _locale,
icon: Icon(Icons.language),
onChanged: (Locale? newLocale) {
if (newLocale != null) _changeLanguage(newLocale);
},
items: [
DropdownMenuItem(
value: Locale('en'),
child: Text('English'),
),
DropdownMenuItem(
value: Locale('es'),
child: Text('Español'),
),
DropdownMenuItem(
value: Locale('de'),
child: Text('Deutsch'),
),
DropdownMenuItem(
value: Locale('fr'),
child: Text('Français'),
),
DropdownMenuItem(
value: Locale('hi'),
child: Text('हिन्दी'),
),
],
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(getTranslatedText('greeting').replaceFirst('{name}', name)),
SizedBox(height: 10),
Text(getTranslatedText('number').replaceFirst('{value}', formattedNumber)),
SizedBox(height: 10),
Text(getTranslatedText('currency').replaceFirst('{amount}', formattedCurrency)),
SizedBox(height: 10),
Text(getTranslatedText('date_label').replaceFirst('{date}', formattedDate)),
],
),
),
),
);
}
}

Conclusion

In this article, we explored how to localize a Flutter app using the intl package. We covered how to localize strings, numbers, currencies, and dates, focusing on the correct formats for various regions, including the Indian numbering system for currencies.

By using intl, your app can automatically adapt to different locales, ensuring that users around the world see content in formats that are familiar to them. This improves user experience and makes your app more accessible globally.

Great work! You’ve just unlocked another key skill in your Flutter toolkit. Keep pushing forward, try applying localization to your own apps, and don’t hesitate to share your progress. Your experience could inspire others, so feel free to spread the word and stay connected for more tips and insights. We’re excited to see what you achieve — keep going strong!

--

--

Maxim Gorin
Maxim Gorin

Written by Maxim Gorin

Team lead in mobile development with a passion for Fintech and Flutter. Sharing insights and stories from the tech and dev world on this blog.

No responses yet