language_helper 0.13.0-rc.4
language_helper: ^0.13.0-rc.4 copied to clipboard
A Flutter package for easy multi-language app localization with automatic text extraction and translation support.
Language Helper #
A Flutter package for easy multi-language app localization with automatic text extraction and translation support.
Features #
- 🚀 Easy Setup: Add
.trand.trPto any string for instant translation - 🔍 Auto Extraction: Automatically extract all translatable text from your Dart files
- 🎯 Smart Translation: Control translations with conditions and parameters
- 🌐 Multiple Sources: Support for Dart maps, JSON files, assets, and network data
- 📱 Device Locale: Automatically uses device locale on first launch
- 🔧 AI Integration: Custom translator for easy language conversion
Quick Start #
1. Add to your project #
flutter pub add language_helper
2. Initialize (while developing) #
final languageHelper = LanguageHelper.instance;
main() async {
WidgetsFlutterBinding.ensureInitialized();
await languageHelper.initial(data: []);
runApp(const MyApp());
}
3. Add translations to your strings #
// Simple translation
Text('Hello World'.tr)
// With parameters
Text('Hello @{name}'.trP({'name': 'John'}))
// With conditions
Text('You have @{count} item'.trP({'count': itemCount}))
4. Wrap your app #
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LanguageBuilder(
builder: (context) {
return MaterialApp(
localizationsDelegates: languageHelper.delegates,
supportedLocales: languageHelper.locales,
locale: languageHelper.locale,
home: const HomePage(),
);
}
);
}
}
Generate Translations #
The generator automatically scans your project for text using language_helper extensions (tr, trP, trT, trF) and translate method, then creates organized translation files with your existing translations preserved.
Note: The generator is smart about managing translations:
- ✅ Keeps existing translations - Your current translated texts are preserved
- 🆕 Marks new texts with TODO - Only untranslated texts get TODO markers
- 🗑️ Removes unused texts - Automatically cleans up translations no longer used in your code
Add Generator Dependency #
First, add the generator to your pubspec.yaml:
dev_dependencies:
language_helper_generator: ^0.7.0
Then run:
flutter pub get
Basic Generation #
Extract all translatable text and generate language files:
dart run language_helper:generate --languages=en,vi,fr --ignore-todo=en
This creates:
lib/languages/
├── codes.dart
└── data/
├── en.dart // without TODO markers for missing translations
├── vi.dart // with TODO markers for missing translations
└── fr.dart // with TODO markers for missing translations
JSON Generation #
For assets or network-based translations:
dart run language_helper:generate --languages=en,vi,fr --json
Creates:
assets/languages/
├── codes.json
└── data/
├── en.json
├── vi.json
└── fr.json
JSON files do not support TODO markers. To identify untranslated or new strings, look for entries where the key and value are identical.
Generator Options #
| Option | Description | Example |
|---|---|---|
--languages |
Language codes to generate | --languages=en,vi,es |
--ignore-todo |
Skip TODO markers for specific languages | --ignore-todo=en |
--path |
Custom output directory | --path=./lib/languages |
--json |
Generate JSON files instead of Dart | --json |
Common Examples #
Skip TODOs in English (your base language):
dart run language_helper:generate --languages=en,vi --ignore-todo=en
Custom output path:
dart run language_helper:generate --path=./lib/languages --languages=en,vi
Generate for multiple languages:
dart run language_helper:generate --languages=en,vi,es,fr --ignore-todo=en
Using Generated Data #
Dart Map #
final languageDataProvider = LanguageDataProvider.lazyData(languageData);
main() async {
await languageHelper.initial(data: [languageDataProvider]);
runApp(const MyApp());
}
JSON Assets #
final languageDataProvider = LanguageDataProvider.asset('assets/languages');
main() async {
await languageHelper.initial(data: [languageDataProvider]);
runApp(const MyApp());
}
Network Data #
final languageDataProvider = LanguageDataProvider.network('https://api.example.com/translations');
main() async {
await languageHelper.initial(data: [languageDataProvider]);
runApp(const MyApp());
}
Manual Translation Setup #
Dart Map Example #
final en = {
'Hello World': 'Hello World',
'Hello @{name}': 'Hello @{name}',
'You have @{count} item': LanguageConditions(
param: 'count',
conditions: {
'1': 'You have one item',
'_': 'You have @{count} items', // Default
}
),
};
final vi = {
'Hello World': 'Xin chào thế giới',
'Hello @{name}': 'Xin chào @{name}',
'You have @{count} item': 'Bạn có @{count} mục',
};
LazyLanguageData languageData = {
LanguageCodes.en: () => en,
LanguageCodes.vi: () => vi,
};
JSON Example #
assets/languages/codes.json:
["en", "vi"]
assets/languages/data/en.json:
{
"Hello World": "Hello World",
"Hello @{name}": "Hello @{name}",
"You have @{count} item": {
"param": "count",
"conditions": {
"1": "You have one item",
"_": "You have @{count} items"
}
}
}
Don't forget to add to pubspec.yaml:
flutter:
assets:
- assets/languages/codes.json
- assets/languages/data/
Language Control #
Change Language #
languageHelper.change(LanguageCodes.vi);
Add New Language Data #
languageHelper.addData(LanguageDataProvider.lazyData(newLanguageData));
Listen to Changes #
final sub = languageHelper.stream.listen((code) => print('Language changed to: $code'));
// Remember to cancel: sub.cancel()
Get Supported Languages #
final codes = languageHelper.codes; // All supported languages
final overrides = languageHelper.codesOverrides; // Override languages
Advanced Usage #
Generator Features #
- Fast: Uses Dart Analyzer, no build_runner dependency
- Smart: Preserves existing translations
- Organized: Groups translations by file path
- Helpful: Adds TODO markers for missing translations
- Clean: Removes unused translation keys automatically
Custom Paths #
# Custom output path
dart run language_helper:generate --path=./lib/languages --languages=en,vi
# Generate JSON to assets folder
dart run language_helper:generate --path=./assets/languages --languages=en,vi --json
Multiple Data Sources #
main() async {
await languageHelper.initial(
data: [
// Assume that our `code.json` in `https://api.example.com/translations/code.json`
// So our data will be in `https://api.example.com/translations/data/en.json`
LanguageDataProvider.network('https://api.example.com/translations'),
// Assume that our `code.json` in `assets/languages/code.json`
// So our data will be in `assets/languages/en.json`
LanguageDataProvider.asset('assets/languages'),
LanguageDataProvider.lazyData(localLanguageData),
],
);
runApp(const MyApp());
}
Data Priority: When multiple sources contain the same translation:
- First source wins - Data sources are processed in order (top to bottom) for the entire source
- Specific overrides - To override individual translations, use
dataOverridesinstead of adding todata- AddData behavior - New data can overwrite existing translations (controlled by
overwriteparameter)
Widget Rebuilding #
LanguageBuilder(
builder: (context) => Text('Hello'.tr),
)
Tr((_) => Text('Hello'.tr))
Force Rebuild and Tree Refresh #
forceRebuild Parameter
By default, only the root LanguageBuilder widget rebuilds when the language changes for better performance. Use forceRebuild: true to force a specific widget to always rebuild:
LanguageBuilder(
forceRebuild: true, // This widget will always rebuild on language change
builder: (context) => Text('Hello'.tr),
)
true→ Always rebuild this widget when language changesfalse→ Only rebuild the root widget (default behavior)null→ Fallback toLanguageHelper.forceRebuilddefault
refreshTree Parameter
Use refreshTree: true to completely refresh the widget tree using KeyedSubtree. This changes the key of the current tree so the entire tree is removed and recreated:
LanguageBuilder(
refreshTree: true, // Uses KeyedSubtree to refresh entire tree
builder: (context) => MyComplexWidget(),
)
⚠️ Performance Warning:
refreshTreecauses the entire widget tree to be destroyed and recreated, which can be expensive for complex widgets. This may lead to:
- Loss of widget state and animations
- Poor performance with large widget trees
- Unnecessary rebuilds of child widgets
💡 Note: If you use
constwidgets nested inside aLanguageBuilder, they may not rebuild automatically when the root rebuilds. To ensure these widgets update on language change (without usingrefreshTree), wrap them in their ownLanguageBuilderwithforceRebuild: true.
Use refreshTree only when you specifically need to reset widget state or when dealing with widgets that don't properly handle language changes.
AI Translator #
Use the Language Helper Translator in Chat-GPT for easy translation:
This is the translation of my Flutter app. Translate it into Spanish:
final en = {
'Hello @{name}': 'Hello @{name}',
'Welcome to the app': 'Welcome to the app',
};
Or using AI instruction
### Step-by-Step Instructions for Translation
1. Identify the Dart `Map<String, dynamic>` structure and focus only on translating the values, not the keys or structure.
2. Analyze the entire input first to understand its context for the best translation results.
3. Check for plural forms and, if present, restructure using `LanguageConditions`.
4. Translate plural forms: 0 → '0 products', 1 → '1 product', other → '@{count} products'.
5. Translate only the values, leaving keys and structure unchanged.
6. Preserve all comments (`//` and `///`), leaving them untranslated.
7. Do not translate nested comments.
8. Ensure the map structure is maintained after translation, with correct handling of plural forms and comments.
### Example for Plural Grammar Handling
If input is:
```dart
{
'@{count} sản phẩm': '@{count} sản phẩm',
}
```
And the destination language is `en`. It should generate to:
```dart
{
'@{count} sản phẩm': LanguageConditions(
param: 'count',
conditions: {
'0': '0 products',
'1': '1 product',
'_': '@{count} products',
},
),
}
```
### Important Reminders
- Translate only values, never keys.
- Leave comments unchanged.
- Handle plural forms with `LanguageConditions` as needed.
iOS Configuration #
Add supported localizations to Info.plist:
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>vi</string>
</array>
Tips #
- Use
@{param}for parameters (recommended) - The package automatically uses device locale on first launch
- Only the outermost
LanguageBuilderrebuilds for better performance - Use
isInitializedto check ifinitial()has been called - Assets are preferred over network data (no caching yet)
Contributing #
Found a bug or want to contribute? Please file an issue or submit a pull request!
License #
This project is licensed under the MIT License.