Flutter Integration
Learn how to integrate internationalization with Flutter applications.
Integrate internationalization with your Flutter application to support multiple languages using Lengrise-managed JSON translations, with a similar approach to i18next.
Prerequisites
- Flutter project (v3.0.0+)
- Dart (v2.17.0+)
- Translations downloaded from Lengrise
Pull Translation Files
First, you must pull your translation files from Lengrise API. These files will contain all the translated text for your application.
- Go to the Installation Guide and follow the steps to download your translations
- Place the downloaded JSON files in your project as shown in the project structure below
- Ensure that each language has its own JSON file named with the language code (e.g.,
en.json
,es.json
)
Integration Setup
After downloading your translation files, add the necessary packages to your pubspec.yaml
:
dependencies:
flutter:
sdk:
flutter flutter_i18n: ^0.33.0
flutter_localizations:
sdk:
flutter shared_preferences: ^2.1.0
provider: ^6.0.5
Run flutter pub get
to install the dependencies.
Project Structure
Configuration Steps
1. Update your pubspec.yaml
Make sure to add the assets path to your pubspec.yaml
to include your translation JSON files:
flutter:
assets:
- assets/i18n/
2. Create an i18n Service
Create a lib/services/i18n_service.dart
file:
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
class I18nService {
// Private constructor
I18nService._();
// Singleton instance
static final I18nService _instance = I18nService._();
// Factory constructor to return the singleton instance
factory I18nService() => _instance;
// Map of loaded translations
Map<String, dynamic> _translations = {};
String _currentLocale = 'en';
// Getter for current locale
String get currentLocale => _currentLocale;
// Initialize translations
Future<void> init(String locale) async {
await loadTranslations(locale);
}
// Load translations for a specific locale
Future<void> loadTranslations(String locale) async {
try {
_currentLocale = locale;
final jsonString = await rootBundle.loadString('assets/i18n/$locale.json');
_translations = json.decode(jsonString);
} catch (e) {
debugPrint('Error loading translations: $e');
// Fall back to empty translations map
_translations = {};
}
}
// Get a translation by key
String translate(String key, {Map<String, dynamic>? params}) {
final keys = key.split('.');
dynamic value = _translations;
// Navigate through nested JSON
for (final k in keys) {
if (value is Map && value.containsKey(k)) {
value = value[k];
} else {
return key; // Key not found, return the key itself
}
}
if (value is String) {
// Process the string with parameters if provided
if (params != null && params.isNotEmpty) {
String result = value;
params.forEach((paramKey, paramValue) {
result = result.replaceAll('{{$paramKey}}', paramValue.toString());
});
return result;
}
return value;
}
return key; // If the value is not a string, return the key
}
// Handle pluralization
String translatePlural(String key, int count) {
final keys = key.split('.');
dynamic value = _translations;
// Navigate through nested JSON
for (final k in keys) {
if (value is Map && value.containsKey(k)) {
value = value[k];
} else {
return key; // Key not found, return the key itself
}
}
if (value is Map) {
// Check if the map has pluralization keys
if (count == 0 && value.containsKey('zero')) {
return value['zero'].replaceAll('{{count}}', count.toString());
} else if (count == 1 && value.containsKey('one')) {
return value['one'].replaceAll('{{count}}', count.toString());
} else if (value.containsKey('other')) {
return value['other'].replaceAll('{{count}}', count.toString());
}
}
return key; // Unable to handle pluralization, return the key
}
}
3. Create a Locale Provider
Create a lib/services/locale_provider.dart
file to manage locale state:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'i18n_service.dart';
class LocaleProvider with ChangeNotifier {
final I18nService _i18nService = I18nService();
Locale _locale = const Locale('en');
final String _prefsKey = 'app_locale';
LocaleProvider() {
_loadSavedLocale();
}
Locale get locale => _locale;
I18nService get i18n => _i18nService;
// Helper method for translations
String t(String key, {Map<String, dynamic>? params}) {
return _i18nService.translate(key, params: params);
}
// Helper method for pluralization
String plural(String key, int count) {
return _i18nService.translatePlural(key, count);
}
// Get all supported locales
List<Locale> get supportedLocales {
return const [
Locale('en'), // English
Locale('es'), // Spanish
];
}
// Change the locale
Future<void> setLocale(Locale locale) async {
if (!isSupported(locale)) return;
_locale = locale;
await _i18nService.loadTranslations(locale.languageCode);
await _saveLocale(locale.languageCode);
notifyListeners();
}
// Check if a locale is supported
bool isSupported(Locale locale) {
return supportedLocales.contains(
Locale(locale.languageCode)
);
}
// Load the saved locale from storage
Future<void> _loadSavedLocale() async {
final prefs = await SharedPreferences.getInstance();
final savedLocale = prefs.getString(_prefsKey);
if (savedLocale != null && isSupported(Locale(savedLocale))) {
_locale = Locale(savedLocale);
await _i18nService.loadTranslations(savedLocale);
notifyListeners();
} else {
// Initialize with default locale
await _i18nService.init(_locale.languageCode);
}
}
// Save the locale to storage
Future<void> _saveLocale(String languageCode) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_prefsKey, languageCode);
}
}
4. Update Your Main App File
Update your lib/main.dart
file to include internationalization support:
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:your_app_name/services/locale_provider.dart';
import 'package:your_app_name/screens/home_screen.dart';
import 'package:your_app_name/screens/settings_screen.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(
ChangeNotifierProvider(
create: (context) => LocaleProvider(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>(context);
return MaterialApp(
title: localeProvider.t('app.title'),
// Locale configuration
locale: localeProvider.locale,
supportedLocales: localeProvider.supportedLocales,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
// App routes
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/settings': (context) => const SettingsScreen(),
},
);
}
}
5. Create a Language Switcher Widget
Create a lib/widgets/language_switcher.dart
file:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:your_app_name/services/locale_provider.dart';
class LanguageSwitcher extends StatelessWidget {
const LanguageSwitcher({Key? key}) : super(key: key);
Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>(context);
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
localeProvider.t('settings.language'),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
children: [
_LanguageButton(
language: localeProvider.t('languages.english'),
locale: const Locale('en'),
isSelected: localeProvider.locale.languageCode == 'en',
onTap: () => localeProvider.setLocale(const Locale('en')),
),
_LanguageButton(
language: localeProvider.t('languages.spanish'),
locale: const Locale('es'),
isSelected: localeProvider.locale.languageCode == 'es',
onTap: () => localeProvider.setLocale(const Locale('es')),
),
],
),
],
),
),
);
}
}
class _LanguageButton extends StatelessWidget {
final String language;
final Locale locale;
final bool isSelected;
final VoidCallback onTap;
const _LanguageButton({
required this.language,
required this.locale,
required this.isSelected,
required this.onTap,
});
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
decoration: BoxDecoration(
color: isSelected ? Theme.of(context).primaryColor : Colors.grey[200],
borderRadius: BorderRadius.circular(8),
),
child: Text(
language,
style: TextStyle(
color: isSelected ? Colors.white : Colors.black87,
),
),
),
);
}
}
6. Create a Settings Screen
Create a lib/screens/settings_screen.dart
file:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:your_app_name/services/locale_provider.dart';
import 'package:your_app_name/widgets/language_switcher.dart';
class SettingsScreen extends StatelessWidget {
const SettingsScreen({Key? key}) : super(key: key);
Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text(localeProvider.t('settings.title')),
),
body: ListView(
children: const [
LanguageSwitcher(),
// Add other settings here
],
),
);
}
}
7. Create a Home Screen
Create a lib/screens/home_screen.dart
file:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:your_app_name/services/locale_provider.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text(localeProvider.t('home.welcome')),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Text(
localeProvider.t('home.welcome'),
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 8),
Center(
child: Text(
localeProvider.t('home.description'),
style: const TextStyle(
fontSize: 16,
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 24),
Text(
localeProvider.t('features.title'),
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
_buildFeature(context, localeProvider.t('features.easy_to_use')),
_buildFeature(context, localeProvider.t('features.fast_translation')),
_buildFeature(context, localeProvider.t('features.flexible_integration')),
const SizedBox(height: 24),
Center(
child: Text(
localeProvider.t('greeting', params: {'name': 'Flutter Developer'}),
style: const TextStyle(fontSize: 18),
),
),
const SizedBox(height: 24),
Center(
child: Text(
localeProvider.plural('items', 5),
style: const TextStyle(fontSize: 18),
),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/settings');
},
child: Text(localeProvider.t('settings.title')),
),
],
),
),
);
}
Widget _buildFeature(BuildContext context, String text) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
const Icon(Icons.check_circle, color: Colors.green),
const SizedBox(width: 8),
Text(text, style: const TextStyle(fontSize: 16)),
],
),
);
}
}
Using Translations
Basic Usage
Access translations in your widgets using the provider:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:your_app_name/services/locale_provider.dart';
class MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>(context);
return Column(
children: [
// Simple translation
Text(localeProvider.t('home.welcome')),
// Translation with parameters
Text(localeProvider.t('greeting', params: {'name': 'John'})),
// Pluralization
Text(localeProvider.plural('items', 3)),
],
);
}
}
Switching Languages
The language can be switched by calling the setLocale
method on your locale provider:
// Inside a widget
final localeProvider = Provider.of<LocaleProvider>(context, listen: false);
localeProvider.setLocale(Locale('es'));
Custom Extension (Optional)
For cleaner code, you can add an extension on the BuildContext:
// In a separate file, e.g., lib/extensions/context_extensions.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:your_app_name/services/locale_provider.dart';
extension ContextExtension on BuildContext {
LocaleProvider get localeProvider => Provider.of<LocaleProvider>(this, listen: true);
String t(String key, {Map<String, dynamic>? params}) {
return localeProvider.t(key, params: params);
}
String plural(String key, int count) {
return localeProvider.plural(key, count);
}
}
// Then in widgets:
// Text(context.t('home.welcome'))
// Text(context.plural('items', 3))
Resources
For more detailed information, check out these resources: