Flutter Internationalization
Internationalization (often abbreviated as i18n) is the process of designing and developing software so that it can be easily adapted to different languages, regions, and cultures without engineering changes. Essentially, it's the practice of preparing your app to handle different locales (languages and regions) in the future. Internationalization allows you to provide different translations of your app's text, formatting, and layout, making it suitable for a global audience.
Why Internationalization is Important
- Global Reach: Internationalizing your app enables you to reach users from different parts of the world who speak different languages and have different cultural preferences.
- Localization Support: Internationalization is a prerequisite for localization (l10n), which is the process of translating and adapting your app for a specific region or language.
- Cultural Sensitivity: Different regions have different formats for dates, currencies, numbers, and text directions. Internationalization ensures that these are handled appropriately.
Key Concepts of Internationalization
1. Locale:A locale is a combination of language and region. For example:
- en_US–English as spoken in the United States.
- fr_FR–French as spoken in France.
- es_ES –Spanish as spoken in Spain.
Each locale has specific cultural preferences such as date formats, currency formats, and language direction (LTR/RTL).
2:String Externalization
Instead of hardcoding text directly into your code (e.g., "Hello, World!"), internationalization involves storing text in external files that can be swapped out depending on the user's locale. This allows you to easily provide translations without changing the core code.
Number and Date Formatting
In some regions, dates are written as MM/DD/YYYY, while in others, it's DD/MM/YYYY. Similarly, currencies, numbers, and measurements can be formatted differently. Internationalization ensures that these are formatted according to the region's conventions.
Text Direction
Some languages, like English, are written from left to right (LTR), while others, like Arabic and Hebrew, are written from right to left (RTL). Internationalization ensures that the layout and text flow correctly based on the locale.
Pluralization
Different languages have different rules for pluralization. For instance, English has two forms (one and many), while languages like Russian or Arabic may have more complex pluralization rules. Internationalization must handle these correctly.
Steps to Implement Internationalization in Flutter
Flutter provides robust support for internationalization, including tools and libraries to help you create a globally accessible app.
Add Dependencies: To enable internationalization in your Flutter app, you'll first need to add the necessary dependencies to your pubspec.yaml file. The main package for internationalization in Flutter is flutter_localizations, and you may also use the intl package for working with translations and formatting.
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.17.0
2.Setup Supported Locales: In the MaterialApp widget, you specify which locales your app supports. You also provide the list of localized delegates that help Flutter understand which resources it should load based on the current locale.
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Internationalization Example',
supportedLocales: [
Locale('en', 'US'), // English
Locale('es', 'ES'), // Spanish
Locale('fr', 'FR'), // French
],
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
home: MyHomePage(),
);
}
}
Create Translations:You'll store translations for your app's text in separate .arb files. These files are JSON-like and contain key-value pairs where the key is the text identifier and the value is the translation.
Example of an en_US.arb file:
{
"hello": "Hello",
"welcome": "Welcome to Flutter"
}
And for Spanish (es_ES.arb):
{
"hello": "Hola",
"welcome": "Bienvenido a Flutter"
}
Configure pubspec.yaml
Ensure you declare your assets in pubspec.yaml:
flutter:
assets:
- assets/lang/en.json
- assets/lang/es.json
- assets/lang/fr.json
Use the Translations in Code After setting up the .arb files and running the flutter_localizations package, you can load and use these translations in your app.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Intl.message('Welcome to Flutter', name: 'welcome')),
),
body: Center(
child: Text(Intl.message('Hello', name: 'hello')),
),
);
}
}
Localization and Formatting With the intl package, you can also handle date, number, and currency formatting in a locale-sensitive way. For example:
import 'package:intl/intl.dart';
// Formatting current date
String formattedDate = DateFormat.yMMMd().format(DateTime.now());
// Formatting currency
String formattedCurrency = NumberFormat.simpleCurrency(locale: 'en_US').format(1234.56);
<!-- Import required packages -->
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
<!-- Localization class -->
class AppLocalizations {
final Locale locale;
AppLocalizations(this.locale);
<!-- Retrieve instance from context -->
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
}
<!-- Localization delegate -->
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
late Map<String, String> _localizedStrings;
<!-- Load localization JSON file -->
Future<void> load() async {
String jsonString =
await rootBundle.loadString('assets/lang/${locale.languageCode}.json');
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedStrings =
jsonMap.map((key, value) => MapEntry(key, value.toString()));
}
<!-- Translate text -->
String translate(String key) {
return _localizedStrings[key] ?? key;
}
}
<!-- Localization delegate class -->
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) =>
['en', 'es', 'fr'].contains(locale.languageCode);
@override
Future<AppLocalizations> load(Locale locale) async {
AppLocalizations localizations = AppLocalizations(locale);
await localizations.load();
return localizations;
}
@override
bool shouldReload(LocalizationsDelegate<AppLocalizations> old) => false;
}
Switching Locales:You can provide a way for users to change the language of your app dynamically. One approach is to use a state management solution to track the current locale and update the Locale in the MaterialApp widget.
Example: Full Internationalization Setup
// Import required packages
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart';
// Main function
void main() {
runApp(MyApp());
}
// Root widget of the application
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Internationalization Example',
supportedLocales: [
Locale('en', 'US'), // English
Locale('es', 'ES'), // Spanish
],
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
home: MyHomePage(),
);
}
}
// Home page widget
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Intl.message('Welcome to Flutter', name: 'welcome')),
),
body: Center(
child: Text(Intl.message('Hello', name: 'hello')),
),
);
}
}
Using intl Package
The intl package in Flutter is a powerful tool for handling internationalization (i18n). It provides features like number formatting, date/time formatting, and message translation for multiple languages.
Add the intl Package to pubspec.yamlFirst, you need to include the intl package in your Flutter project’s dependencies:
dependencies:
flutter:
sdk: flutter
intl: ^0.18.0 # Latest version
Run the command:
flutter pub get
Generate Dart Code from ARB Files
To make localization easier, intl supports Application Resource Bundle (ARB) files, which are JSON-like files that contain translations for your app. You can use these ARB files to store translations for different languages.
Create lib/l10n/ Folder: Store your ARB files here (e.g., intl_en.arb for English, intl_es.arb for Spanish).
Step 1: Create ARB Files
intl_en.arb (for English)
{
"title": "Welcome",
"message": "Hello, how are you?"
}
Step 2: Create Localization Delegate
Create a new file, lib/l10n/intl.dart, where you'll define the logic to handle translations:
import 'package:intl/intl.dart';
class AppLocalizations {
static Future<AppLocalizations> load(Locale locale) {
return initializeMessages(locale.toString()).then((_) {
return AppLocalizations();
});
}
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
}
String get title {
return Intl.message(
'Welcome',
name: 'title',
desc: 'App title',
);
}
String get message {
return Intl.message(
'Hello, how are you?',
name: 'message',
desc: 'Greeting message',
);
}
}
Step 3: Generate Dart Code Using intl Package
The intl package comes with a tool to generate Dart code from the ARB files. To run this tool:
Create a l10n.yaml file in the root of the project:
arb-dir: lib/l10n
template-arb-file: intl_en.arb
output-localization-file: lib/l10n/intl.dart
Run the following command in the terminal:
flutter pub run intl_utils:generate
Step 4: Implement Localization in main.dart
import 'package:flutter/material.dart';
import 'l10n/intl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
supportedLocales: [
Locale('en', 'US'),
Locale('es', 'ES'),
],
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale?.languageCode) {
return supportedLocale;
}
}
return supportedLocales.first;
},
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).title),
),
body: Center(
child: Text(AppLocalizations.of(context).message),
),
);
}
}
Step 5: Enable Multiple Language Support
You can now switch between English and Spanish (or any other languages you add) by changing the language in the app settings, or by using a language picker in the app. For manual language switching, follow the same approach as I showed earlier with the locale object
Step 6: Use the intl Package for Date and Time Formatting
The intl package can also be used to format dates and numbers according to the user's locale. For example, formatting a date:
import 'package:intl/intl.dart';
void main() {
var now = DateTime.now();
print(DateFormat('yMMMd').format(now)); // E.g., January 29, 2025
}