Best Localization
Best Localization is a lightweight and flexible localization package for Flutter. It supports dynamic translations, interpolation, pluralization, remote translations, fallback locales, and custom localization for the Kurdish language, including widgets like Material and Cupertino.
Features
- Dynamic Translations: Translate text based on locale dynamically.
- Multiple File Format Support (NEW! 🎉): Load translations from JSON, CSV, YAML, or XML files.
- Remote Translations (NEW! 🎉): Load translations from HTTP API with automatic caching.
- Fallback Locale (NEW! 🎉): Automatically fall back to a default language when translations are missing.
- Translation Key Verification Tool (NEW! 🎉): Command-line tool to verify translation keys across locales.
- Interpolation: Insert dynamic values into translations (e.g., Hello, {name}!).
- Pluralization: Handle plural forms for text based on numeric values.
- BuildContext Extensions: Easy access to translations without boilerplate code.
- Custom Localization for Kurdish:
- Supports Kurdish (ku) localization for Material and Cupertino widgets.
- Includes custom date and number formatting.
- Seamless Integration:
- Works with Flutter's native Localizations system.
- Fully compatible with MaterialApp and CupertinoApp.
- Flexible Translation Loading:
- Load from assets (JSON, CSV, YAML, XML)
- Load from remote API (HTTP)
- Define translations directly in Dart maps
- Create custom loaders for your specific needs
Getting Started
Installation
1- Add best_localization
To install best_localization package, run the following commands in your terminal:
flutter pub add best_localization
or add best_localization to your pubspec.yaml:
dependencies:
best_localization: ^1.2.1
2- Add flutter_localizations
Add the flutter_localizations package to your pubspec.yaml file:
dependencies:
flutter_localizations:
sdk: flutter
Using the package
1. Initialize Localization
You can load translations in multiple ways:
Option A: From a Map (Direct)
final translations = {
'en': {
'hello': 'Hello, {name}!',
'welcome': 'welcome',
},
'ku': {
'hello': 'سڵاو، {name}!',
'welcome': 'بەخێربێیت',
},
//more language...
};
Option B: From JSON File
// Create assets/translations/translations.json
{
"en": {
"hello": "Hello, {name}!",
"welcome": "Welcome"
},
"ku": {
"hello": "سڵاو، {name}!",
"welcome": "بەخێربێیت"
}
}
Option C: From CSV File
key,en,ku
hello,Hello,سڵاو
welcome,Welcome,بەخێربێیت
Option D: From YAML File
en:
hello: Hello, {name}!
welcome: Welcome
ku:
hello: سڵاو، {name}!
welcome: بەخێربێیت
Option E: From XML File
<?xml version="1.0" encoding="UTF-8"?>
<translations>
<language code="en">
<string key="hello">Hello, {name}!</string>
<string key="welcome">Welcome</string>
</language>
<language code="ku">
<string key="hello">سڵاو، {name}!</string>
<string key="welcome">بەخێربێیت</string>
</language>
</translations>
Option F: From Remote API
// Load from your server with automatic caching
Loaders.remote(
url: 'https://api.example.com/translations',
cacheEnabled: true,
cacheDuration: Duration(hours: 24),
)
Don't forget to add assets to pubspec.yaml:
flutter:
assets:
- assets/translations/
📚 For detailed loader documentation, see Loader Guide 📚 For remote translations, see Remote Loader Guide 📚 For translation verification, see Verification Tool Guide
2. Add Localization Delegates
Update your MaterialApp or CupertinoApp to include the localization delegates:
import 'package:best_localization/best_localization.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
// Choose one of the following methods:
// Method 1: Using Loaders class (Recommended)
BestLocalizationDelegate.fromLoader(
Loaders.json(path: 'assets/translations.json'),
fallbackLocale: Locale('en'),
),
// Method 2: Using specific factory methods
// BestLocalizationDelegate.fromJson(
// JsonAssetLoader(path: 'assets/translations.json'),
// ),
// BestLocalizationDelegate.fromCsv(
// CsvAssetLoader(path: 'assets/translations.csv'),
// ),
// BestLocalizationDelegate.fromYaml(
// YamlAssetLoader(path: 'assets/translations.yaml'),
// ),
// BestLocalizationDelegate.fromXml(
// XmlAssetLoader(path: 'assets/translations.xml'),
// ),
// BestLocalizationDelegate.fromHttp(
// HttpLoader(url: 'https://api.example.com/translations'),
// ),
// Method 3: Using a map directly
// BestLocalizationDelegate.fromMap(
// translations,
// fallbackLocale: Locale('en'),
// ),
// Kurdish localizations
...kurdishLocalizations,
// Default Flutter localizations
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: const [
Locale('ku'), // Kurdish
Locale('en'), // English
Locale('ar'), // Arabic
],
locale: Locale('ku'),
home: MyHomePage(),
);
}
}
All available loader methods:
BestLocalizationDelegate.fromMap()- Direct mapBestLocalizationDelegate.fromJson()- JSON filesBestLocalizationDelegate.fromCsv()- CSV filesBestLocalizationDelegate.fromYaml()- YAML filesBestLocalizationDelegate.fromXml()- XML filesBestLocalizationDelegate.fromHttp()- Remote APIBestLocalizationDelegate.fromLoader()- Generic loader (with Loaders class)
3. Access Translations
Use the BestLocalization.of(context) method or the convenient extension methods:
Option A: Using Extension Methods (Recommended)
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Short and clean syntax
title: Text(context.translate('hello', args: {'name': 'John'})),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Translate text
Text(context.translate('welcome')),
// Check current language
if (context.isKurdish)
Text('Kurdish language detected!'),
// Get text direction automatically
Text(
context.translate('some_text'),
textDirection: context.textDirection,
),
// Access current language code
Text('Current language: ${context.languageCode}'),
],
),
),
);
}
}
Option B: Using Traditional Method
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final localizer = BestLocalization.of(context);
return Scaffold(
appBar: AppBar(
title: Text(localizer.translate('hello', args: {'name': 'John'})),
),
body: Center(
child: Text(localizer.translate('welcome')),
),
);
}
}
Available Extension Methods:
context.translate('key')- Translate a keycontext.translate('key', args: {...})- Translate with argumentscontext.localization- Get BestLocalization instancecontext.currentLocale- Get current localecontext.languageCode- Get language code ('en', 'ku', etc.)context.isKurdish- Check if current language is Kurdishcontext.isArabic- Check if current language is Arabiccontext.isEnglish- Check if current language is Englishcontext.isRTL- Check if current language is RTLcontext.textDirection- Get text direction
4. Fallback Locale (NEW! 🎉)
Use fallback locale to automatically use a default language when a translation is missing:
BestLocalizationDelegate.fromLoader(
Loaders.json(path: 'assets/translations.json'),
fallbackLocale: Locale('en'), // Falls back to English
)
Example:
{
"en": {
"hello": "Hello",
"new_feature": "New Feature"
},
"ku": {
"hello": "سڵاو"
// "new_feature" is missing
}
}
When Kurdish is selected:
context.translate('hello') // Returns "سڵاو" (from Kurdish)
context.translate('new_feature') // Returns "New Feature" (from fallback English)
📚 Learn more about Fallback Locale: Fallback Locale Guide
5. Remote Translations (NEW! 🎉)
Load translations from your server with automatic caching:
BestLocalizationDelegate.fromLoader(
Loaders.remote(
url: 'https://api.example.com/translations',
cacheEnabled: true,
cacheDuration: Duration(hours: 24),
headers: {'Authorization': 'Bearer token'},
),
fallbackLocale: Locale('en'),
)
Benefits:
- ✅ Update translations without app updates
- ✅ Automatic caching for offline support
- ✅ Custom cache duration
- ✅ Works with authentication
📚 Learn more about Remote Translations: Remote Translations Guide
6. Pluralization
Define keys for singular and plural forms in your translations:
final translations = {
'en': {
'items.one': 'One item',
'items.other': '{count} items',
},
'ku': {
'items.one': 'یەک دانە',
'items.other': '{count} دانە',
},
};
Access pluralized translations dynamically:
Text(localizer.translate('items', args: {'count': '2'})); // Output: 2 دانە
7. Translation Key Verification Tool (NEW! 🎉)
Verify your translation files to find missing keys, duplicate values, and inconsistencies across locales.
In Your Code:
import 'package:best_localization/best_localization.dart';
// Load your translations
final translations = {
'en': await JsonAssetLoader(path: 'assets/translations/en.json').load(),
'ku': await JsonAssetLoader(path: 'assets/translations/ku.json').load(),
'ar': await JsonAssetLoader(path: 'assets/translations/ar.json').load(),
};
// Verify all locales
final report = TranslationVerifier.verify(
translations: translations,
referenceLocale: 'en', // Optional: use English as reference
);
// Print report
print(report.generateReport());
// Get coverage percentage
print('Kurdish coverage: ${report.getCoverage('ku')}%');
// Export as JSON
final jsonReport = report.toJson();
Command-Line Tool:
# Activate the package globally (one-time setup)
flutter pub global activate best_localization
# Verify all translations in a directory
dart run best_localization:verify_translations verify assets/languages
# Verify with specific reference locale
dart run best_localization:verify_translations verify assets/languages --reference en
# Compare two translation files
dart run best_localization:verify_translations compare assets/languages/en.json assets/languages/ku.json
# Find duplicate values (same translation for different keys)
dart run best_localization:verify_translations duplicates assets/languages/en.json
# Find similar keys (potential typos)
dart run best_localization:verify_translations similar assets/languages/en.json --threshold 0.8
# Output as JSON for CI/CD integration
dart run best_localization:verify_translations verify assets/languages --json
Verification Report Example:
📋 Translation Verification Report
══════════════════════════════════════════════════
Reference Locale: en
Total Keys: 150
Locales: en, ku, ar
══════════════════════════════════════════════════
❌ Missing Keys:
ku: 5 missing
- new_feature
- settings.advanced
- error.network_timeout
⚠️ Empty Values:
ar: 2 empty
- placeholder_text
- coming_soon
══════════════════════════════════════════════════
Summary:
Missing: 5 keys
Extra: 0 keys
Empty: 2 keys
Use Cases:
- ✅ Pre-release verification: Check translations before publishing
- ✅ CI/CD integration: Add as automated test in your pipeline
- ✅ Translation audit: Find missing/duplicate translations
- ✅ Quality assurance: Ensure consistency across locales
- ✅ Typo detection: Find similar keys that might be duplicates
Available Verification Methods:
TranslationVerifier.verify()- Verify all locales against referenceTranslationVerifier.compareLocales()- Compare two specific localesTranslationVerifier.findDuplicateValues()- Find duplicate translationsTranslationVerifier.findSimilarKeys()- Find similar key names (potential typos)
8. Set Keys to Languages Other Than English
You can define your translation keys in languages other than English. For example:
final translations = {
'en': {
'سڵاو': 'Hello, {name}!', // Translation for "سڵاو" in English
'بەخێربێن': 'Welcome', // Translation for "بەخێربێن" in English
},
'ku': {
'سڵاو': 'سڵاو، {name}!', // Translation for "سڵاو" in Kurdish
'بەخێربێن': 'بەخێربێیت', // Translation for "بەخێربێن" in Kurdish
},
// Add more languages here...
};
About the developer
This package was developed by Dosty Pshtiwan, inspired by the flutter_kurdish_localization package created by Amin Samad. It includes Kurdish localization support for Flutter apps and builds upon their foundational work to provide a comprehensive localization solution.