Flutter Translations: Complete Guide to Managing Multi-Language Apps
Master Flutter translations from setup to production. Learn the complete workflow for managing translations, organizing ARB files, and scaling your localization across dozens of languages.
Understanding Flutter Translations
Flutter translations allow your app to display text in the user's preferred language. The official approach uses ARB (Application Resource Bundle) files with the flutter_localizations package.
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: any
flutter:
generate: true
Setting Up Your Translation Workflow
Step 1: Configure l10n.yaml
Create an l10n.yaml file in your project root:
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations
nullable-getter: false
Step 2: Create Your Template ARB File
The template file defines all translatable strings:
{
"@@locale": "en",
"appTitle": "My Flutter App",
"@appTitle": {
"description": "The title of the application"
},
"welcomeMessage": "Welcome to our app!",
"@welcomeMessage": {
"description": "Greeting shown on the home screen"
},
"itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
"@itemCount": {
"description": "Shows number of items in cart",
"placeholders": {
"count": {
"type": "int",
"example": "5"
}
}
}
}
Step 3: Add Translation Files
Create additional ARB files for each language:
lib/l10n/app_es.arb (Spanish):
{
"@@locale": "es",
"appTitle": "Mi Aplicación Flutter",
"welcomeMessage": "¡Bienvenido a nuestra aplicación!",
"itemCount": "{count, plural, =0{Sin artículos} =1{1 artículo} other{{count} artículos}}"
}
lib/l10n/app_de.arb (German):
{
"@@locale": "de",
"appTitle": "Meine Flutter App",
"welcomeMessage": "Willkommen in unserer App!",
"itemCount": "{count, plural, =0{Keine Artikel} =1{1 Artikel} other{{count} Artikel}}"
}
Step 4: Configure MaterialApp
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: HomePage(),
);
}
}
Step 5: Use Translations
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(title: Text(l10n.appTitle)),
body: Column(
children: [
Text(l10n.welcomeMessage),
Text(l10n.itemCount(5)),
],
),
);
}
}
Organizing Translations at Scale
File Structure for Large Projects
lib/
├── l10n/
│ ├── app_en.arb # English (template)
│ ├── app_es.arb # Spanish
│ ├── app_de.arb # German
│ ├── app_fr.arb # French
│ ├── app_ar.arb # Arabic
│ ├── app_ja.arb # Japanese
│ ├── app_zh.arb # Chinese (Simplified)
│ └── app_pt.arb # Portuguese
Categorizing Translation Keys
Use consistent naming conventions to organize your translations:
{
"@@locale": "en",
"// --- Common ---": "",
"common_ok": "OK",
"common_cancel": "Cancel",
"common_save": "Save",
"common_delete": "Delete",
"common_loading": "Loading...",
"// --- Auth ---": "",
"auth_login": "Log In",
"auth_logout": "Log Out",
"auth_signup": "Sign Up",
"auth_forgotPassword": "Forgot Password?",
"// --- Home ---": "",
"home_title": "Home",
"home_welcomeBack": "Welcome back, {name}!",
"// --- Settings ---": "",
"settings_title": "Settings",
"settings_language": "Language",
"settings_notifications": "Notifications"
}
Namespace Pattern
For very large apps, consider a namespace pattern:
{
"@@locale": "en",
"nav.home": "Home",
"nav.profile": "Profile",
"nav.settings": "Settings",
"auth.login.title": "Welcome Back",
"auth.login.email": "Email Address",
"auth.login.password": "Password",
"auth.login.submit": "Sign In",
"profile.edit.title": "Edit Profile",
"profile.edit.name": "Display Name",
"profile.edit.bio": "Bio"
}
Managing Translation Quality
Adding Context for Translators
Always include descriptions and examples:
{
"greeting": "Hey {name}!",
"@greeting": {
"description": "Informal greeting shown to returning users on the home screen",
"placeholders": {
"name": {
"type": "String",
"example": "Sarah"
}
}
},
"orderStatus": "Your order is {status}",
"@orderStatus": {
"description": "Status can be: Processing, Shipped, Delivered, Cancelled",
"placeholders": {
"status": {
"type": "String",
"example": "Shipped"
}
}
}
}
Character Limits
Specify limits for UI constraints:
{
"buttonText": "Submit",
"@buttonText": {
"description": "Primary action button. MAX 10 CHARACTERS to fit button width"
},
"tabLabel": "Home",
"@tabLabel": {
"description": "Bottom navigation tab. MAX 6 CHARACTERS"
}
}
Handling Untranslatable Content
Some content should not be translated:
// Don't put these in ARB files:
// - Brand names: "Flutter", "Google"
// - Technical terms that shouldn't change
// - User-generated content
// - Dynamic server content
// Keep in ARB:
// - UI labels and buttons
// - Error messages
// - Instructional text
// - Static content
Translation Workflow Strategies
Strategy 1: Developer-First
- Developers add English strings as they code
- Run
flutter gen-l10nto generate code - Export ARB files for translation
- Import translated files back
- Review and merge
Strategy 2: Design-First
- Copy/UX team defines all strings upfront
- Create master translation sheet
- Developers reference the sheet
- Translations happen in parallel
- All languages ship together
Strategy 3: Continuous Translation
- English strings added continuously
- Translation platform syncs daily
- Human translators work on new strings
- Automated imports to codebase
- Ship when translation coverage hits threshold
Common Translation Challenges
Challenge 1: Missing Translations
Detect missing translations in CI:
// test/translation_test.dart
void main() {
test('all locales have complete translations', () {
final en = loadArb('lib/l10n/app_en.arb');
final es = loadArb('lib/l10n/app_es.arb');
final enKeys = en.keys.where((k) => !k.startsWith('@'));
final esKeys = es.keys.where((k) => !k.startsWith('@'));
final missing = enKeys.toSet().difference(esKeys.toSet());
expect(missing, isEmpty, reason: 'Spanish missing keys: $missing');
});
}
Challenge 2: Text Expansion
German and other languages can be 30-40% longer than English:
// Handle text overflow gracefully
Text(
l10n.longMessage,
overflow: TextOverflow.ellipsis,
maxLines: 2,
)
// Use flexible layouts
Flexible(
child: Text(l10n.buttonLabel),
)
Challenge 3: Concatenation Problems
Never concatenate translated strings:
// BAD - word order varies by language
final message = l10n.hello + ' ' + l10n.world;
// GOOD - single translation with placeholders
final message = l10n.helloWorld; // "Hello, World!" or "Hola, Mundo!"
Challenge 4: Hardcoded Strings
Find hardcoded strings with this regex:
# Find potential hardcoded strings in Dart files
grep -rn "Text(['\"]" lib/ --include="*.dart"
grep -rn "title: ['\"]" lib/ --include="*.dart"
Automating Translation Workflows
Git Hooks for Validation
#!/bin/sh
# .git/hooks/pre-commit
# Check for missing ARB keys
dart run scripts/check_arb_keys.dart
if [ $? -ne 0 ]; then
echo "Error: Missing translation keys detected"
exit 1
fi
CI Pipeline Integration
# .github/workflows/translations.yml
name: Translation Check
on: [push, pull_request]
jobs:
check-translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Flutter
uses: subosito/flutter-action@v2
- name: Generate localizations
run: flutter gen-l10n
- name: Check for missing keys
run: dart run scripts/check_translations.dart
- name: Verify no hardcoded strings
run: dart run scripts/find_hardcoded_strings.dart
Translation Memory
Keep track of previous translations to ensure consistency:
{
"// Translation memory - do not translate these differently": "",
"brand_name": "FlutterLocalisation",
"product_feature_sync": "Cloud Sync",
"product_feature_export": "ARB Export"
}
Scaling to 20+ Languages
Priority Order for Languages
Based on typical Flutter app demographics:
- Tier 1 (Launch): English, Spanish, Chinese (Simplified)
- Tier 2 (Early): German, French, Japanese, Portuguese
- Tier 3 (Growth): Arabic, Hindi, Korean, Italian, Russian
- Tier 4 (Expansion): Turkish, Dutch, Polish, Vietnamese, Thai
Managing Many Languages
// locale_config.dart
class LocaleConfig {
static const List<LocaleInfo> supportedLocales = [
LocaleInfo('en', 'English', '🇺🇸'),
LocaleInfo('es', 'Español', '🇪🇸'),
LocaleInfo('de', 'Deutsch', '🇩🇪'),
LocaleInfo('fr', 'Français', '🇫🇷'),
LocaleInfo('ja', '日本語', '🇯🇵'),
LocaleInfo('zh', '中文', '🇨🇳'),
LocaleInfo('ar', 'العربية', '🇸🇦'),
LocaleInfo('pt', 'Português', '🇧🇷'),
];
}
class LocaleInfo {
final String code;
final String nativeName;
final String flag;
const LocaleInfo(this.code, this.nativeName, this.flag);
}
Partial Translation Strategy
Ship with partial translations for new languages:
MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate,
// Fallback to English for missing translations
GlobalMaterialLocalizations.delegate,
],
localeResolutionCallback: (locale, supportedLocales) {
// Check translation coverage
final coverage = getTranslationCoverage(locale);
if (coverage < 0.8) {
// Fall back to English if less than 80% translated
return const Locale('en');
}
return locale;
},
)
Simplify with FlutterLocalisation
Managing translations across multiple languages is complex. FlutterLocalisation.com streamlines your workflow:
- Visual ARB editor - Edit all languages side-by-side
- AI translations - Generate quality translations for 80+ languages instantly
- Missing key detection - See untranslated strings at a glance
- Export ready - Download ARB files ready for your Flutter project
- Team collaboration - Multiple translators can work simultaneously
Stop juggling JSON files manually. Try FlutterLocalisation free and scale your app globally.
Summary
Effective Flutter translation management requires:
- Proper setup - Configure l10n.yaml and MaterialApp correctly
- Organization - Use consistent naming and file structure
- Context - Provide descriptions for translators
- Automation - Validate translations in CI/CD
- Scalability - Plan for language expansion from the start
With the right workflow, you can maintain high-quality translations across dozens of languages without slowing down development.