Localization (i18n / l10n)
Flow Mobile supports three languages out of the box: English (base), Italian, and Spanish. All user-facing strings are managed through Flutter’s ARB-based localization system.
Architecture
| Component | Path |
|---|---|
| ARB files (source of truth) | lib/l10n/app_en.arb, app_it.arb, app_es.arb |
| Generated Dart files | lib/l10n/app_localizations*.dart |
| Config | l10n.yaml (synthetic-package: false) |
| Import path | package:flow_app/l10n/app_localizations.dart |
The synthetic-package: false setting in l10n.yaml means generated files live inside lib/l10n/ rather than in a synthetic package. This is why the import is package:flow_app/l10n/app_localizations.dart and not flutter_gen/gen_l10n/.
Usage Pattern
import 'package:flow_app/l10n/app_localizations.dart';
// In a build method:
final l10n = AppLocalizations.of(context)!;
Text(l10n.someKey);
// With parameters:
Text(l10n.photoUploadError(e.toString()));
Text(l10n.participantsCount(count.toString()));Adding New Strings
- Add the key + value to
app_en.arb(English is the template). - Add the same key to
app_it.arbandapp_es.arbwith translations. - For parameterized strings, add an
@keymetadata block:
"photoUploadError": "Error uploading photo: {error}",
"@photoUploadError": {
"placeholders": {
"error": { "type": "String" }
}
}- Regenerate:
flutter gen-l10n
# or simply:
flutter pub get- Use the new key in Dart code via
AppLocalizations.of(context)!.photoUploadError(e.toString()).
Key Conventions
| Pattern | Example | When |
|---|---|---|
screenAction | editProfile, saveChanges | Buttons, titles |
sectionName | sectionPersonalInfo, sectionVibeCheck | Section headers |
wizardStepTitle | wizardWelcomeTitle, wizardNameSubtitle | Registration wizard |
errorDescription | photoUploadError, cannotLoadMessages | Error messages |
labelName | emailLabel, passwordLabel, bioLabel | Form field labels |
const Propagation
When replacing a const Text('literal') with Text(l10n.key), the const must be removed from Text (since l10n.key is not a compile-time constant). However, const should be kept on:
TextStyleinstances (they remain constant)- Ancestor widgets that don’t depend on the runtime value (e.g.,
Icon,SizedBox)
If a parent widget like Padding or SliverToBoxAdapter was const and now contains a non-const child, remove const from the parent too.
Locale-Aware Dates
Instead of hardcoded Italian month/day arrays, use the intl package:
import 'package:intl/intl.dart';
final locale = Localizations.localeOf(context).toString();
final monthName = DateFormat('MMMM', locale).format(DateTime(2024, month));This is used in FlowDateWheelPicker and the feed screen’s showDatePicker.
Strings Intentionally NOT Localized
- Vibe filter labels (
"Tutto","Consigliati", etc.) in the feed screen serve as both display text and filter keys matched against database tags. Localizing them would break the filtering logic. searchFieldLabelinSearchDelegatesubclasses has noBuildContext(it’s a getter), so it can’t useAppLocalizations.- Brand names (Instagram, TikTok) are kept as-is.
Quality Sweep (2026-04-08)
A cleanup pass on the existing ARB files removed 22 duplicate keys per locale (66 total lines), corrected the age-gate string from 16 to 18 in wizardBirthdaySubtitle, localized two previously hardcoded surfaces (welcome_screen carousel + headline, feed_interruption_widgets invite card and “vibe of the day” label), and stripped decorative emoji from 15 keys (imIn, youreInSquad, findACrewButton, chatEmptyTitle, sectionRecommendedForYou, profileUpdateSuccess, sectionVibeCheck, bugReportSent, joinSquad, crewsNearYou, noConnection, serverError, sessionExpired, checkInputAndRetry, locationNeeded).
The duplicate-key bug class is worth flagging for the future: Flutter’s ARB parser silently keeps the last occurrence, so a drifted duplicate value will be the one that ships at runtime — a category of latent bug that won’t surface until an unlucky merge introduces a divergence. Use:
python -c "import re; from collections import Counter; \
print(Counter(re.findall(r'^\s*\"([a-zA-Z]\w*)\"\s*:', open('lib/l10n/app_en.arb').read(), re.M)).most_common(10))"…to spot duplicates before they bite.
Migration Summary (April 2025)
The l10n migration covered ~540+ keys across 20+ screens:
Screens fully localized:
- Auth:
registration_wizard_screen,forgot_password_screen,welcome_screen - Feed:
feed_screen - Events:
event_discovery_screen,event_details_screen,events_screen - Messaging:
chat_screen,chat_details_screen,chats_screen,message_requests_screen,new_chat_screen - Profile:
profile_screen,edit_profile_screen,profile_setup_screen - Settings:
settings_screen,privacy_settings_screen - Social:
friends_screen - Search:
search_screen - Notifications:
notifications_screen
Shared widgets localized:
error_reporter_overlay,empty_state_widget,error_widget,loading_widgets,flow_date_wheel_picker,minimal_event_card,product_checkout_sheet,squad_bento_section,squad_detail_sheet