flutter_pipwave_localization 0.3.1
flutter_pipwave_localization: ^0.3.1 copied to clipboard
Powerful and flexible localization package for Pipwave Flutter projects, featuring ICU message formatting, runtime locale switching, asset and dynamic translation file support.
PW Localization Flutter #
A powerful and flexible localization package for Flutter applications, designed as a replacement for EasyLocalization with enhanced features including runtime ICU formatting support and dynamic language file downloading.
Features #
- 🚀 Easy Integration: Drop-in replacement for EasyLocalization with familiar API patterns
- 🌍 ICU Message Formatting: Full support for ICU message format including plurals and gender rules
- 📱 Dynamic Downloads: Download and update language files at runtime from remote servers
- 💾 Asset Fallback: Automatic fallback to asset-based translations when downloads are unavailable or when a locale is not found in downloaded files; empty translation maps fall through the lookup chain
- 🔄 Hot Reload: Support for runtime locale switching and translation updates
- 🏗️ Code Generation: Generate type-safe translation keys from JSON files
- 🎯 Flutter Integration: Seamless integration with Flutter's localization system
- 🗣️ 17 Built-in Delegates: Material, Cupertino, and Widget localization for Guarani, Hausa, Hawaiian, Igbo, Javanese, Latin, Maori, Nahuatl, Quechua, Shona, Somali, Swahili, Tajik, Turkmen, Xhosa, Yoruba, and Cantonese
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
flutter_pipwave_localization: ^0.3.1
Example #
A complete working example is available in the example/ directory. The example demonstrates:
- Custom setup with
PWLocalizationServiceandPWLocalizationDelegate - Using
context.tr()for simple translations,context.plural()andcontext.gender()for plurals and gender - Runtime language switching with
AnimatedBuilder - Dynamic download of language files from a remote URL
- BCP-47 locale support (e.g.,
en,id,zh-CN)
To run the example:
cd example
flutter pub get
flutter run
Quick Start #
1. Basic Setup #
Wrap your app with PWLocalizationProvider:
import 'package:flutter/material.dart';
import 'package:flutter_pipwave_localization/flutter_pipwave_localization.dart';
void main() {
runApp(
PWLocalizationProvider(
startLocale: const Locale('en'),
assetPath: 'assets/translations',
downloadedPath: 'translations',
supportedLocales: const [
Locale('en'),
Locale('es'),
Locale('fr'),
],
child: const MyApp(),
),
);
}
2. Using Translations #
Access translations through the context extension:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(context.tr('welcome.title')),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(context.tr('welcome.message', args: {'name': 'John'})),
Text(context.plural('items.countInObject', 5)),
],
),
),
);
}
}
3. Using Translation Keys (Optional) #
After running the code generator, use type-safe constants:
// Generated keys (names depend on your JSON structure)
Text(LocaleKeys.welcomeTitle.tr());
Text(LocaleKeys.itemsCount.trPlural(5));
API Reference #
PWLocalizationProvider #
The main widget that provides localization to your app. Wraps your app and initializes [PWLocalizationService].
PWLocalizationProvider({
required Widget child,
required Locale startLocale,
required String assetPath,
required String downloadedPath,
List<Locale> supportedLocales = const [Locale('en')],
Widget? loadingWidget,
bool isNewInstance = false,
Key? key,
});
Parameters:
child: The widget tree to provide localization forstartLocale: The initial locale to useassetPath: Path to asset-based translation files (e.g., 'assets/translations')downloadedPath: Directory name for downloaded translation filessupportedLocales: List of supported localesloadingWidget: Optional widget to show while the initial locale is loadingisNewInstance: If true, creates a new service instance instead of using the singleton
PWLocalizedMaterialApp #
A convenience widget combining [MaterialApp] with localization. Use when you need both Material Design and localization in a single widget.
PWLocalizedMaterialApp(
home: const MyHomePage(),
startLocale: const Locale('en'),
assetPath: 'assets/translations',
downloadedPath: 'translations',
supportedLocales: const [Locale('en'), Locale('es')],
theme: ThemeData.light(),
);
PWLocalizationService #
The core service that handles translations and ICU formatting. Accessed via PWLocalizationService.instance.
Key Methods
// Initialize the service
await PWLocalizationService.instance.initialize(
assetPath: 'assets/translations',
downloadedPath: 'translations',
defaultLanguage: 'en',
locale: const Locale('en'),
);
// Translate a key (supports map, list, or named args)
String translated = PWLocalizationService.instance.tr(
'welcome.message',
args: {'name': 'John'},
);
// For plurals, use plural()
String plural = PWLocalizationService.instance.plural(
'newMessages',
5,
);
// Check if locale is supported
bool isSupported = PWLocalizationService.instance.isLocaleSupported('en');
// Download language files (headers and query params optional)
await PWLocalizationService.instance.downloadLanguageFiles(
'https://api.example.com/translations',
['en', 'es', 'fr'],
headers: {'Authorization': 'Bearer token'},
queryParameters: {'version': '1.0'},
);
// Alternative: download by locale tags or Locale objects
await PWLocalizationService.instance.downloadLanguageFilesForTags(
baseUrl, ['en', 'zh-CN'],
);
await PWLocalizationService.instance.downloadLanguageFilesForLocales(
baseUrl, [const Locale('en'), const Locale('zh', 'CN')],
);
// Clear downloaded translations
await PWLocalizationService.instance.clearDownloadedTranslations();
Locale Helpers
// Convert between Locale and BCP-47 tags
String tag = PWLocalizationService.localeToTag(const Locale('zh', 'CN')); // 'zh-CN'
Locale locale = PWLocalizationService.localeFromTag('zh-CN');
PWLocalizationDelegate #
Integrates with Flutter's localizationsDelegates. Requires either an already-initialized service or configuration for initialization.
PWLocalizationDelegate(
instance: PWLocalizationService.instance,
locale: locale,
supportedLocales: supportedLocales,
assetPath: 'assets/translations',
downloadedPath: 'translations',
defaultLocaleTag: 'en',
);
Helper to get configured delegates:
PWLocalizationDelegate.getConfiguredDelegates(
instance: PWLocalizationService.instance,
locale: locale,
supportedLocales: supportedLocales,
assetPath: 'assets/translations',
downloadedPath: 'translations',
defaultLocaleTag: 'en',
);
Context Extensions #
Convenient extensions for accessing localization in widgets:
// Simple translation with arguments
String text = context.tr('welcome.message', args: {'name': 'John'});
// Plural forms (object-based: zero, one, many, other)
String plural = context.plural('items.countInObject', 5);
// Gender forms (object-based: male, female, other)
String welcome = context.gender('profileWelcome', 'male', namedArgs: {'name': 'John'});
// Get current locale
Locale? locale = context.currentLocale;
String Extensions #
Extensions for direct string translation:
// Simple translation
String text = 'welcome.message'.tr();
// Translation with arguments
String text = 'welcome.message'.tr(args: {'name': 'John'});
String text = 'welcome.message'.trArgs({'name': 'John'});
// Plural and gender (for object-based translation keys)
String text = 'items.countInObject'.trPlural(5);
String text = 'profileWelcome'.trGender('male', namedArgs: {'name': 'John'});
ICU Message Format Support #
This package fully supports ICU message formatting for advanced pluralization and gender rules. Use dedicated methods:
- Object-based plurals (zero, one, many, other):
context.plural(key, count)orservice.plural(key, count) - Object-based gender (male, female, other):
context.gender(key, gender)orservice.gender(key, gender) - ICU inline format (e.g.
{count, plural, ...}in string):context.tr(key, args: {...})
Plural Rules #
Object-Based Plural Format
Object-based plural definitions use plural():
{
"items_count": {
"zero": "No items",
"one": "One item",
"many": "{count} items",
"other": "{count} items"
}
}
Usage:
Text(context.plural('items_count', 5));
ICU Message Format Plural
For ICU plural syntax embedded directly in string values:
{
"items_count": "{count, plural, =0{No items} =1{One item} =2{Two items} few{Few items} many{Many items} other{{count} items}}"
}
Usage:
Text(context.tr('items_count', args: {'count': 5}));
The ICU format supports:
- Exact matches:
=0,=1,=2, etc. - Plural categories:
zero,one,two,few,many,other - Variable interpolation:
{count}within messages
Gender Rules #
{
"welcome": {
"male": "Welcome Mr. {name}",
"female": "Welcome Ms. {name}",
"other": "Welcome {name}"
}
}
Usage:
Text(context.gender('welcome', 'male', namedArgs: {'name': 'John'}));
Complex ICU Formatting #
{
"message": "You have {count, plural, zero{no messages} one{one message} many{# messages}} from {sender}."
}
Usage:
Text(context.tr('message', args: {
'count': 5,
'sender': 'Alice'
}));
Code Generation #
Generate type-safe translation keys from your JSON files:
dart run flutter_pipwave_localization:generate assets/translations/en.json lib/src/generated/locale_keys.g.dart
The arguments are optional and default to:
- Translation file:
assets/translations/en.json - Output path:
lib/src/generated/locale_keys.g.dart
You can also run without arguments to use the defaults:
dart run flutter_pipwave_localization:generate
This generates a LocaleKeys class with static constants for all translation keys.
Built-in Localization Delegates #
The package includes Material, Cupertino, and Widget localization delegates for 17 languages. These provide localized labels for Flutter's built-in widgets (date pickers, time pickers, buttons, dialogs, reorder semantics, etc.).
Supported Languages #
| Code | Language | Code | Language |
|---|---|---|---|
| gn | Guarani | sn | Shona |
| ha | Hausa | so | Somali |
| haw | Hawaiian | sw | Swahili |
| ig | Igbo | tg | Tajik |
| jv | Javanese | tk | Turkmen |
| la | Latin | xh | Xhosa |
| mi | Maori | yo | Yoruba |
| nah | Nahuatl | yue | Cantonese |
| qu | Quechua |
Plus Flutter's built-in locales (en, es, fr, zh, etc.) for Material, Cupertino, and Widgets.
Implementation #
These delegates are automatically included when you use PWLocalizationProvider or PWLocalizationDelegate.getConfiguredDelegates(). You only need to add your desired locales to supportedLocales:
PWLocalizationProvider(
startLocale: const Locale('ha'),
supportedLocales: const [
Locale('en'),
Locale('ha'), // Hausa - Material/Cupertino/Widget labels
Locale('yo'), // Yoruba
Locale('sw'), // Swahili
],
assetPath: 'assets/translations',
downloadedPath: 'translations',
child: const MyApp(),
);
When the app locale matches one of the built-in delegates (e.g. ha for Hausa), date pickers, buttons, and other Flutter widgets will display labels in that language.
Adding New Delegates #
To add a new language delegate, use the delegate generator tool:
dart tool/create_delegate.dart tool/sample_delegate.json
Provide a JSON file following the structure in tool/sample_delegate.json. The script generates all delegate files and automatically updates lib/src/delegates/delegates.dart. See tool/README.md for the JSON format and available keys.
Advanced Usage #
Custom Localization Setup #
For more control (e.g., manual locale switching without PWLocalizationProvider), use the service and delegate directly:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final localizationService = PWLocalizationService.instance;
return AnimatedBuilder(
animation: localizationService,
builder: (context, _) {
final locale = localizationService.currentLocale ?? const Locale('en');
const supported = [Locale('en'), Locale('es'), Locale('zh', 'CN')];
return MaterialApp(
home: const HomePage(),
localizationsDelegates: PWLocalizationDelegate.getConfiguredDelegates(
instance: localizationService,
locale: locale,
supportedLocales: supported,
assetPath: 'assets/translations',
downloadedPath: 'translations',
defaultLocaleTag: 'en',
),
supportedLocales: supported,
locale: locale,
);
},
);
}
}
Dynamic Locale Switching #
// Access the localization service (works with both PWLocalizationProvider and custom setup)
final localization = PWLocalizationProvider.of(context);
// Optionally download new language files first
await PWLocalizationService.instance.downloadLanguageFiles(
'https://api.example.com/translations',
['es'],
);
// Switch locale (re-initializes with new locale)
await localization.initialize(
assetPath: 'assets/translations',
downloadedPath: 'translations',
defaultLanguage: 'en', // BCP-47 tag, lowercased internally
locale: const Locale('es'),
);
When using AnimatedBuilder with the service, locale changes will trigger a rebuild and the delegate will load the new locale.
BCP-47 Locale Tags (e.g. zh-CN) #
This package stores and looks up translations by lowercased BCP-47 locale tags
such as en, id, zh-CN (not by languageCode only).
- Assets/downloads are resolved as
{localeTag}.json(e.g.assets/translations/zh-CN.json) - Backward compatibility: if
{localeTag}.jsonis missing, it may fall back to{languageCode}.json(e.g.zh.json) for legacy projects.
Example download:
await PWLocalizationService.instance.downloadLanguageFiles(
'https://api.example.com/translations',
['en', 'es', 'fr', 'zh-CN'],
);
Error Handling #
try {
await PWLocalizationService.instance.initialize(
assetPath: 'assets/translations',
downloadedPath: 'translations',
defaultLanguage: 'en',
locale: const Locale('en'),
);
} catch (e) {
// Handle initialization errors
print('Localization initialization failed: $e');
}
Translation File Structure #
Your translation files should be JSON files with the following structure:
{
"welcome": {
"title": "Welcome",
"message": "Hello {name}!",
"items": {
"count": {
"zero": "No items",
"one": "One item",
"many": "{count} items",
"other": "{count} items"
}
}
},
"items_count_icu": "{count, plural, =0{No items} =1{One item} =2{Two items} few{Few items} many{Many items} other{{count} items}}",
"common": {
"ok": "OK",
"cancel": "Cancel",
"loading": "Loading..."
}
}
You can use either:
- Object-based plurals: Nested objects with
zero,one,many,other, etc. - ICU message format: Direct string values with ICU plural syntax
Best Practices #
- Key Naming: Use descriptive, hierarchical keys (e.g.,
welcome.titleinstead ofwelcomeTitle) - Fallback Strategy: Always provide fallback translations in your default language
- ICU Formatting: Use ICU message format for complex pluralization and gender rules
- Asset Organization: Keep translation files organized in
assets/translations/directory - Error Handling: Implement proper error handling for network failures when downloading translations
License #
This project is licensed under the MIT License - see the LICENSE file for details.