Internationalization (i18n)
Comprehensive guide for handling multiple languages and localization in the Flutter application.
The boilerplate uses the official Flutter internationalization (i18n) system, powered by the flutter_localizations package and the gen-l10n tool. This approach ensures high performance, type safety, and seamless integration with the Flutter ecosystem.
Overview
The localization system consists of three main parts:
- ARB Files: JSON-like files (
.arb) containing key-value pairs for translations. - Code Generation: The
gen-l10ntool generates type-safe Dart classes from ARB files. - State Management: Riverpod providers manage the current locale and persist user preferences.
Configuration
The localization behavior is configured in the l10n.yaml file located in the root of the Flutter project:
arb-dir: lib/l10n
template-arb-file: app_id.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations
output-dir: lib/l10n/generated
nullable-getter: falsearb-dir: Where your translation files live.template-arb-file: The source of truth for all localization keys (in this case, Indonesian).output-class: The name of the generated class used in code.
ARB Files
Translation files are located in lib/l10n/. The boilerplate currently supports:
app_id.arb: Bahasa Indonesia (Default)app_en.arb: English
Example Entry
{
"hello": "Halo!",
"@hello": {
"description": "Greeting shown on the home screen"
}
}Adding New Strings
- Open
lib/l10n/app_id.arb(the template file). - Add your new key and value.
- Add the corresponding value in
lib/l10n/app_en.arb.
Automatic Generation
The project is configured with generate: true in pubspec.yaml. This means Flutter will automatically run gen-l10n whenever you build the app or save files in many IDEs. If you need to trigger it manually, use:
flutter gen-l10nFormatting Dates and Numbers
For formatting dates, currencies, and numbers according to the user's locale, the boilerplate uses the intl package.
import 'package:intl/intl.dart';
// Formats date based on current locale
String formatDate(DateTime date, String locale) {
return DateFormat.yMMMMd(locale).format(date);
}
// Inside a widget
Text(DateFormat.yMMMMd(Localizations.localeOf(context).toString()).format(DateTime.now()))Usage in Code
To access localized strings, use the AppLocalizations class provided by the BuildContext.
Basic Usage
import 'package:mobile/l10n/generated/app_localizations.dart';
// ... inside build method
final l10n = AppLocalizations.of(context);
Text(l10n.hello)With Placeholders
If your ARB entry has placeholders:
"welcomeUser": "Welcome, {name}"
Usage:
Text(l10n.welcomeUser('John'))State Management & Persistence
The application's locale is managed by Riverpod and is persisted both locally and on the server.
Persistence Flow
- User selects a language in the app settings.
UserPreferencesNotifierupdates the state and saves toSharedPreferences.- The change is asynchronously synced to the backend server.
- On app restart, the locale is restored from
SharedPreferences.
Changing Language Programmatically
You can use the languageNotifier or update through userPreferencesProvider:
// Using LanguageNotifier
ref.read(languageNotifierProvider.notifier).setLanguage('en');
// OR directly via UserPreferences
ref.read(userPreferencesProvider.notifier).setLanguage('id');Adding a New Language
To add support for a new language (e.g., Japanese - ja):
- Create ARB File: Create
lib/l10n/app_ja.arband translate all keys from the template. - Update SupportedLocales: Edit
lib/providers/user_preferences_provider.dart:static const Locale japanese = Locale('ja'); static const List<Locale> all = [indonesian, english, japanese]; static const Map<String, String> names = { 'id': 'Bahasa Indonesia', 'en': 'English', 'ja': '日本語', }; - UI Updates: The language selection dialog in
lib/widgets/language_settings.dartautomatically pulls fromSupportedLocales.all, so it will show up there automatically.
Best Practices
- Descriptive Keys: Use semantic names like
loginButtonLabelinstead of generic ones likebutton1. - Provide Descriptions: Use the
@keysyntax in the template ARB to provide context for translators. - Avoid Hardcoding: Never hardcode user-facing strings in widgets. Always add them to ARB files.
- Handle Plurals: Use Flutter's built-in plural support in ARB files for complex quantities.