Summary

Flow Mobile supports three languages: English (base), Italian, Spanish. All user-facing strings are managed via Flutter’s ARB-based localization system.


Architecture

ComponentPath
ARB source fileslib/l10n/app_en.arb, app_it.arb, app_es.arb
Generated Dart fileslib/l10n/app_localizations*.dart (auto-generated)
Configl10n.yaml (synthetic-package: false)
Import pathpackage:flow/l10n/app_localizations.dart

The package is named flow (renamed from flow_app). The import is package:flow/l10n/... — not package: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

  1. Add key + value to app_en.arb (English is the template).
  2. Add the same key to app_it.arb and app_es.arb with translations.
  3. For parameterized strings, add an @key metadata block:
"photoUploadError": "Error uploading photo: {error}",
"@photoUploadError": {
  "placeholders": {
    "error": { "type": "String" }
  }
}
  1. Regenerate:
flutter pub get
# or:
flutter gen-l10n
  1. Use in Dart via AppLocalizations.of(context)!.myKey.

Key Conventions

PatternExampleWhen
screenActioneditProfile, saveChangesButtons, titles
sectionNamesectionPersonalInfo, sectionVibeCheckSection headers
wizardStepTitlewizardWelcomeTitle, wizardNameSubtitleRegistration wizard
wizardValidation*wizardValidationEmailRequiredInline wizard error messages
errorDescriptionphotoUploadError, cannotLoadMessagesError messages
labelNameemailLabel, passwordLabel, bioLabelForm 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.
  • searchFieldLabel in SearchDelegate subclasses — getter has no BuildContext.
  • 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, crew_bento_section, crew_detail_sheet


Key Inventory — Added May 2026

Added during event-details v3 refactor (2026-05-07, see feat-event-details-v3):

KeyWhere used
liveTonight, tomorrowNightHero live-chip (date-aware)
attendeesLabel, likesLabelStats strip
ratingSeriesLabel, ratingVenueLabelStats strip — context-aware rating row
venueLabel, seriesLabelVenue + series breadcrumb headers
ticketSoldOut, ticketSoldOutShortSold-out indicator + button
ticketLowStockUrgent (placeholder remaining: int)“Hurry up! N spots left”
ticketNotOnSaleSale window not yet open
lineupGeneralArea, lineupUnknownAreaLineup grouping fallbacks
crewsHeader, crewsSeeAll, crewsSeeAllWithMore (placeholder extra: int), crewsEmptyTitle, crewsEmptySubtitleCrews section
bottomBarAttendingLabel, bottomBarAttendingHeadline, bottomBarFromLabelBottom action bar states

Added during auth-screen deep-rework (edit/mobile-audit-ux-polish, commit 841ec58):

KeyScreen
continueWithFacebookLogin social divider
socialSignInComingSoonLogin / forgot social icons
orContinueWithLogin / forgot divider label
byUsingYouAcceptWelcome terms line
welcomeRegisterWelcome footer CTA
welcomeLoginWelcome footer CTA
messagePublic profile screen
nextEventsTabPublic profile screen
pastEventsTabPublic 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

Closed-beta access gate (June 2026, see feat-beta-access-gate):

accessPendingBadge, accessRejectedBadge, accessPendingTitle, accessPendingBody, accessRejectedTitle, accessRejectedBody, accessInviteCodeLabel, accessInviteCodeHint, accessRedeemButton, accessRefreshButton, accessLogoutButton, accessRedeemSuccess, accessRedeemErrorInvalid, accessRedeemErrorExpired, accessRedeemErrorUsed, accessRedeemErrorEmail, accessRedeemErrorNetwork

Condivisione in chat (luglio 2026, vedi feature-chat-sharing):

shareSheetTitle, shareSheetEmpty, shareSheetFailed, shareChatFallbackName, shareInviteToChat, shareJoinCrew, sharedKindEvent, sharedKindCrewInvite, sharedKindCrew, sharedKindVenue, sharedKindProfile, sharedKindPlace

Profilo di gusto locale (agosto 2026, vedi feature-taste-profile):

feedForYou, feedForYouWhy, tasteProfileSection, tasteProfileDescription, tasteProfileClear, tasteProfileCleared, tasteProfileLearning, tasteProfileActive

Ciclo di vita chat di crew e visibilita (agosto 2026, vedi feature-crew-chat-lifecycle):

crewWrapUpTitle, crewWrapUpBody, crewWrapUpAdd, crewWrapUpSent, crewWrapUpFriends, crewWrapUpAlone, crewVisibilityLabel, crewVisibilityPublic, crewVisibilityPublicHint, crewVisibilityFriends, crewVisibilityFriendsHint, crewVisibilityFriendsBadge

City picker (August 2026, see audit-mock-data-and-dead-controls):

citySearchHint, cityNoResults, allCities, cityListLoading

Crew merge and membership rules (August 2026, see feature-crew-merge):

alreadyInCrewForEvent, crewMergeTitle, crewMergeBody, crewMergeAccept, crewMergeReject, crewMergeVoted, crewMergeDone, crewMergeRejected, crewMergeOthers, crewMergePropose, crewMergeSent, crewMergeNoOthers

Chat and search discovery (August 2026, see design-chat-and-search-discovery):

chatToday, chatYesterday, spotsShort, noCrewsOpenTitle, noCrewsOpenBody, noVenuesYetTitle, noVenuesYetBody, noPeopleYetTitle, noPeopleYetBody


auth-screens — auth-specific l10n keys grouped by screen feat-registration-wizard-v2 — wizard validation keys feature-chat-sharing — chiavi della condivisione in chat