anas_localization
In Memory of Anas Al-Sharif - A Palestinian journalist who gave his life reporting truth. This package serves as a Sadaqah Jariyah (ongoing charity) in his honor.
A comprehensive Flutter/Dart localization solution with type-safe translations, advanced pluralization, runtime flexibility, and powerful CLI tools.
Platform Support
| Android | iOS | Web | macOS | Windows | Linux |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Features
- Type-safe generated Dictionary with compile-time validation
- Runtime key lookup for fast iteration without code generation
- Dual access modes - use both typed and runtime access simultaneously
- Zero-configuration setup with automatic dictionary detection
- Deterministic locale fallback chain with script/region handling
- Advanced pluralization including Arabic gender-aware forms
- Built-in RTL support with automatic text direction
- CLI tools for validation, ARB/CSV/JSON import/export, and statistics
- Catalog UI - Swift String Catalog-style translation editor
- Regional English support -
en_US,en_GB,en_CA,en_AUoverlays - Date/time and number formatting with locale-specific patterns
- Rich text support with markdown-like formatting
- Migration guides from
gen_l10nandeasy_localization
Quick Start
Installation
Add to your pubspec.yaml:
dependencies:
anas_localization: ^0.1.0
Run:
flutter pub get
1. Create Translation Files
Create JSON files in assets/lang/:
assets/lang/en.json
{
"app_name": "My App",
"welcome_user": "Welcome, {name}!",
"items_count": {
"one": "{count} item",
"other": "{count} items"
}
}
assets/lang/ar.json
{
"app_name": "تطبيقي",
"welcome_user": "مرحباً، {name}!",
"items_count": {
"one": "{count} عنصر",
"other": "{count} عناصر"
}
}
Update pubspec.yaml:
flutter:
assets:
- assets/lang/
2. Generate Dictionary
Run the code generator:
dart run anas_localization:anas update --gen
Or use watch mode for live updates:
dart run anas_localization:anas update --gen --watch
This creates lib/generated/dictionary.dart with type-safe accessors.
3. Configure Your App
lib/main.dart
import 'package:flutter/material.dart';
import 'package:anas_localization/anas_localization.dart';
import 'generated/dictionary.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const AnasLocalization(
fallbackLocale: Locale('en'),
assetPath: 'assets/lang',
assetLocales: [
Locale('en'),
Locale('ar'),
],
app: MainApp(),
);
}
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
final locale = AnasLocalization.of(context).locale;
return MaterialApp(
locale: locale,
builder: (context, child) => AnasDirectionalityWrapper(
locale: locale,
child: child!,
),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
const DictionaryLocalizationsDelegate(),
],
supportedLocales: context.supportedLocales,
home: const HomePage(),
);
}
}
4. Use Translations
Access translations with type-safe getters:
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final dictionary = getDictionary();
return Scaffold(
appBar: AppBar(
title: Text(dictionary.appName),
),
body: Column(
children: [
// Simple translation
Text(dictionary.appName),
// With parameters
Text(dictionary.welcomeUser(name: 'Ahmed')),
// Pluralization
Text(dictionary.itemsCount(count: 1)), // "1 item"
Text(dictionary.itemsCount(count: 5)), // "5 items"
// Language selector widget
AnasLanguageSelector(
supportedLocales: context.supportedLocales,
),
],
),
);
}
}
Runtime Lookup (No Generation)
For fast iteration during development, you can skip code generation:
// Use getString for runtime key lookup
Text(dictionary.getString('app_name'))
Text(dictionary.getStringWithParams('welcome_user', {'name': 'Ahmed'}))
See Runtime Lookup Guide for details.
Language Switching
Built-in language switching with smooth animations:
ElevatedButton(
onPressed: () {
AnasLocalization.of(context).setLocale(const Locale('ar'));
},
child: const Text('العربية'),
)
// Or use pre-built widgets
AnasLanguageDialog(
supportedLocales: context.supportedLocales,
showDescription: true,
)
Advanced Features
Arabic Gender-Aware Pluralization
{
"car": {
"one": {"male": "سيارة واحدة", "female": "سيارة واحدة"},
"two": {"male": "سيارتان", "female": "سيارتان"},
"few": {"male": "{count} سيارات", "female": "{count} سيارات"},
"many": {"male": "{count} سيارة", "female": "{count} سيارة"}
}
}
dictionary.car(count: 5, gender: 'male') // "5 سيارات"
CLI Tools
# Validate translations
anas validate assets/lang --profile=strict
# Import/Export ARB files
anas export assets/lang arb lib/l10n
anas import l10n.yaml assets/lang
# Translation statistics
anas stats assets/lang
# Catalog UI for visual editing
anas catalog --init
anas catalog --serve
Remote Localization (Optional)
Pull translations from a remote backend at runtime, in addition to local assets.
// 1. Implement a connector
class MyRemoteConnector implements RemoteLocalizationConnector {
@override
bool get supportsGlobalCheck => true;
@override
bool get supportsLocaleCheck => true;
@override
Future<RemoteCheckResponse> checkForUpdates(cachedVersions) async { /* ... */ }
@override
Future<RemoteCheckResponse> checkForLocaleUpdate(locale, cachedVersion) async { /* ... */ }
@override
Future<RemoteLocalizationPayload> downloadPayload(update) async { /* ... */ }
}
// 2. Configure
await AnasLocalization.initialize(
fallbackLocale: const Locale('en'),
supportedLocales: const [Locale('en'), Locale('ar')],
remote: RemoteLocalizationConfig(
connector: MyRemoteConnector(),
checkOnStartup: true,
),
);
// 3. Check for updates manually
final result = await AnasLocalization.remote.checkForUpdates();
Protected app keys and merge policy: package < app < remote cache. Mark entries in app assets with {"value": "text", "__override__": false} to prevent remote replacement.
Migration Support
Migrate from existing solutions:
# From gen_l10n
anas convert --from gen_l10n --source l10n.yaml --out assets/lang
# From easy_localization
anas convert --from easy_localization
# Validate migration
anas validate-migration --from gen_l10n
Documentation
- Getting Started: Installation & Setup
- Full Setup Guide: doc/SETUP_AND_USAGE.md
- Runtime Lookup: doc/RUNTIME_LOOKUP_WITHOUT_GENERATION.md
- Catalog UI: doc/CATALOG_UI.md
- CLI Reference: doc/reference/cli-reference.md
- Migration Guides:
- Cookbook: https://melsaeed276.github.io/anas_localization/
Why anas_localization?
| Feature | Flutter gen_l10n |
easy_localization |
slang |
anas_localization |
|---|---|---|---|---|
| Type-safe accessors | ✅ | ⚠️ | ✅ | ✅ |
| Runtime flexibility | ⚠️ | ✅ | ✅ | ✅ |
| ARB import/export | ✅ | ⚠️ | ✅ | ✅ |
| CLI validation | ❌ | ⚠️ | ✅ | ✅ |
| Module namespaces | ❌ | ❌ | ✅ | ✅ |
| Migration tools | ❌ | ⚠️ | ⚠️ | ✅ |
| Custom UI | ❌ | ❌ | ❌ | ✅ |
anas_localization prioritizes migration tooling, runtime flexibility, and CI-friendly validation workflows. See detailed comparison.
Example
See the example directory for a complete working app demonstrating all features.
cd example
flutter pub get
flutter run
Contributing
Contributions are welcome! See CONTRIBUTING.md for guidelines.
Security
Report security issues privately. See SECURITY.md for details.
License
Apache License 2.0 - See LICENSE for details.
Arabic translation (unofficial): LICENSE.ar.md
Trademark
The name "anas_localization" honors Anas Al-Sharif's legacy. Modified versions should use different names. See TRADEMARK.md.
Remember: This work is a Sadaqah Jariyah for Anas Al-Sharif, whose courage in journalism continues to inspire.
Libraries
- anas_localization
- Main entry point for the localization package.
- catalog
- localization
- Backwards-compatible entrypoint.