Summary
Flow Mobile supports three languages: English (base), Italian, Spanish. All user-facing strings are managed via Flutter’s ARB-based localization system.
Architecture
| Component | Path |
|---|---|
| ARB source files | lib/l10n/app_en.arb, app_it.arb, app_es.arb |
| Generated Dart files | lib/l10n/app_localizations*.dart (auto-generated) |
| Config | l10n.yaml (synthetic-package: false) |
| Import path | package:flow/l10n/app_localizations.dart |
The package is named
flow(renamed fromflow_app). The import ispackage:flow/l10n/...— notpackage:flow_app/l10n/....
synthetic-package: false means generated files live inside lib/l10n/ rather than a synthetic package. Never use flutter_gen/gen_l10n/ imports.
Usage Pattern
import 'package:flow/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 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 pub get
# or:
flutter gen-l10n- Use in Dart via
AppLocalizations.of(context)!.myKey.
Key Conventions
| Pattern | Example | When |
|---|---|---|
screenAction | editProfile, saveChanges | Buttons, titles |
sectionName | sectionPersonalInfo, sectionVibeCheck | Section headers |
wizardStepTitle | wizardWelcomeTitle, wizardNameSubtitle | Registration wizard |
wizardValidation* | wizardValidationEmailRequired | Inline wizard error messages |
errorDescription | photoUploadError, cannotLoadMessages | Error messages |
labelName | emailLabel, passwordLabel, bioLabel | Form field labels |
const Propagation Rule
When replacing const Text('literal') with Text(l10n.key), remove const from the Text. Keep const on: TextStyle, Icon, SizedBox, and any ancestor that doesn’t depend on the runtime value.
Locale-Aware Dates
Use the intl package — not hardcoded arrays:
import 'package:intl/intl.dart';
final locale = Localizations.localeOf(context).toString();
final monthName = DateFormat('MMMM', locale).format(DateTime(2024, month));Used in FlowDateWheelPicker and the feed screen’s showDatePicker.
Strings Intentionally NOT Localized
- Vibe filter labels (
"Tutto","Consigliati"etc.) — serve as DB tag keys; localizing would break filter logic. searchFieldLabelinSearchDelegatesubclasses — getter has noBuildContext.- Brand names (Instagram, TikTok, Flow) — kept as-is.
Duplicate-Key Detection
Flutter’s ARB parser silently keeps the last occurrence of a duplicate key. Run this before any merge:
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))"Screens Fully Localized
Auth: registration_wizard_screen, forgot_password_screen, welcome_screen, login_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: 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
Key Inventory — Added May 2026
Added during event-details v3 refactor (2026-05-07, see feat-event-details-v3):
| Key | Where used |
|---|---|
liveTonight, tomorrowNight | Hero live-chip (date-aware) |
attendeesLabel, likesLabel | Stats strip |
ratingSeriesLabel, ratingVenueLabel | Stats strip — context-aware rating row |
venueLabel, seriesLabel | Venue + series breadcrumb headers |
ticketSoldOut, ticketSoldOutShort | Sold-out indicator + button |
ticketLowStockUrgent (placeholder remaining: int) | “Hurry up! N spots left” |
ticketNotOnSale | Sale window not yet open |
lineupGeneralArea, lineupUnknownArea | Lineup grouping fallbacks |
crewsHeader, crewsSeeAll, crewsSeeAllWithMore (placeholder extra: int), crewsEmptyTitle, crewsEmptySubtitle | Crews section |
bottomBarAttendingLabel, bottomBarAttendingHeadline, bottomBarFromLabel | Bottom action bar states |
Added during auth-screen deep-rework (edit/mobile-audit-ux-polish, commit 841ec58):
| Key | Screen |
|---|---|
continueWithFacebook | Login social divider |
socialSignInComingSoon | Login / forgot social icons |
orContinueWith | Login / forgot divider label |
byUsingYouAccept | Welcome terms line |
welcomeRegister | Welcome footer CTA |
welcomeLogin | Welcome footer CTA |
message | Public profile screen |
nextEventsTab | Public profile screen |
pastEventsTab | Public profile screen |
Previously added (April 2026 design cleanup):
welcomeHeadline, welcomeCarouselLine1–4, continueWithApple, continueWithGoogle, appleSignInComingSoon, googleSignInComingSoon, signInWithEmail
Wizard validation keys (full list in feat-registration-wizard-v2):
wizardValidationFirstNameRequired, wizardValidationAgeRestriction, wizardValidationUsernameRequired, wizardValidationUsernameFormat, wizardValidationUsernameTaken, wizardValidationEmailRequired, wizardValidationEmailFormat, wizardValidationPasswordTooShort, wizardValidationBusinessNameRequired, wizardValidationBusinessCategoryRequired, wizardValidationStageNameRequired, wizardValidationLocationRequired, wizardValidationEmailTaken, dateInputDay, dateInputMonth, dateInputYear, dateInputModeKeyboard, dateInputModeWheel
Related
auth-screens — auth-specific l10n keys grouped by screen feat-registration-wizard-v2 — wizard validation keys