best_localization
一個具有動態翻譯和庫爾德語支援的Flutter本地化套件。
一個輕量且彈性的 Flutter 國際化套件,提供動態翻譯、插入、複數形式,以及針對 Material 和 Cupertino 小部件的庫爾德語特定國際化。
{"sdk":"flutter"}{"sdk":"flutter"}>=0.19.0 <0.21.0>=3.0.0 <4.0.0>=6.0.0 <8.0.0>=0.13.0 <2.0.0>=2.0.0 <3.0.0{"sdk":"flutter"}^6.0.0以下為英文專案原文快照,最新內容請造訪 GitHub。
https://i.pinimg.com/1200x/e6/c2/fe/e6c2fe97ea37619eb784ddd48abdb522.jpg
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 Kurdish Sorani and Kurdish Kurmanji, including widgets like Material and Cupertino.
.tr() on both Strings and Text widgets without context!ckb) and Kurdish Kurmanji (ku) for Material and Cupertino widgets.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: ^2.0.3
2- Add flutter_localizations
Add the flutter_localizations package to your pubspec.yaml file:
dependencies:
flutter_localizations:
sdk: flutter
You can load translations in multiple ways:
Option A: From a Map (Direct)
final translations = {
'en': {
'hello': 'Hello, {name}!',
'welcome': 'welcome',
},
'ckb': {
'hello': 'سڵاو، {name}!',
'welcome': 'بەخێربێیت',
},
'ku': {
'hello': 'Silav, {name}!',
'welcome': 'Bi xêr hatî',
},
//more language...
};
Option B: From JSON File
// Create assets/translations/translations.json
{
"en": {
"hello": "Hello, {name}!",
"welcome": "Welcome"
},
"ckb": {
"hello": "سڵاو، {name}!",
"welcome": "بەخێربێیت"
},
"ku": {
"hello": "Silav, {name}!",
"welcome": "Bi xêr hatî"
}
}
Option C: From CSV File
key,en,ckb,ku
hello,Hello,سڵاو,Silav
welcome,Welcome,بەخێربێیت,Bi xêr hatî
Option D: From YAML File
en:
hello: Hello, {name}!
welcome: Welcome
ckb:
hello: سڵاو، {name}!
welcome: بەخێربێیت
ku:
hello: Silav, {name}!
welcome: Bi xêr hatî
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="ckb">
<string key="hello">سڵاو، {name}!</string>
<string key="welcome">بەخێربێیت</string>
</language>
<language code="ku">
<string key="hello">Silav, {name}!</string>
<string key="welcome">Bi xêr hatî</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
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('ckb'), // Kurdish Sorani
Locale('ku'), // Kurdish Kurmanji
Locale('en'), // English
Locale('ar'), // Arabic
],
locale: Locale('ckb'),
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)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.plural('key', count) - Translate with plural formcontext.localization - Get BestLocalization instancecontext.currentLocale - Get current localecontext.languageCode - Get language code ('en', 'ckb', 'ku', etc.)context.isKurdish - Check if current language is Kurdish (Sorani or Kurmanji)context.isSorani - Check if current language is Kurdish Sorani (ckb)context.isKurmanji - Check if current language is Kurdish Kurmanji (ku)context.isArabic - Check if current language is Arabiccontext.isEnglish - Check if current language is Englishcontext.isRTL - Check if current language is RTLcontext.textDirection - Get text directionTranslate strings and Text widgets easily without passing context!
For Strings:
// Simple translation - no context needed!
print('hello'.tr());
// With arguments
print('welcome'.tr(args: {'name': 'John'}));
// With gender
print('greeting'.tr(gender: 'male'));
// With custom locale
print('hello'.tr(locale: Locale('en')));
// With context (optional)
print('hello'.tr(context: context));
For Text Widgets:
// Simple translation - no context needed!
Text('hello').tr()
// With arguments
Text('welcome').tr(args: {'name': 'John'})
// With gender
Text('greeting').tr(gender: 'female')
// Plural form
Text('items').plural(5)
// Plural with arguments
Text('money').plural(10, args: {'name': 'Sarah'})
// Using translate() alias
Text('hello').translate() // Same as .tr()
Usage in Your Widget:
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// Direct translation - clean and simple!
Text('welcome'.tr()),
// With styling
Text('title').tr(args: {'name': 'User'}),
// Plural form
Text('items').plural(itemCount),
// Gender-specific
Text('greeting').tr(gender: userGender),
],
);
}
}
Handle plural forms with language-specific rules:
JSON Structure:
{
"day": {
"zero": "{} дней",
"one": "{} день",
"two": "{} дня",
"few": "{} дня",
"many": "{} дней",
"other": "{} дней"
},
"money": {
"zero": "You have no money",
"one": "You have {} dollar",
"many": "You have {} dollars",
"other": "You have {} dollars"
},
"money_named_args": {
"zero": "{name} has no money",
"one": "{name} has {} dollar",
"many": "{name} has {} dollars",
"other": "{name} has {} dollars"
}
}
Usage:
// String plural
'day'.plural(0) // "0 дней"
'day'.plural(1) // "1 день"
'day'.plural(5) // "5 дней"
// With named arguments
'money_named_args'.plural(5, args: {'name': 'John'}) // "John has 5 dollars"
'money_named_args'.plural(1, args: {'name': 'John'}) // "John has 1 dollar"
'money_named_args'.plural(0, args: {'name': 'John'}) // "John has no money"
// Text widget plural
Text('day').plural(itemCount)
Text('money_named_args').plural(balance, args: {'name': userName})
// With context (optional)
'day'.plural(5, context: context)
Supported Plural Forms:
zero - When count is 0one - When count is 1two - When count is 2few - Language-specific (e.g., 2-4 in Russian)many - Language-specific (e.g., 5+ in Russian)other - Default fallbackLanguage-Specific Rules:
Support for gender-specific text variations:
JSON Structure:
{
"greeting": {
"male": "Hi man ;) {}",
"female": "Hello girl :) {}",
"other": "Hello {}"
},
"welcome_user": {
"male": "Welcome Mr. {name}",
"female": "Welcome Ms. {name}",
"other": "Welcome {name}"
}
}
Usage:
// String gender translation
'greeting'.tr(gender: 'male') // "Hi man ;) "
'greeting'.tr(gender: 'female') // "Hello girl :) "
'greeting'.tr(gender: 'other') // "Hello "
// With arguments
'welcome_user'.tr(gender: 'female', args: {'name': 'Sarah'}) // "Welcome Ms. Sarah"
'welcome_user'.tr(gender: 'male', args: {'name': 'John'}) // "Welcome Mr. John"
// Text widget gender translation
Text('greeting').tr(gender: userGender)
Text('welcome_user').tr(gender: userGender, args: {'name': userName})
// With context (optional)
'greeting'.tr(gender: 'male', context: context)
Dynamic Gender Example:
class UserProfile extends StatelessWidget {
final String userName;
final String userGender; // 'male', 'female', or 'other'
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('greeting').tr(gender: userGender),
Text('profile_title').tr(
gender: userGender,
args: {'name': userName},
),
],
);
}
}
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"
},
"ckb": {
"hello": "سڵاو"
// "new_feature" is missing
}
}
When Kurdish Sorani is selected:
context.translate('hello') // Returns "سڵاو" (from Kurdish Sorani)
context.translate('new_feature') // Returns "New Feature" (from fallback English)
📚 Learn more about Fallback Locale: Fallback Locale Guide
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:
📚 Learn more about Remote Translations: Remote Translations Guide
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(),
'ckb': await JsonAssetLoader(path: 'assets/translations/ckb.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('Sorani coverage: ${report.getCoverage('ckb')}%');
print('Kurmanji 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/ckb.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, ckb, ku, ar
══════════════════════════════════════════════════
❌ Missing Keys:
ckb: 3 missing
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:
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)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
},
'ckb': {
'سڵاو': 'سڵاو، {name}!', // Translation for "سڵاو" in Kurdish Sorani
'بەخێربێن': 'بەخێربێیت', // Translation for "بەخێربێن" in Kurdish Sorani
},
'ku': {
'Silav': 'Silav, {name}!', // Translation for "Silav" in Kurdish Kurmanji
'Bi xêr hatî': 'Bi xêr hatî', // Translation for "Bi xêr hatî" in Kurdish Kurmanji
},
// Add more languages here...
};
This package was developed by Dosty Pshtiwan, inspired by the flutter_kurdish_localization package created by Amin Samad. It includes Kurdish Sorani (ckb) and Kurdish Kurmanji (ku) localization support for Flutter apps and builds upon their foundational work to provide a comprehensive localization solution.
| Variant | Locale code | Script | Direction |
|---|---|---|---|
| Kurdish Sorani | ckb |
Arabic | RTL |
| Kurdish Kurmanji | ku |
Latin | LTR |
Migration note: In older versions of this package, Kurdish Sorani used the
kucode. Sorani is nowckb, andkuis used for Kurmanji. Update your translation files andLocalevalues accordingly.