Add localization (l10n/i18n) support with language selection#277
Add localization (l10n/i18n) support with language selection#277dhruvi-16-me wants to merge 5 commits into
Conversation
WalkthroughThis PR introduces multi-language support (English/Spanish) via Flutter localization, adds a responsive design system with breakpoints and layout utilities, establishes Riverpod state providers for locale/theme/dashboard/navigation, bootstraps the app with provider wiring, and converts screen UIs throughout to use localized strings integrated with the new state management. ChangesLocalization & Responsive Design with Riverpod State Management
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
lib/screens/profile/profile_screen.dart (1)
514-527:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftProfile/settings localization is still incomplete.
Language selection was added, but many visible profile/settings strings (including the logout button text and multiple setting titles/subtitles) remain hardcoded, so this screen still renders mixed languages.
Also applies to: 748-802
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/screens/profile/profile_screen.dart` around lines 514 - 527, Replace hardcoded visible strings in the profile/settings screen with localized lookups: update _buildLogoutButton to use the app's localization accessor (e.g., AppLocalizations.of(context).logout or similar) instead of the hardcoded 'Logout', and scan the same file for other hardcoded titles/subtitles (notably the widgets around lines where setting items are built, referenced by their build methods/variables) and replace each literal with the corresponding localization key (e.g., settingsTitle, settingsSubtitle) via the localization API; ensure you pass the BuildContext into any helper methods that currently lack it so you can call the localization accessor.lib/screens/home/home_screen.dart (1)
153-168:⚠️ Potential issue | 🟠 Major | ⚡ Quick winBottom navigation localization is inconsistent.
Only the Dashboard tab uses
l10n; Calendar/Workspace/Chat/Profile are still hardcoded, so the nav bar shows mixed languages after locale switch.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/screens/home/home_screen.dart` around lines 153 - 168, The BottomNavigationBarItem labels are partially hardcoded, causing mixed-language UI; replace the hardcoded labels ('Calendar', 'Workspace', 'Chat', 'Profile') with the corresponding localization properties from the l10n instance (same way l10n.dashboard is used) in the BottomNavigationBar items in home_screen.dart so all tabs use localized strings consistently (update the BottomNavigationBarItem entries that use Icon(Icons.calendar_today), Icon(Icons.work), Icon(Icons.chat_bubble_outline), and Icon(Icons.person) to use l10n.calendar, l10n.workspace, l10n.chat, and l10n.profile or the exact keys defined in your arb/localization class).lib/screens/auth/login_screen.dart (1)
163-167:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftLocalization is incomplete on the login flow.
Only the primary login and sign-up labels are localized; the rest of user-facing text on this screen is still hardcoded English, so switching language results in mixed-language UX.
Also applies to: 176-223, 239-291
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/screens/auth/login_screen.dart` around lines 163 - 167, The login screen currently uses hardcoded English strings; replace all user-facing literals in AuthScreenWrapper (e.g., title, subtitle) and throughout the children subtree (the widgets and labels rendered between lines shown, including buttons, helper texts, error messages, link labels and any RichText spans used in the login flow) with localized values from the l10n object (AppLocalizations.of(context))—use the appropriate l10n getters for strings (e.g., l10n.loginTitle, l10n.loginSubtitle, l10n.signInButton, l10n.signUpLink, l10n.forgotPassword, etc.) and update any concatenations to use interpolated localized templates; ensure every hardcoded literal in AuthScreenWrapper and its children is replaced so the screen is fully localized.lib/screens/profile/edit_profile_screen.dart (1)
124-145:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftEdit profile localization is only partially applied.
The save label is localized, but most other user-facing strings on this screen are still hardcoded English, producing mixed-language behavior.
Also applies to: 201-237
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/screens/profile/edit_profile_screen.dart` around lines 124 - 145, The AppBar title ("Edit Profile") and other hardcoded user-facing strings in edit_profile_screen.dart (e.g., the AppBar title and strings between lines ~201-237) must be replaced with localization lookups using the existing l10n object (like l10n.editProfile or appropriately named keys) to avoid mixed-language UI; update or add the corresponding keys in your localization resources if missing and replace occurrences of hardcoded literals in widgets such as the AppBar title and any Button/Label/Text widgets in the EditProfileScreen (reference: the AppLocalizations l10n variable and the _saveProfile TextButton.icon) so all visible strings use l10n.* consistently.lib/widgets/custom_widgets.dart (1)
656-677:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winComplete localization in
WorkspaceLoadingSkeletonlabels
searchis localized, but the same loading skeleton still renders hardcoded text (Workspace,New,All,Tasks,Tickets). This causes mixed-language UI during loading for non-English locales.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/widgets/custom_widgets.dart` around lines 656 - 677, The WorkspaceLoadingSkeleton renders hardcoded labels causing mixed-language UI; replace the hardcoded strings "Workspace", "New", "All", "Tasks", and "Tickets" with their localized equivalents using AppLocalizations (e.g. AppLocalizations.of(context).workspace, .newLabel or appropriate keys) where those texts are built, and ensure the tab builder (_buildTab) and any parent widget that supplies those labels receive the localized strings so all labels (including tabs and action buttons) use AppLocalizations.of(context) consistently during loading.lib/screens/workspace/workspace_screen.dart (1)
156-181:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLocalize success SnackBar messages to avoid mixed-language UX
With
l10nnow wired for this screen, success messages are still hardcoded English at Line 86, Line 112, and Line 138. After switching to Spanish, these remain untranslated.Suggested fix
- const SnackBar( - content: Text('Task created successfully'), + SnackBar( + content: Text(AppLocalizations.of(context).taskCreatedSuccessfully), backgroundColor: Colors.green, ), - const SnackBar( - content: Text('Ticket created successfully'), + SnackBar( + content: Text(AppLocalizations.of(context).ticketCreatedSuccessfully), backgroundColor: Colors.green, ), - const SnackBar( - content: Text('Meeting created successfully'), + SnackBar( + content: Text(AppLocalizations.of(context).meetingCreatedSuccessfully), backgroundColor: Colors.green, ),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/screens/workspace/workspace_screen.dart` around lines 156 - 181, Three SnackBar success messages in workspace_screen.dart are still hardcoded in English; update them to use the localized strings from the l10n instance already obtained (replace the literal strings passed to SnackBar(content: Text(...)) with appropriate l10n properties), e.g., add new keys to AppLocalizations (if not present) for those success messages and use l10n.mySuccessKey when building the SnackBar in the callbacks that currently show hardcoded English text; ensure you update the Widget locations that call SnackBar (where SnackBar(content: Text("...")) is used) to reference the l10n properties instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/main.dart`:
- Around line 55-60: The MaterialApp is forcing English by using locale: locale
?? const Locale('en'), which prevents using the device/system locale when the
user has no preference; change the MaterialApp locale prop to pass the watched
locale directly (e.g., locale: locale) so a null value falls back to the system
locale, updating the code around MaterialApp and the localeControllerProvider
usage accordingly.
In `@lib/providers/dashboard/dashboard_controller.dart`:
- Around line 10-13: The screen is using local state (_selectedTimeRange and
_timeRangeButton) instead of the Riverpod DashboardController; update the UI to
use ref.watch(dashboardControllerProvider) for the selected range and chart data
and change the tap handlers in _timeRangeButton to call
ref.read(dashboardControllerProvider.notifier).setTimeRange(index) (or remove
the provider if you prefer not to use it). Specifically, replace reads/writes of
_selectedTimeRange with the value from
ref.watch(dashboardControllerProvider).timeRange and wire the button onTap to
call DashboardController.setTimeRange via
ref.read(dashboardControllerProvider.notifier).setTimeRange(index) so the
controller is actually used.
In `@lib/providers/locale_provider.dart`:
- Around line 40-46: Extract a single constant defaultLocale (e.g. const Locale
defaultLocale = Locale('en')) at the top of this file and replace the hardcoded
'en' fallback in isSelected with a comparison to defaultLocale.languageCode (or
compare Locale objects to defaultLocale); also update currentLanguageLabel and
any fallback usage in main.dart to reference that same defaultLocale constant so
all default-locale logic is centralized and consistent (look for symbols
isSelected and currentLanguageLabel to locate the spots to change).
---
Outside diff comments:
In `@lib/screens/auth/login_screen.dart`:
- Around line 163-167: The login screen currently uses hardcoded English
strings; replace all user-facing literals in AuthScreenWrapper (e.g., title,
subtitle) and throughout the children subtree (the widgets and labels rendered
between lines shown, including buttons, helper texts, error messages, link
labels and any RichText spans used in the login flow) with localized values from
the l10n object (AppLocalizations.of(context))—use the appropriate l10n getters
for strings (e.g., l10n.loginTitle, l10n.loginSubtitle, l10n.signInButton,
l10n.signUpLink, l10n.forgotPassword, etc.) and update any concatenations to use
interpolated localized templates; ensure every hardcoded literal in
AuthScreenWrapper and its children is replaced so the screen is fully localized.
In `@lib/screens/home/home_screen.dart`:
- Around line 153-168: The BottomNavigationBarItem labels are partially
hardcoded, causing mixed-language UI; replace the hardcoded labels ('Calendar',
'Workspace', 'Chat', 'Profile') with the corresponding localization properties
from the l10n instance (same way l10n.dashboard is used) in the
BottomNavigationBar items in home_screen.dart so all tabs use localized strings
consistently (update the BottomNavigationBarItem entries that use
Icon(Icons.calendar_today), Icon(Icons.work), Icon(Icons.chat_bubble_outline),
and Icon(Icons.person) to use l10n.calendar, l10n.workspace, l10n.chat, and
l10n.profile or the exact keys defined in your arb/localization class).
In `@lib/screens/profile/edit_profile_screen.dart`:
- Around line 124-145: The AppBar title ("Edit Profile") and other hardcoded
user-facing strings in edit_profile_screen.dart (e.g., the AppBar title and
strings between lines ~201-237) must be replaced with localization lookups using
the existing l10n object (like l10n.editProfile or appropriately named keys) to
avoid mixed-language UI; update or add the corresponding keys in your
localization resources if missing and replace occurrences of hardcoded literals
in widgets such as the AppBar title and any Button/Label/Text widgets in the
EditProfileScreen (reference: the AppLocalizations l10n variable and the
_saveProfile TextButton.icon) so all visible strings use l10n.* consistently.
In `@lib/screens/profile/profile_screen.dart`:
- Around line 514-527: Replace hardcoded visible strings in the profile/settings
screen with localized lookups: update _buildLogoutButton to use the app's
localization accessor (e.g., AppLocalizations.of(context).logout or similar)
instead of the hardcoded 'Logout', and scan the same file for other hardcoded
titles/subtitles (notably the widgets around lines where setting items are
built, referenced by their build methods/variables) and replace each literal
with the corresponding localization key (e.g., settingsTitle, settingsSubtitle)
via the localization API; ensure you pass the BuildContext into any helper
methods that currently lack it so you can call the localization accessor.
In `@lib/screens/workspace/workspace_screen.dart`:
- Around line 156-181: Three SnackBar success messages in workspace_screen.dart
are still hardcoded in English; update them to use the localized strings from
the l10n instance already obtained (replace the literal strings passed to
SnackBar(content: Text(...)) with appropriate l10n properties), e.g., add new
keys to AppLocalizations (if not present) for those success messages and use
l10n.mySuccessKey when building the SnackBar in the callbacks that currently
show hardcoded English text; ensure you update the Widget locations that call
SnackBar (where SnackBar(content: Text("...")) is used) to reference the l10n
properties instead.
In `@lib/widgets/custom_widgets.dart`:
- Around line 656-677: The WorkspaceLoadingSkeleton renders hardcoded labels
causing mixed-language UI; replace the hardcoded strings "Workspace", "New",
"All", "Tasks", and "Tickets" with their localized equivalents using
AppLocalizations (e.g. AppLocalizations.of(context).workspace, .newLabel or
appropriate keys) where those texts are built, and ensure the tab builder
(_buildTab) and any parent widget that supplies those labels receive the
localized strings so all labels (including tabs and action buttons) use
AppLocalizations.of(context) consistently during loading.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: bf96d35a-6956-4c97-a216-c8498d035887
⛔ Files ignored due to path filters (1)
pubspec.lockis excluded by!**/*.lock
📒 Files selected for processing (23)
l10n.yamllib/core/responsive/breakpoints.dartlib/core/responsive/responsive_layout.dartlib/core/responsive/responsive_utils.dartlib/l10n/app_en.arblib/l10n/app_es.arblib/main.dartlib/providers/dashboard/dashboard_controller.dartlib/providers/dashboard/dashboard_state.dartlib/providers/dashboard/dashboard_state.freezed.dartlib/providers/dashboard_provider.dartlib/providers/locale_provider.dartlib/providers/navigation_provider.dartlib/providers/shared_preferences_provider.dartlib/providers/theme_provider.dartlib/screens/auth/login_screen.dartlib/screens/home/home_screen.dartlib/screens/profile/edit_profile_screen.dartlib/screens/profile/profile_screen.dartlib/screens/workspace/workspace_screen.dartlib/widgets/custom_widgets.dartpubspec.yamltest/widget_test.dart
| final locale = ref.watch(localeControllerProvider); | ||
| return MaterialApp( | ||
| title: 'Ell-ena', | ||
| onGenerateTitle: (context) => AppLocalizations.of(context).appTitle, | ||
| debugShowCheckedModeBanner: false, | ||
| navigatorKey: NavigationService().navigatorKey, | ||
| locale: locale ?? const Locale('en'), | ||
| localizationsDelegates: const [ |
There was a problem hiding this comment.
Preserve device-locale fallback instead of forcing English.
locale: locale ?? const Locale('en') forces English when no user preference exists, so system locale is never used. This breaks the intended default locale behavior.
💡 Suggested fix
- locale: locale ?? const Locale('en'),
+ locale: locale,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| final locale = ref.watch(localeControllerProvider); | |
| return MaterialApp( | |
| title: 'Ell-ena', | |
| onGenerateTitle: (context) => AppLocalizations.of(context).appTitle, | |
| debugShowCheckedModeBanner: false, | |
| navigatorKey: NavigationService().navigatorKey, | |
| locale: locale ?? const Locale('en'), | |
| localizationsDelegates: const [ | |
| final locale = ref.watch(localeControllerProvider); | |
| return MaterialApp( | |
| onGenerateTitle: (context) => AppLocalizations.of(context).appTitle, | |
| debugShowCheckedModeBanner: false, | |
| locale: locale, | |
| localizationsDelegates: const [ |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/main.dart` around lines 55 - 60, The MaterialApp is forcing English by
using locale: locale ?? const Locale('en'), which prevents using the
device/system locale when the user has no preference; change the MaterialApp
locale prop to pass the watched locale directly (e.g., locale: locale) so a null
value falls back to the system locale, updating the code around MaterialApp and
the localeControllerProvider usage accordingly.
| /// Sets chart range: 0 = week, 1 = month. | ||
| void setTimeRange(int timeRange) { | ||
| state = state.copyWith(timeRange: timeRange); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check if dashboard_screen.dart uses dashboardControllerProvider
rg -n -C5 'dashboardControllerProvider' --type=dart --glob '*dashboard_screen.dart'
echo "---"
echo "Current setState usage in dashboard_screen:"
rg -n -C3 'setState.*_selectedTimeRange' --type=dart --glob '*dashboard_screen.dart'Repository: AOSSIE-Org/Ell-ena
Length of output: 686
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="lib/screens/home/dashboard_screen.dart"
echo "=== dashboardControllerProvider references in dashboard_screen.dart ==="
rg -n --type=dart 'dashboardControllerProvider' "$FILE" || true
echo "=== setTimeRange calls in dashboard_screen.dart ==="
rg -n --type=dart 'setTimeRange\s*\(' "$FILE" || true
echo "=== notifier usage in dashboard_screen.dart (read/watch) ==="
rg -n --type=dart '\.notifier\b|ref\.(watch|read)\b' "$FILE" || true
echo "=== DashboardController mutation method names in controller file ==="
CTRL="lib/providers/dashboard/dashboard_controller.dart"
rg -n --type=dart 'void\s+(setTimeRange|setSelectedTeam|clearSelectedTeam|setFilter|reset)\b' "$CTRL" || trueRepository: AOSSIE-Org/Ell-ena
Length of output: 502
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the time-range button area that previously matched setState for _selectedTimeRange
echo "=== dashboard_screen.dart: excerpt around _timeRangeButton ==="
sed -n '900,1020p' lib/screens/home/dashboard_screen.dart
# Show provider declaration + state class around top of controller file
echo "=== dashboard_controller.dart: excerpt (top) ==="
sed -n '1,120p' lib/providers/dashboard/dashboard_controller.dartRepository: AOSSIE-Org/Ell-ena
Length of output: 5760
DashboardController is not wired into dashboard_screen UI (time range stays local state).
lib/screens/home/dashboard_screen.dart updates _selectedTimeRange and chart data via local setState in _timeRangeButton, and the file has no references to dashboardControllerProvider (so DashboardController.setTimeRange(...) isn’t called). This leaves the Riverpod controller infrastructure unused for this screen. Wire the tap handlers to ref.read(dashboardControllerProvider.notifier).setTimeRange(index) and drive the UI from ref.watch(dashboardControllerProvider), or defer/limit this controller until it’s actually consumed.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/providers/dashboard/dashboard_controller.dart` around lines 10 - 13, The
screen is using local state (_selectedTimeRange and _timeRangeButton) instead of
the Riverpod DashboardController; update the UI to use
ref.watch(dashboardControllerProvider) for the selected range and chart data and
change the tap handlers in _timeRangeButton to call
ref.read(dashboardControllerProvider.notifier).setTimeRange(index) (or remove
the provider if you prefer not to use it). Specifically, replace reads/writes of
_selectedTimeRange with the value from
ref.watch(dashboardControllerProvider).timeRange and wire the button onTap to
call DashboardController.setTimeRange via
ref.read(dashboardControllerProvider.notifier).setTimeRange(index) so the
controller is actually used.
| bool isSelected(Locale locale) { | ||
| final current = state; | ||
| if (current == null) { | ||
| return locale.languageCode == 'en'; | ||
| } | ||
| return current.languageCode == locale.languageCode; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Centralize the default locale constant.
The hardcoded 'en' fallback on line 43 duplicates the default locale logic that also appears in currentLanguageLabel (line 62) and main.dart. Consider extracting a const Locale defaultLocale = Locale('en') at the top of this file and referencing it consistently across all methods and in main.dart's fallback.
♻️ Proposed refactor to centralize default locale
const String _localeStorageKey = 'app_locale';
+const Locale defaultLocale = Locale('en');
/// Supported app locales. Add new entries when ARB files are added.
const List<Locale> supportedAppLocales = [Then update the usages:
/// Whether [locale] matches the persisted selection.
bool isSelected(Locale locale) {
final current = state;
if (current == null) {
- return locale.languageCode == 'en';
+ return locale.languageCode == defaultLocale.languageCode;
}
return current.languageCode == locale.languageCode; /// Subtitle for the settings row showing the active language.
String currentLanguageLabel(AppLocalizations l10n) {
- final code = state?.languageCode ?? 'en';
+ final code = state?.languageCode ?? defaultLocale.languageCode;
return labelFor(l10n, Locale(code));
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/providers/locale_provider.dart` around lines 40 - 46, Extract a single
constant defaultLocale (e.g. const Locale defaultLocale = Locale('en')) at the
top of this file and replace the hardcoded 'en' fallback in isSelected with a
comparison to defaultLocale.languageCode (or compare Locale objects to
defaultLocale); also update currentLanguageLabel and any fallback usage in
main.dart to reference that same defaultLocale constant so all default-locale
logic is centralized and consistent (look for symbols isSelected and
currentLanguageLabel to locate the spots to change).
Description
This PR introduces localization (l10n/i18n) support to Ell-ena using Flutter's official
gen_l10nworkflow.The implementation adds a scalable localization architecture, language selection UI, locale persistence, and initial English/Spanish translations for commonly used navigation, authentication, and settings strings.
Users can now switch between supported languages directly from Settings, and their preference is preserved across app restarts and web refreshes.
The localization system is designed to support future language additions through ARB files without requiring further architectural changes.
Changes Made
Localization Infrastructure
gen_l10n)MaterialAppLanguage Management
LocaleControllerfor application-wide locale stateSharedPreferencesUser Interface
Initial Localization Coverage
Localized core user-facing strings including:
Navigation
Authentication
General Actions
Settings
Testing
Supported Languages
Future Expansion
Additional languages can be added by:
app_xx.arb)No further architectural changes are required.
Validation
All completed successfully.
✅ Checklist
Summary by CodeRabbit
New Features
Improvements