-
Notifications
You must be signed in to change notification settings - Fork 131
Modernized codebase for multilingual support #381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modernized codebase for multilingual support #381
Conversation
WalkthroughThe codebase was refactored to introduce comprehensive multilingual support across the application. This includes a new language controller, extensive expansion of localization keys and resources for both English and Hindi, integration of localization into all user-facing UI text, and updates to tests to support and validate localized content. No business logic changes were made. Changes
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested labels
Suggested reviewers
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
🔭 Outside diff range comments (7)
lib/ui/views/projects/edit_project_view.dart (1)
88-114: Dropdown now mixes view-labels with domain values – this will crash and/or send wrong data
DropdownButtonFormFieldis fed with localized strings (public,private,limited_access) but itsvalueis still the raw backend value coming fromwidget.project.attributes.projectAccessType(e.g."Public").
When the current locale is not English thevaluewill not be found initems, causing an exception at build time.
Even worse, after the user selects an option the string stored in_projectAccessTypebecomes the localized label, soupdateProject()will post Hindi (or other) text to the API instead of the expected enum value.- [ - AppLocalizations.of(context)!.public, - AppLocalizations.of(context)!.private, - AppLocalizations.of(context)!.limited_access, - ].map<DropdownMenuItem<String>>((type) { - return DropdownMenuItem<String>(value: type, child: Text(type)); - }).toList(), + // Keep the backend value as `value`, show the localized label in `child` + const ['Public', 'Private', 'Limited Access'] + .map<DropdownMenuItem<String>>( + (type) => DropdownMenuItem<String>( + value: type, + child: Text( + { + 'Public': AppLocalizations.of(context)!.public, + 'Private': AppLocalizations.of(context)!.private, + 'Limited Access': AppLocalizations.of(context)!.limited_access, + }[type]!, + ), + ), + ) + .toList(),This keeps UI text localized while ensuring the model continues to use stable, backend-compatible values.
lib/ui/views/profile/user_projects_view.dart (1)
74-80: Detach the addedProfileViewModellistener to avoid leaks
context.watch<ProfileViewModel>().addListener(...)is registered inonModelReadybut never removed. Because theBaseViewcan be rebuilt or the widget disposed while the provider lives longer, the closure keeps the view alive and can deliver stale callbacks.+late VoidCallback _profileListener; onModelReady: (model) { model.fetchUserProjects(userId: widget.userId); - context.watch<ProfileViewModel>().addListener(() { + _profileListener = () { var updatedProject = context.read<ProfileViewModel>().updatedProject; if (updatedProject == null) return; model.onProjectChanged(updatedProject); - }); + }; + context.read<ProfileViewModel>().addListener(_profileListener); }, ... @override void dispose() { + context.read<ProfileViewModel>().removeListener(_profileListener); super.dispose(); }lib/ui/views/projects/project_details_view.dart (2)
118-133: String concatenation prevents proper pluralisation
' ${_model.starCount} ${l10n.stars}'will read “1 stars”. Use Intl plural-format strings (Intl.plural) or ARB plural placeholders so translators can handle singular/plural correctly. Same for “views”.
399-415: Validator error message still hard-coded in English
The email validator returns a hard-coded string (“Enter emails in valid format.”), breaking localisation consistency.- validator: - (emails) => - Validators.areEmailsValid(emails) - ? null - : 'Enter emails in valid format.', + validator: (emails) => Validators.areEmailsValid(emails) + ? null + : AppLocalizations.of(context)!.email_format_error,Ensure
email_format_errorexists in both ARB files.test/ui_tests/authentication/forgot_password_view_test.dart (1)
55-64: Hard-coded English strings defeat the purpose of the new i18n layerAlthough localisation delegates are wired, the assertions still rely on the literal text
'Forgot Password?'.
Use the translated value so tests survive future copy updates or additional languages.@@ - expect(find.text('Forgot Password?'), findsOneWidget); + final context = tester.element(find.byType(LoginView)); + final localizations = AppLocalizations.of(context)!; + expect(find.text(localizations.forgot_password), findsOneWidget);Apply the same treatment to the “New User? Sign Up” RichText checks below.
test/ui_tests/groups/add_assignment_view_test.dart (1)
139-141: Drive the UI – avoid callingonPresseddirectlyInvoking the callback bypasses gesture detection and semantics.
Prefertester.tapto mimic the user path:- Widget widget = find.byType(CVPrimaryButton).evaluate().first.widget; - (widget as CVPrimaryButton).onPressed!(); + await tester.tap( + find.widgetWithText(CVPrimaryButton, localizations.create_assignment), + );test/ui_tests/authentication/login_view_test.dart (1)
80-95:verify()without an explicit call-count hides navigation failuresThe first
verifyafterpumpWidgetalready satisfies Mockito’s “called ≥ 1” rule.
A second bareverifytherefore passes even if the actual navigation did not occur.- verify(mockObserver.didPush(any, any)); + // Expect exactly two pushes so that the second verification is meaningful + verify(mockObserver.didPush(any, any)).called(2);
🧹 Nitpick comments (44)
lib/ui/views/authentication/login_view.dart (1)
98-102: Avoid blindtoUpperCase()on localized strings
AppLocalizations.of(context)!.login.toUpperCase()assumes the language has a straightforward upper-case mapping.
Scripts like Devanagari have no case, and languages such as Turkish have locale-specific rules.
Consider either:
- Provide an explicit upper-case variant in the ARB files, or
- Drop the transformation and rely on styling (e.g.,
fontWeight: FontWeight.bold) instead.lib/ui/views/authentication/forgot_password_view.dart (1)
43-50: Micro-optimisation: cache the localization handle
AppLocalizations.of(context)!is called many times inside a single build.
Capturing it once (final l10n = AppLocalizations.of(context)!;) trims repetition and slightly reduces lookup overhead.lib/ui/views/groups/new_group_view.dart (1)
34-36: Avoid repeatedAppLocalizations.of(context)!look-ups & locale-insensitivetoUpperCase()You are calling
AppLocalizations.of(context)!10+ times in this widget and converting some of the returned strings to upper-case with the default (Locale.defaultLocale) collation.
Consider:
- Caching the localizations object at the top of
build:final l10n = AppLocalizations.of(context)!;
- Providing pre-capitalised keys in the ARB files (e.g.
save_caps) or usingtoUpperCase(l10n.localeName)to respect locale rules (Turkish “i”, Greek accents, etc.).This will make the build method cheaper and avoid unexpected glyph changes in certain languages.
Also applies to: 46-48, 51-52, 72-76, 85-92, 98-99
test/ui_tests/teachers/teachers_view_test.dart (1)
26-34: Hard-codingsupportedLocalesto English may hide i18n regressionsThe test fixes the immediate failure, but locking
supportedLocalesto[Locale('en', '')]means widgets won’t be built for other locales, defeating the purpose of the new multilingual support.Prefer the generated list to keep the test aligned with production:
- supportedLocales: const [Locale('en', '')], - locale: const Locale('en', ''), + supportedLocales: AppLocalizations.supportedLocales, + locale: const Locale('en', ''), // keep deterministicThis still forces English for golden-tests while ensuring delegates for all locales are registered.
lib/ui/views/groups/my_groups_view.dart (1)
70-73: Upper-casing a translated string may corrupt certain scripts
confirmationTitle: AppLocalizations.of(context)!.delete.toUpperCase(),The generic
String.toUpperCase()is not Unicode-aware for every script (e.g. Turkish, German ß). Supply an already-capitalised key (delete_caps) or pass the locale:confirmationTitle: AppLocalizations.of(context)! .delete .toUpperCase(AppLocalizations.of(context)!.localeName),Same issue exists for
_dialogService.showCustomProgressDialog(“DELETING_GROUP”) and any other runtime case conversions.lib/ui/views/contributors/contributors_view.dart (1)
24-26: Micro-optimisation: cache localisation instanceLarge stateless lists that rebuild frequently (e.g., scroll momentum) benefit from a single local variable instead of 15 identical calls:
final l10n = AppLocalizations.of(context)!; … title: l10n.contribute_title, description: l10n.contribute_description,Not critical but improves readability and trace-time.
Also applies to: 30-37, 41-47, 49-73, 77-84
test/ui_tests/profile/user_favourites_view_test.dart (1)
60-64: Missing explicitlocalemakes the test rely on host settingsThe delegates are wired, but the locale isn’t fixed, so CI machines with a non-EN default could render unexpected text and break matching. Add an explicit locale for determinism:
GetMaterialApp( … + locale: const Locale('en', ''), localizationsDelegates: [Optionally pull in
GlobalMaterialLocalizations.delegateand friends for completeness (they’re added automatically, but being explicit avoids confusion).test/ui_tests/ib/ib_page_view_test.dart (1)
87-93: Configure localization delegates and locales in tests
InjectedlocalizationsDelegatesandsupportedLocalesintoGetMaterialApp. Consider adding all supported locales (e.g., Hindi) here to fully exercise multilingual behavior.lib/ui/views/profile/edit_profile_view.dart (1)
42-48: Remove controller listeners before disposing
_countryController/_instituteControllerhave active listeners added ininitState, but they are not removed indispose. Disposing the controller implicitly removes them, yet callingremoveListenerfirst is the documented pattern and avoids surprises if the controller stays alive longer than the widget (e.g., if passed somewhere).void dispose() { _nameFocusNode.dispose(); _countryFocusNode.dispose(); _instituteFocusNode.dispose(); - _countryController.dispose(); - _instituteController.dispose(); + _countryController.removeListener(_onCountryChanged); + _countryController.dispose(); + _instituteController.removeListener(_onInstituteChanged); + _instituteController.dispose(); super.dispose(); }test/ui_tests/groups/assignment_details_view_test.dart (1)
64-71: Use the samesupportedLocaleslist as production codeHard-coding
[Locale('en', '')]means the test will break if the default locale is ever changed or additional locales are introduced. Re-use the generated list for resilience.- supportedLocales: const [Locale('en', '')], + supportedLocales: AppLocalizations.supportedLocales,lib/ui/views/groups/edit_group_view.dart (1)
74-81: Minor: cacheAppLocalizationsinsidebuild
AppLocalizations.of(context)!is invoked several times in a tight scope. Storing it in a local variable improves readability and micro-performance.final l10n = AppLocalizations.of(context)!; CVSubheader( title: l10n.edit_group.toUpperCase(), subtitle: l10n.edit_group_description, ), ... label: l10n.group_name, ... title: l10n.save,test/ui_tests/groups/group_details_view_test.dart (1)
70-75: Add the default Flutter localisation delegatesOnly
FlutterQuillLocalizationsandAppLocalizationsare registered. While this works for most text, widgets such as date pickers rely onGlobalMaterialLocalizations. Including the full default set mirrors production and avoids silent fallback to English.localizationsDelegates: [ ...FlutterQuillLocalizations.localizationsDelegates, AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, ],lib/ui/views/profile/profile_view.dart (2)
81-93: Colon & fallback logic should be part of the translation, not hard-coded
By concatenating'$title : 'and appending the fallback"N.A"you lose flexibility for RTL languages and for languages where the order/spacing differs. Consider moving the entire pattern (e.g.'joined_label_with_value') to the.arbfile and using{value}interpolation.
190-204: Minor performance / readability – fetch localisation once
Inside the same build scope you invokeAppLocalizations.of(context)!six times. Cache it once to a localfinal l10n = AppLocalizations.of(context)!;before the widgets are built to avoid redundant look-ups and improve readability.lib/ui/views/authentication/signup_view.dart (3)
55-67: Validation strings split across widgets – potential word-order issues
_buildNameInputsplits the label and the error message, but both are simple strings. For languages with different grammar you may need the full sentence/localised pattern (e.g. “Veuillez saisir votre nom”). Prefer a single translation key with placeholders rather than assembling fragments.
118-131: “Already registered?” sentence assembled manually
The question and the call-to-action are built with two separate localisations which forces the English word order on every language. Provide a single key likealready_registered_loginwith a{login}placeholder or consider aRichTextbuilder that inserts the styled “Login” inside the translated sentence.
145-149: Hard-coded generic “error” title could be more specific
You always show the titleerrorfor any signup failure. If you already have localisation keys, add specific ones (e.g.signup_error_title) so translators can adapt tone/context.Also applies to: 154-154
lib/main.dart (1)
38-40:delegatesmutation is unnecessary
You copy the list returned byAppLocalizations.localizationsDelegatesonly to append one delegate. A simpler and const-safe approach:localizationsDelegates: [ ...AppLocalizations.localizationsDelegates, FlutterQuillLocalizations.delegate, ],test/ui_tests/profile/user_projects_view_test.dart (1)
58-71: Localization boilerplate duplicated across tests
Each widget test now repeats the delegates/locale boilerplate. Extract a helper (e.g.TestApp.wrap(widget)) to centralise this, reduce noise and prevent divergence when supported locales change.lib/ui/views/projects/project_details_view.dart (3)
245-256: Repeated modal copy → extract keys & re-use
All dialog/snackbar titles (forking,deleting, etc.) repeat identical pattern of “ing”. Consider a generic progress message key with a{action}placeholder to cut ARB bloat.
439-443: Row children could beconst
Both theIconand the translatedTextare rebuilt every frame. TheIconcan beconst, and you can cachel10nas shown earlier. Minor performance win.
195-199: Heading + colon pattern limits localisation
'${l10n.author} : 'forces the colon and spacing. Prefer including the delimiter inside the translated string or use interpolation ({author}: {value}) to support RTL languages. Applies to several similar headings.test/ui_tests/authentication/forgot_password_view_test.dart (2)
1-3: File name no longer matches the content – please rename for clarityThe file is still called
forgot_password_view_test.dartbut now exclusively exercisesLoginView. This mis-match makes grepping / IDE look-ups confusing and will trip up new contributors.
30-43: Consider declaring thelocale:explicitly in every test for determinismOther test files pin the locale to
const Locale('en', ''); doing the same here avoids flakiness when the host machine default is not English.test/ui_tests/groups/edit_group_view_test.dart (2)
40-50: Locale not fixed – tests will depend on host language
supportedLocalesis set but no explicitlocale:is supplied. On a non-English CI runner the first supported locale may not be chosen and the test will fail.- supportedLocales: const [Locale('en', '')], + supportedLocales: const [Locale('en', '')], + locale: const Locale('en', ''),
64-67: Minor: reuse helper that already exposes localisationsYou fetch localisations twice across tests; consider exposing a small helper (
_loc(WidgetTester)) to DRY things up. Totally optional.test/ui_tests/groups/add_assignment_view_test.dart (1)
42-51: Consistency: pin the localeSame comment as in the previous file – add
locale: const Locale('en', '')for deterministic runs.test/ui_tests/profile/profile_view_test.dart (1)
73-77: Missing core localisation delegatesOnly
AppLocalizations.delegateis provided. Material / Cupertino widgets fall back to English but may emit warnings; add the global delegates for completeness (mirrors other test files).- localizationsDelegates: [ - AppLocalizations.delegate, - ], + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ],test/ui_tests/home/home_view_test.dart (1)
32-39: Deterministic localeAs elsewhere, explicitly set
locale:to keep the test immune from host settings.- supportedLocales: AppLocalizations.supportedLocales, + supportedLocales: AppLocalizations.supportedLocales, + locale: const Locale('en', ''),lib/controllers/launguage_controller.dart (1)
8-12: Redundant state mutation.
changeLanguagealready setsisLanguageExpanded.value = false; the tap-handler in the drawer sets the same flag immediately afterwards (cv_drawer.dart lines 110-111). One of them can be removed.test/ui_tests/groups/new_group_view_test.dart (1)
50-57: Brittle context lookup – usetester.element(find.byType(GetMaterialApp)).
GetMaterialAppis the root widget;MaterialAppmay disappear if GetX changes its internals. Grab the context directly fromGetMaterialAppto future-proof the test.- final materialAppFinder = find.byType(MaterialApp); + final materialAppFinder = find.byType(GetMaterialApp);lib/ui/components/cv_drawer.dart (2)
80-82: Hard-coded language names defeat localization.
'English'and'हिंदी'are baked in. Users running the app in French will still see these labels in English/Hindi. Either:
- Add self-referential keys (
language_english,language_hindi) to each ARB file, or- Derive the display name from
Localeusingintl’stoLanguageTag()/LocaleNames.
108-112: Duplicate collapse call.
langController.changeLanguagealready setsisLanguageExpanded.value = false; the extra assignment here is unnecessary.- langController.changeLanguage(entry.key); - langController.isLanguageExpanded.value = false; + langController.changeLanguage(entry.key);lib/ui/views/groups/group_details_view.dart (1)
268-274: Per-call map creation is wasteful – switch on the enum instead.Creating a new
Mapevery time_buildSubHeaderis called is minor but avoidable. A simpleswitch(or an enum) is clearer and allocation-free.- final title = { - 'mentors': AppLocalizations.of(context)!.mentors, - 'members': AppLocalizations.of(context)!.members, - 'assignments': AppLocalizations.of(context)!.assignments, - }[titleKey] ?? titleKey; + late final String title; + switch (titleKey) { + case 'mentors': + title = AppLocalizations.of(context)!.mentors; + break; + case 'members': + title = AppLocalizations.of(context)!.members; + break; + case 'assignments': + title = AppLocalizations.of(context)!.assignments; + break; + default: + title = titleKey; + }test/ui_tests/authentication/login_view_test.dart (2)
36-45: Prefer the canned delegate/locale lists to avoid duplication
AppLocalizationsalready exposeslocalizationsDelegatesandsupportedLocales. Re-using them keeps every test in sync with future delegate additions and removes boiler-plate.- localizationsDelegates: const [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: const [Locale('en', '')], + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales,
117-141: Small DRY win: capturelocalizationsonceEach test re-derives the
localizationsinstance. Return it from_pumpLoginView(or store it in a test-level variable) to trim repetition.test/ui_tests/groups/my_groups_view_test.dart (1)
64-66: Use the same one-liner for delegates/locales as other testsKeep the setup uniform across the test-suite and future-proof against new delegates.
- localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales,test/ui_tests/profile/edit_profile_view_test.dart (1)
60-65: LeverageAppLocalizations.localizationsDelegatesMinor: the constant already bundles the core Flutter delegates, keeping the list shorter (and consistent).
test/ui_tests/ib/ib_landing_view_test.dart (1)
62-67: Duplicate stubbing ofIB_FETCH_PAGE_DATAThe same getter is stubbed twice; the second call is redundant.
test/ui_tests/authentication/signup_view_test.dart (2)
21-22: Mismatch between file name and test groupThe file is
signup_view_test.dartbut the group is"LoginViewTest -"– this is confusing when scanning test output.
36-45: Delegate list duplicationAs in other files, prefer the constants offered by
AppLocalizations.test/ui_tests/groups/update_assignment_view_test.dart (2)
51-51: Usefinalinstead ofvarfor immutable variablesFor consistency with Dart best practices, consider using
finalfor variables that won't be reassigned.-var assignment = Assignment.fromJson(mockAssignment); +final assignment = Assignment.fromJson(mockAssignment);
118-119: Usefinalfor mock service declarationsFor consistency and to follow Dart conventions, use
finalfor these mock service variables.-var dialogService = MockDialogService(); +final dialogService = MockDialogService(); locator.registerSingleton<DialogService>(dialogService); // Mock UpdateAssignment ViewModel -var updateAssignmentViewModel = MockUpdateAssignmentViewModel(); +final updateAssignmentViewModel = MockUpdateAssignmentViewModel(); locator.registerSingleton<UpdateAssignmentViewModel>( updateAssignmentViewModel, );Also applies to: 127-130
lib/gen_l10n/app_localizations_hi.dart (1)
908-912: Minor linguistic nit
'लॉग आउट करे'/'एक्स्प्लोर करे'– consider using the polite imperative “करें” to match the rest of the UI tone ('लॉग आउट करें','एक्सप्लोर करें').
Purely cosmetic; ignore if style-guide differs.Also applies to: 926-930
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (50)
lib/controllers/launguage_controller.dart(1 hunks)lib/gen_l10n/app_localizations.dart(2 hunks)lib/gen_l10n/app_localizations_en.dart(2 hunks)lib/gen_l10n/app_localizations_hi.dart(2 hunks)lib/l10n/app_en.arb(2 hunks)lib/l10n/app_hi.arb(2 hunks)lib/main.dart(3 hunks)lib/ui/components/cv_drawer.dart(5 hunks)lib/ui/views/authentication/forgot_password_view.dart(5 hunks)lib/ui/views/authentication/login_view.dart(8 hunks)lib/ui/views/authentication/signup_view.dart(7 hunks)lib/ui/views/contributors/contributors_view.dart(2 hunks)lib/ui/views/groups/add_assignment_view.dart(10 hunks)lib/ui/views/groups/assignment_details_view.dart(16 hunks)lib/ui/views/groups/edit_group_view.dart(5 hunks)lib/ui/views/groups/group_details_view.dart(17 hunks)lib/ui/views/groups/my_groups_view.dart(4 hunks)lib/ui/views/groups/new_group_view.dart(5 hunks)lib/ui/views/groups/update_assignment_view.dart(8 hunks)lib/ui/views/home/home_view.dart(5 hunks)lib/ui/views/ib/ib_landing_view.dart(1 hunks)lib/ui/views/ib/ib_page_view.dart(17 hunks)lib/ui/views/notifications/notifications_view.dart(6 hunks)lib/ui/views/profile/edit_profile_view.dart(9 hunks)lib/ui/views/profile/profile_view.dart(8 hunks)lib/ui/views/profile/user_favourites_view.dart(2 hunks)lib/ui/views/profile/user_projects_view.dart(2 hunks)lib/ui/views/projects/edit_project_view.dart(9 hunks)lib/ui/views/projects/featured_projects_view.dart(4 hunks)lib/ui/views/projects/project_details_view.dart(24 hunks)lib/ui/views/teachers/teachers_view.dart(2 hunks)test/ui_tests/authentication/forgot_password_view_test.dart(5 hunks)test/ui_tests/authentication/login_view_test.dart(3 hunks)test/ui_tests/authentication/signup_view_test.dart(2 hunks)test/ui_tests/contributors/contributors_view_test.dart(3 hunks)test/ui_tests/groups/add_assignment_view_test.dart(5 hunks)test/ui_tests/groups/assignment_details_view_test.dart(4 hunks)test/ui_tests/groups/edit_group_view_test.dart(5 hunks)test/ui_tests/groups/group_details_view_test.dart(6 hunks)test/ui_tests/groups/my_groups_view_test.dart(6 hunks)test/ui_tests/groups/new_group_view_test.dart(4 hunks)test/ui_tests/groups/update_assignment_view_test.dart(3 hunks)test/ui_tests/home/home_view_test.dart(3 hunks)test/ui_tests/ib/ib_landing_view_test.dart(1 hunks)test/ui_tests/ib/ib_page_view_test.dart(1 hunks)test/ui_tests/profile/edit_profile_view_test.dart(5 hunks)test/ui_tests/profile/profile_view_test.dart(3 hunks)test/ui_tests/profile/user_favourites_view_test.dart(3 hunks)test/ui_tests/profile/user_projects_view_test.dart(3 hunks)test/ui_tests/teachers/teachers_view_test.dart(3 hunks)
🧰 Additional context used
🪛 RuboCop (1.75.5)
lib/l10n/app_en.arb
[warning] 91-91: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 93-93: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 94-94: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 99-99: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 100-100: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 103-103: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 105-105: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 107-107: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 108-108: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 109-109: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 111-111: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 114-114: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 117-117: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 122-122: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 123-123: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 132-132: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 137-137: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 145-145: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 162-162: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 169-169: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 172-172: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 179-179: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 183-183: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 187-187: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 204-204: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 239-239: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 243-243: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 251-251: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 254-254: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 255-255: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 256-256: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 258-258: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 259-259: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 261-261: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 262-262: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 263-263: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 265-265: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 266-266: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 269-269: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 287-287: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 288-288: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 289-289: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 303-303: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 304-304: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 305-305: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 307-307: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 313-313: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 314-314: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 324-324: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 326-326: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 327-327: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 333-333: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 334-334: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 335-335: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 336-336: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 337-337: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 338-338: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 339-339: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 340-340: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 341-341: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 342-342: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 343-343: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 344-344: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 345-345: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 346-346: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 347-347: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 348-348: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 349-349: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 350-350: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 351-351: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 352-352: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 353-353: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 354-354: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 355-355: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 356-356: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 357-357: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 358-358: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 359-359: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 360-360: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 361-361: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 362-362: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 363-363: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 364-364: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 365-365: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 366-366: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 373-373: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 374-374: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 376-376: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 377-377: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 389-389: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 400-400: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
lib/l10n/app_hi.arb
[warning] 91-91: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 93-93: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 94-94: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 99-99: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 100-100: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 103-103: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 105-105: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 107-107: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 108-108: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 109-109: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 111-111: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 114-114: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 117-117: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 122-122: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 123-123: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 132-132: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 144-144: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 146-146: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 154-154: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 171-171: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 178-178: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 181-181: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 188-188: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 192-192: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 196-196: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 213-213: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 248-248: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 252-252: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 260-260: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 263-263: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 264-264: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 265-265: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 267-267: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 268-268: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 270-270: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 271-271: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 272-272: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 274-274: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 275-275: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 278-278: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 279-279: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 280-280: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 281-281: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 282-282: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 283-283: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 284-284: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 285-285: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 286-286: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 287-287: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 296-296: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 297-297: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 298-298: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 312-312: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 313-313: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 314-314: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 316-316: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 322-322: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 323-323: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 333-333: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 335-335: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 342-342: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 343-343: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 345-345: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 346-346: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 358-358: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 369-369: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: build (macos-latest)
- GitHub Check: build (windows-latest)
- GitHub Check: build (ubuntu-latest)
🔇 Additional comments (34)
lib/ui/views/home/home_view.dart (1)
70-89: LGTM – localization applied cleanlyThe buttons now source their titles from
AppLocalizations; implementation stays simple and readable.test/ui_tests/contributors/contributors_view_test.dart (1)
28-36: Tests are now localization-aware – good additionIncluding the delegates and fixing the locale ensures widgets render predictable text during testing.
lib/ui/views/teachers/teachers_view.dart (3)
2-2: Add localization import
Importedapp_localizations.dartto enable use ofAppLocalizations.of(context).
20-25: Enable dynamic localization by removingconst
Changing thechildrenlist from aconstto a non-const list is necessary for runtime calls toAppLocalizations.of(context)so that the UI updates when the locale changes.
28-31: ```shell
#!/bin/bashCorrectly verify teacher feature keys in ARB files
Search only .arb files under lib/l10n for the required patterns
grep -R -n --include *.arb 'teachers_feature[1-4]_(title|description)' lib/l10n
</details> <details> <summary>lib/ui/views/ib/ib_landing_view.dart (3)</summary> `14-14`: **Import localization package** Added `app_localizations.dart` import to enable localized string lookups via `AppLocalizations.of(context)`. --- `260-260`: **Localize drawer menu entries** Drawer tile titles now use the localization keys `return_home`, `ib_home`, and `loading_chapters`. Confirm these keys and translations in each locale. Also applies to: 268-268, 282-282 --- `76-76`: **Localize AppBar and Showcase text** Replaced hardcoded hints and descriptions in the search bar and Showcases with the following localization keys: - `search_circuitverse` - `navigate_chapters` - `circuitverse` / `interactive_book` - `show_toc` Validate that these keys exist in all ARB files. ```shell #!/bin/bash keys=(search_circuitverse navigate_chapters circuitverse interactive_book show_toc) for key in "${keys[@]}"; do rg "\"$key\"" -n lib/l10n doneAlso applies to: 136-136, 148-149, 164-164
lib/ui/views/profile/user_favourites_view.dart (3)
11-11: Import localization package
Addedapp_localizations.dartimport to support localized UI strings in the empty state.
16-16: Add static route identifier
Introducedstatic const String id = 'user_favourites_view';for consistent navigation usage.
37-38: ```shell
#!/bin/bashSearch ARB files for the new localization keys
rg 'no_favorites_title' -n lib/l10n
rg 'no_favorites_subtitle' -n lib/l10n</details> <details> <summary>test/ui_tests/ib/ib_page_view_test.dart (1)</summary> `17-18`: **Import localization in tests** Added imports for `app_localizations` and `flutter_localizations` so widgets under test can resolve localized strings. </details> <details> <summary>lib/ui/views/projects/featured_projects_view.dart (3)</summary> `13-13`: **Import localization package** Added `app_localizations.dart` import to enable `AppLocalizations.of(context)` in this view. --- `56-57`: **Dispose text controllers properly** You’ve correctly added `_controller.dispose();` alongside `ScrollController.dispose()` in `dispose()`. This prevents potential memory leaks. --- `113-114`: **Localize Feature Projects UI strings** Replaced hardcoded UI text with localization keys: - `no_result_found` - `editor_picks` / `editor_picks_description` - `featured_circuits` - `search_for_circuits` Ensure these keys are present in all ARB files. ```shell #!/bin/bash keys=(no_result_found editor_picks editor_picks_description featured_circuits search_for_circuits) for key in "${keys[@]}"; do rg "\"$key\"" -n lib/l10n doneAlso applies to: 125-128, 139-140, 160-160
lib/ui/views/profile/user_projects_view.dart (1)
35-37: 👍 Localised empty-state copy – looks goodHard-coded strings have been replaced by
AppLocalizationskeys. The change is correct and aligns with the PR’s i18n objective.lib/main.dart (1)
23-23: Controller is never disposed – potential memory leak
Get.put(LanguageController())registers a permanent instance. If it owns streams or listeners consider usingGet.put(LanguageController(), permanent: true)explicitly orGet.lazyPutwithfenixto clarify intent, and ensure you close any streams insideonClose.test/ui_tests/profile/profile_view_test.dart (1)
95-98: Assertion looseness could mask regressions
findsAtLeastNWidgets(4)onRichTextis very coarse. Consider asserting specific keys or semantics to keep the test valuable.lib/ui/views/groups/assignment_details_view.dart (2)
110-120: Extract the title-map to a static/const to avoid per-frame allocations
_buildDetailComponentdeclares a literal map on every build – that map is recreated every time the widget tree rebuilds, which happens frequently in Flutter. Move the map to astatic const(or convert the logic to aswitch) so that only one instance is allocated.
[ suggest_optional_refactor ]- final title = { - 'name': AppLocalizations.of(context)!.name, - ... - }[titleKey] ?? titleKey; + static const _titleKeys = ['name', 'deadline', 'time_remaining', 'restricted_elements']; + final title = switch (titleKey) { + 'name' => AppLocalizations.of(context)!.name, + 'deadline' => AppLocalizations.of(context)!.deadline, + 'time_remaining' => AppLocalizations.of(context)!.time_remaining, + 'restricted_elements' => AppLocalizations.of(context)!.restricted_elements, + _ => titleKey, + };
484-487: Pluralisation is not locale-aware
'${_remainingTime.inDays} ${AppLocalizations.of(context)!.days} …'hard-codes the plural form. Languages with complex plural rules (e.g., Russian) will render incorrectly. Consider using theIntl.pluralAPI or add separate pluralised ARB keys.
[ request_verification ]lib/ui/views/notifications/notifications_view.dart (2)
95-106:model.valuestill stores a localised string → breaks after a language switch
Although you introducedallValue / unreadValue,onChangedwrites the display text back intomodel.value, and later comparisons (lines 98, 164) again rely on the localised string.
If the user changes the app language,model.valuewill no longer match the new localisation, so filters & badge counters silently break.Store the stable enum/constant instead and only convert for UI:
[ raise_critical_issue ]-// state -class NotificationsViewModel { String value = 'all'; } +class NotificationsViewModel { String filter = 'all'; } -// dropdown value binding -value: model.filter, +value: model.filter, -// onChanged -model.value = value; +model.filter = value; -// comparisons -hasNoUnread = model.filter == unreadValue; -... -.where((_) => model.filter != unreadValue || n.attributes.unread) +hasNoUnread = model.filter == unreadValue; +... +.where((_) => model.filter != unreadValue || n.attributes.unread)Follow-up: audit the view-model so that persisted state survives locale changes.
130-151: Redundant mapping logic
Oncemodel.filterholds the raw'all' | 'unread'token, the ternaries converting back and forth (model.value == allDisplayText ? allValue …) become unnecessary.
[ suggest_essential_refactor ]lib/ui/views/groups/update_assignment_view.dart (2)
55-64: Validator duplicates localisation lookup
Minor: you can avoid two lookups by caching the localised error once. Not blocking.
[ suggest_nitpick ]
213-215: Good use of localised progress dialog
The busy-state message is now locale aware. Nice catch.lib/l10n/app_hi.arb (1)
90-110: Duplicate keys in ARB break generation & override earlier values
Static analysis flags >40 duplicated keys (error,login,no_image_source,image_load_error, …).
The ARB is valid JSON so the last occurrence silently wins, but Flutter’sgen-l10nprints warnings and drops earlier strings, which may hide regressions.Deduplicate the file: keep a single entry per key, strip the rest, and regenerate localisation classes.
[ raise_critical_issue ]Also applies to: 290-299
lib/ui/components/cv_drawer.dart (1)
2-2: Import path inherits the “launguage” typo.After renaming the controller file (see earlier comment) this import must be updated as well.
test/ui_tests/groups/my_groups_view_test.dart (1)
90-98: Hard-coded"Total Members: 1"bypasses i18nLocalising every visible string is the whole point of this PR. Please expose/consume a
total_memberskey (e.g.localizations.total_members(1)) and adapt the test accordingly.test/ui_tests/profile/edit_profile_view_test.dart (1)
79-83: Assertion may be too rigidThe view could legitimately gain another
CVTextFieldwithout breaking functionality. Consider usingfindsAtLeastNWidgetsor asserting on specific keys instead.test/ui_tests/groups/update_assignment_view_test.dart (2)
41-49: Good practice: Service cleanup in tearDownExcellent addition of the tearDown method to unregister services. This prevents state leakage between tests and ensures proper test isolation.
57-75: Well-implemented localization setupThe localization configuration is properly implemented with all necessary delegates and the pattern of retrieving localizations after the widget is built is correct.
lib/ui/views/groups/add_assignment_view.dart (2)
44-66: Correct implementation of context-dependent initializationGood use of
didChangeDependencieswith the_isInitializedflag to ensure localization-dependent variables are initialized only once when the context is available. This is the proper pattern for accessing context-dependent resources.
58-64: Smart approach for grading options localizationUsing a map with stable keys and localized display values is an excellent approach. This maintains data integrity while providing localized UI strings.
lib/ui/views/ib/ib_page_view.dart (2)
163-168: Excellent design for localized image builderGreat approach to parameterize the
CustomImageBuilderwith localized strings. This makes the component reusable while supporting different locales. The default values ensure backward compatibility.Also applies to: 526-536
237-242: Properly localized UI elementsAll user-facing strings including copyright notice, table of contents, and navigation hints have been correctly replaced with localized versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🔭 Outside diff range comments (1)
lib/l10n/app_en.arb (1)
374-377: Duplicate key with different meaning
"interactive_book"exists earlier at line 276 and again here. Consolidate to a single key to avoid future confusion.
🧹 Nitpick comments (7)
lib/controllers/language_controller.dart (2)
8-12: Persist the selected locale between app launches
currentLocaleis only stored in memory; the user’s choice will be lost on every fresh run or hot-restart.
Persist the code usingGetStorage,SharedPreferences, or any other preferred cache so thatLanguageController.onInit()can restore the saved locale before the first frame is built.This avoids a jarring flash of the default language and improves UX.
14-16: Use the built-intoggle()helper for brevityMinor nit – RxBool already exposes
toggle(), so this can be simplified:-void toggleExpansion() { - isLanguageExpanded.value = !isLanguageExpanded.value; -} +void toggleExpansion() => isLanguageExpanded.toggle();lib/gen_l10n/app_localizations_en.dart (3)
40-45: Grammar – duplicate “be be” & smoother wording needed
feature2_descriptioncurrently reads:
“... and can be easily be made into a subcircuit.” – duplicated “be” and awkward phrasing.- 'Automatically generate circuit based on truth table data. This is great to create complex logic circuits and can be easily be made into a subcircuit.'; + 'Automatically generate circuits from truth-table data. This is great for creating complex logic circuits and can easily be turned into a sub-circuit.';Update the key in
app_en.arband re-run the l10n generator so the change persists.
104-107: Wording – “in build preview” & stray comma“in build preview” → “in-built preview” (or “built-in preview”).
Extra comma after “student”.- 'Grade assignments very easily with the in build preview. Simply select the student, to his/her assignment work.'; + 'Grade assignments easily with the built-in preview. Simply select a student to view their assignment.';Correct in the ARB source, then regenerate.
61-65: Capitalisation & hyphenationUI copy is more polished if we use title-case and hyphenate compound adjectives:
- String get feature5_title => 'Multi Bit Buses and components'; + String get feature5_title => 'Multi-bit Buses and Components';(Same adjustment may be needed for the matching Hindi string.)
lib/gen_l10n/app_localizations.dart (2)
64-100: Generated file should ideally be excluded from source control
app_localizations.dartis produced byflutter gen-l10n. Committing this 2 500-line artefact bloats diffs and creates frequent merge conflicts.
Consider committing only the.arbresource files and addinglib/gen_l10n/**to.gitignore; let CI/regeneration recreate it when building.
305-310: Minor nit – prefer title-case for visible strings
Slack channelis exposed to the UI through this key; considerSlack Channelfor consistency with other title-case labels likeEmail IDs,Terms Of Service, etc.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
lib/controllers/language_controller.dart(1 hunks)lib/gen_l10n/app_localizations.dart(2 hunks)lib/gen_l10n/app_localizations_en.dart(2 hunks)lib/gen_l10n/app_localizations_hi.dart(2 hunks)lib/l10n/app_en.arb(2 hunks)lib/l10n/app_hi.arb(2 hunks)lib/main.dart(3 hunks)lib/ui/components/cv_drawer.dart(5 hunks)lib/ui/views/profile/profile_view.dart(8 hunks)test/ui_tests/authentication/forgot_password_view_test.dart(5 hunks)test/ui_tests/authentication/signup_view_test.dart(2 hunks)test/ui_tests/groups/my_groups_view_test.dart(6 hunks)test/ui_tests/ib/ib_landing_view_test.dart(1 hunks)test/ui_tests/profile/edit_profile_view_test.dart(5 hunks)test/ui_tests/profile/profile_view_test.dart(3 hunks)test/ui_tests/profile/user_favourites_view_test.dart(2 hunks)test/ui_tests/profile/user_projects_view_test.dart(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- test/ui_tests/profile/user_favourites_view_test.dart
🚧 Files skipped from review as they are similar to previous changes (10)
- lib/main.dart
- test/ui_tests/profile/user_projects_view_test.dart
- lib/ui/views/profile/profile_view.dart
- test/ui_tests/profile/profile_view_test.dart
- test/ui_tests/profile/edit_profile_view_test.dart
- test/ui_tests/authentication/signup_view_test.dart
- test/ui_tests/authentication/forgot_password_view_test.dart
- test/ui_tests/ib/ib_landing_view_test.dart
- test/ui_tests/groups/my_groups_view_test.dart
- lib/ui/components/cv_drawer.dart
🧰 Additional context used
🪛 RuboCop (1.75.5)
lib/l10n/app_hi.arb
[warning] 91-91: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 94-94: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 95-95: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 97-97: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 100-100: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 101-101: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 104-104: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 106-106: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 108-108: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 109-109: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 110-110: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 112-112: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 115-115: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 118-118: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 123-123: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 124-124: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 133-133: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 145-145: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 147-147: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 155-155: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 172-172: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 179-179: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 182-182: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 189-189: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 193-193: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 197-197: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 214-214: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 249-249: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 253-253: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 261-261: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 264-264: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 265-265: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 266-266: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 268-268: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 269-269: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 271-271: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 272-272: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 273-273: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 275-275: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 276-276: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 279-279: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 280-280: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 281-281: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 282-282: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 283-283: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 284-284: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 285-285: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 286-286: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 287-287: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 288-288: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 297-297: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 311-311: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 312-312: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 313-313: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 315-315: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 321-321: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 322-322: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 332-332: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 334-334: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 341-341: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 342-342: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 344-344: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 345-345: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 357-357: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 368-368: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
lib/l10n/app_en.arb
[warning] 91-91: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 94-94: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 95-95: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 97-97: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 100-100: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 101-101: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 104-104: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 106-106: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 108-108: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 109-109: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 110-110: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 112-112: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 115-115: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 118-118: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 123-123: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 124-124: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 133-133: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 138-138: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 146-146: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 163-163: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 170-170: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 173-173: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 180-180: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 184-184: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 188-188: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 205-205: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 240-240: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 244-244: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 252-252: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 255-255: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 256-256: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 257-257: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 259-259: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 260-260: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 262-262: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 263-263: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 264-264: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 266-266: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 267-267: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 270-270: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 288-288: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 302-302: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 303-303: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 304-304: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 306-306: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 312-312: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 313-313: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 323-323: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 325-325: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 332-332: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 334-334: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 346-346: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 357-357: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: build (macos-latest)
- GitHub Check: build (ubuntu-latest)
- GitHub Check: build (windows-latest)
🔇 Additional comments (8)
lib/l10n/app_en.arb (2)
360-363: Apostrophe in key violates recommended naming style
"editor_picks": "Editor's Picks"contains an apostrophe in the value – ok – but the key is fine. No action.
1-480: Automate duplicate check for all ARB filesRun once per CI to avoid regressions.
#!/bin/bash # List duplicate keys in every ARB file fd .arb -x jq -r 'keys[]' {} | sort | uniq -dlib/l10n/app_hi.arb (1)
375-388: Inconsistent casing in navigation keys
"explore": "एक्स्प्लोर करे"vs English"explore": "Explore"– good.
No issues.lib/gen_l10n/app_localizations_en.dart (1)
1-1280: Generated file – avoid manual editsRemember that this file is auto-generated by Flutter’s l10n tool. Any direct edits will be overwritten next generation cycle. Make your changes in the corresponding
app_en.arb(andapp_hi.arb) files instead, then runflutter gen-l10n.lib/gen_l10n/app_localizations_hi.dart (1)
11-24: Strings look consistent – no issues spottedAll visible corrections made earlier (Hindi in English file) are now resolved, and Hindi translations are consistently applied here.
lib/gen_l10n/app_localizations.dart (3)
101-112: Looks good – previous Hindi-in-English pollution no longer presentVerified that the sample value for
home_header_title/_subtitleis proper English.
Nice fix to the earlier ARB pollution problem.
2435-2468: Sanity-check ARB files for stray Devanagari charactersGiven the earlier mix-up, run an automated scan to ensure no Hindi text slipped back into the English resources.
#!/bin/bash # Detect any Devanagari characters in English ARB or generated Dart rg --pcre2 "[\p{Devanagari}]" lib/l10n/app_en.arb lib/gen_l10n/app_localizations_en.dart
1883-1888: New keylanguageadded – remember to use it consistentlyThe getter is defined, but any hard-coded “Language” strings elsewhere (drawer, settings, tests) should now reference this key to stay localisable.
Generate a quick sweep to find leftovers:
#!/bin/bash # Find hard-coded occurrences of the word Language outside localisation files rg --ignore-case --fixed-strings "Language" --glob "!lib/l10n/**"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (7)
lib/l10n/app_en.arb (1)
1-503: Consider a naming convention to avoid future collisionsWith ~500 lines in a single ARB file, accidental duplicates are inevitable. Adopting a prefix strategy (
viewName_keyName) or grouping shared strings in a dedicated section will keep the file maintainable and prevent CI breakages.No immediate code change required, but worth planning before more locales are added.
lib/l10n/app_hi.arb (1)
1-501: Adopt a unique-key policy across viewsGiven the size of the ARB file and the two-language setup, a strict naming convention (e.g.
view_key) will prevent duplicate-key regressions and ease translator tooling.lib/gen_l10n/app_localizations_en.dart (3)
105-107: Fix grammar in assignment-grading description“In build preview” → “built-in preview”,
“select the student, to his/her assignment work” → “select the student to view his/her assignment work”.- 'Grade assignments very easily with the in build preview. Simply select the student, to his/her assignment work.'; + 'Grade assignments very easily with the built-in preview. Simply select the student to view his/her assignment work.';
368-370: Minor wording improvement“Enable elements restriction” reads a little awkwardly; “Enable element restriction” (singular) is what users usually expect.
- 'Enable elements restriction'; + 'Enable element restriction';
52-59: Capitalisation inconsistencyIn “Multi Bit Buses and components” the second word of the pair is not capitalised, unlike the first. Consider:
- String get feature5_title => 'Multi Bit Buses and components'; + String get feature5_title => 'Multi-Bit Buses and Components';lib/gen_l10n/app_localizations.dart (1)
1-12: Generated file should be kept out of version control
app_localizations.dartis produced byflutter gen-l10nand will be regenerated on every localization change. Committing this 2 600-line artifact bloats the repo and causes noisy diffs.
Move the entirelib/gen_l10n/*directory to.gitignoreand regenerate it in CI / local builds instead.lib/gen_l10n/app_localizations_hi.dart (1)
1-4: Import can be dropped – theintlpackage is never referencedThe generated file adds
intlonly to immediately silence the “unused import” warning.
Since nointlAPIs are used (all getters return hard-coded strings), simply remove the import; the analyzer directive then becomes unnecessary.-// ignore: unused_import -import 'package:intl/intl.dart' as intl; +// No external APIs needed
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (37)
lib/gen_l10n/app_localizations.dart(1 hunks)lib/gen_l10n/app_localizations_en.dart(1 hunks)lib/gen_l10n/app_localizations_hi.dart(1 hunks)lib/l10n/app_en.arb(1 hunks)lib/l10n/app_hi.arb(1 hunks)lib/main.dart(3 hunks)lib/ui/components/cv_drawer.dart(8 hunks)lib/ui/views/authentication/forgot_password_view.dart(5 hunks)lib/ui/views/authentication/login_view.dart(8 hunks)lib/ui/views/authentication/signup_view.dart(7 hunks)lib/ui/views/cv_landing_view.dart(1 hunks)lib/ui/views/groups/add_assignment_view.dart(10 hunks)lib/ui/views/groups/assignment_details_view.dart(16 hunks)lib/ui/views/groups/edit_group_view.dart(5 hunks)lib/ui/views/groups/group_details_view.dart(17 hunks)lib/ui/views/groups/my_groups_view.dart(4 hunks)lib/ui/views/groups/new_group_view.dart(5 hunks)lib/ui/views/groups/update_assignment_view.dart(8 hunks)lib/ui/views/ib/ib_landing_view.dart(1 hunks)lib/ui/views/ib/ib_page_view.dart(17 hunks)lib/ui/views/notifications/notifications_view.dart(6 hunks)lib/ui/views/profile/edit_profile_view.dart(9 hunks)lib/ui/views/profile/profile_view.dart(8 hunks)lib/ui/views/projects/edit_project_view.dart(9 hunks)lib/ui/views/projects/featured_projects_view.dart(4 hunks)lib/ui/views/projects/project_details_view.dart(24 hunks)lib/viewmodels/cv_landing_viewmodel.dart(1 hunks)test/ui_tests/authentication/login_view_test.dart(3 hunks)test/ui_tests/authentication/signup_view_test.dart(2 hunks)test/ui_tests/groups/add_assignment_view_test.dart(5 hunks)test/ui_tests/groups/assignment_details_view_test.dart(4 hunks)test/ui_tests/groups/edit_group_view_test.dart(5 hunks)test/ui_tests/groups/group_details_view_test.dart(6 hunks)test/ui_tests/groups/my_groups_view_test.dart(6 hunks)test/ui_tests/groups/new_group_view_test.dart(4 hunks)test/ui_tests/groups/update_assignment_view_test.dart(3 hunks)test/ui_tests/ib/ib_landing_view_test.dart(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- lib/ui/views/cv_landing_view.dart
- lib/viewmodels/cv_landing_viewmodel.dart
- lib/ui/views/groups/add_assignment_view.dart
🚧 Files skipped from review as they are similar to previous changes (29)
- lib/ui/views/groups/my_groups_view.dart
- lib/ui/views/authentication/login_view.dart
- lib/ui/views/authentication/forgot_password_view.dart
- lib/ui/views/projects/edit_project_view.dart
- lib/ui/views/projects/featured_projects_view.dart
- lib/ui/views/groups/new_group_view.dart
- test/ui_tests/groups/assignment_details_view_test.dart
- lib/ui/views/ib/ib_landing_view.dart
- lib/ui/views/projects/project_details_view.dart
- test/ui_tests/groups/add_assignment_view_test.dart
- test/ui_tests/groups/group_details_view_test.dart
- lib/ui/views/groups/update_assignment_view.dart
- lib/main.dart
- lib/ui/views/authentication/signup_view.dart
- test/ui_tests/authentication/login_view_test.dart
- lib/ui/views/profile/edit_profile_view.dart
- lib/ui/views/profile/profile_view.dart
- lib/ui/components/cv_drawer.dart
- lib/ui/views/notifications/notifications_view.dart
- lib/ui/views/groups/assignment_details_view.dart
- lib/ui/views/groups/edit_group_view.dart
- test/ui_tests/groups/edit_group_view_test.dart
- test/ui_tests/groups/update_assignment_view_test.dart
- test/ui_tests/groups/my_groups_view_test.dart
- test/ui_tests/ib/ib_landing_view_test.dart
- test/ui_tests/groups/new_group_view_test.dart
- lib/ui/views/groups/group_details_view.dart
- test/ui_tests/authentication/signup_view_test.dart
- lib/ui/views/ib/ib_page_view.dart
🧰 Additional context used
🪛 RuboCop (1.75.5)
lib/l10n/app_hi.arb
[warning] 318-318: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 319-319: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
lib/l10n/app_en.arb
[warning] 320-320: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
[warning] 321-321: Duplicated key in hash literal.
(Lint/DuplicateHashKey)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: build (windows-latest)
- GitHub Check: build (macos-latest)
- GitHub Check: build (ubuntu-latest)
🔇 Additional comments (3)
lib/gen_l10n/app_localizations_en.dart (1)
1-1469: ```shell
#!/bin/bashCorrectly scan for any Devanagari characters using Unicode property support in PCRE2
grep -nP '\p{Devanagari}' lib/gen_l10n/app_localizations_en.dart || echo "✅ No Devanagari characters found."
</details> <details> <summary>lib/gen_l10n/app_localizations.dart (1)</summary> `969-993`: ```shell #!/bin/bash # Find ARB definitions for assignment_details_* keys rg '"assignment_details_days"' -n rg '"assignment_details_hours"' -n rg '"assignment_details_minutes"' -n rg '"assignment_details_time_remaining"' -nlib/gen_l10n/app_localizations_hi.dart (1)
1180-1186: Let’s try a fixed-string search for the\"sequence:#!/bin/bash # List every line containing an escaped double-quote (\") by treating the pattern as plain text. rg -nF '\\"' lib/gen_l10n/app_localizations_hi.dart
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
lib/gen_l10n/app_localizations_hi.dart (1)
1067-1070: Typo propagated from the ARB fileThe generated getter returns
'जोडें', propagating the missing diacritic noted in the ARB file.
Fixing the ARB entry and regenerating localizations will automatically correct this.
🧹 Nitpick comments (4)
lib/l10n/app_hi.arb (1)
373-374: Use correct Hindi diacritics for “Add”
"edit_add": "जोडें"is missing the ड़ (\u095c) diacritic. Please switch to the standard spelling"जोड़ें"to avoid visible typos in the UI.-"edit_add": "जोडें", +"edit_add": "जोड़ें",lib/gen_l10n/app_localizations_en.dart (1)
1-1478: Consider excluding generated localization files from version control
app_localizations_en.dartis fully generated byflutter gen-l10nand sprawls over 1 400+ lines. Committing it inflates diffs and invites accidental edits (as happened here).
Typical practice is to:
- Add
lib/gen_l10n/*.dartto.gitignore.- Generate the file in CI (
flutter pub run flutter_localizationsor viaflutter build) so every build still has the artifacts.This keeps reviews readable and eliminates merge conflicts on generated code.
lib/gen_l10n/app_localizations.dart (2)
95-99: EnsuresupportedLocalesstays in sync with your ARB files
supportedLocalesis hard-coded. Whenever you introduce a new.arbfile (e.g.,app_es.arb), remember to update this list; otherwise that locale will silently fail at runtime. Automating this via theflutter gen-l10ntask (instead of committing the generated file) prevents this class of drift.
2788-2799: Generated file – consider excluding from VCS
app_localizations.dartis auto-generated byflutter gen-l10n. Committing it clutters diffs (this PR adds ~2 700 lines) and invites merge conflicts. A common workflow is:-.gitignore -# add -lib/gen_l10n/Then regenerate in CI (
flutter gen-l10n) immediately afterflutter pub get.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
lib/gen_l10n/app_localizations.dart(2 hunks)lib/gen_l10n/app_localizations_en.dart(2 hunks)lib/gen_l10n/app_localizations_hi.dart(2 hunks)lib/l10n/app_en.arb(2 hunks)lib/l10n/app_hi.arb(2 hunks)lib/ui/views/about/about_view.dart(1 hunks)lib/ui/views/profile/profile_view.dart(8 hunks)
✅ Files skipped from review due to trivial changes (1)
- lib/ui/views/about/about_view.dart
🚧 Files skipped from review as they are similar to previous changes (2)
- lib/ui/views/profile/profile_view.dart
- lib/l10n/app_en.arb
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: build (macos-latest)
- GitHub Check: build (ubuntu-latest)
- GitHub Check: build (windows-latest)
🔇 Additional comments (3)
lib/l10n/app_hi.arb (1)
1-501: Re-run duplicate-key / JSON validity check after the bulk importThe file grew from ~50 to 500 + keys in one shot. A single duplicate or trailing comma will break
flutter gen-l10nand the build.#!/bin/bash # 1. Fail fast on malformed JSON jq empty lib/l10n/app_hi.arb # 2. Detect duplicate keys (ignoring comments/metadata) grep -oP '"\K[^"]+(?=":)' lib/l10n/app_hi.arb | sort | uniq -dThis should exit silently.
Any output on the second command means at least one key is still duplicated and needs to be renamed / removed.lib/gen_l10n/app_localizations_hi.dart (1)
130-134: Check whether bothslack_channelandabout_slack_channelare still required
slack_channelsurvives here even after the earlier refactor that introducedabout_slack_channel. If the UI only references the new key, this getter (and the ARB entry) is now dead code.#!/bin/bash # Show all Dart usages of the old key-getter – expect zero matches. rg -n '\.slack_channel\b' --glob '*.dart' | headIf no usages appear, delete the key from the ARB file, run
flutter gen-l10n, and drop this getter.lib/gen_l10n/app_localizations.dart (1)
2111-2152: High risk of key-name proliferation (featured_circuitsvscv_featured_circuits)Having both
featured_circuitsand the namespacedcv_featured_circuitsincreases the chance of developers choosing the wrong key and creating inconsistent UI text. Unless both are intentionally different, consider deleting the obsolete one or adding a deprecation comment in the ARB source so that the generator produces a clearer warning.
|
🎉 This PR is included in version 1.2.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Fixes #371
Describe the changes you have made in this PR:
Implemented robust multilingual support:
Added language selection dropdown in sidebar with English/Hindi options
Refactored CVDrawer to use proper localization patterns
Implemented dynamic locale switching using GetX
Localization infrastructure:
Added new keys to app_en.arb and app_hi.arb files
Implemented proper string localization throughout the UI
Added "language" key for consistent translation
UI Improvements:
Standardized drawer item spacing and padding
Added visual feedback for selected language (radio buttons)
Maintained expansion state during language changes
Technical enhancements:
Used ValueKey to force widget rebuilds on locale changes
Optimized localization access patterns
Made test files localization-friendly
Screenshots of the changes:
Implementation Approach:
GetX for State Management:
Used Get.find() to manage locale state
Implemented reactive updates with Obx widget
Flutter Localization:
Leveraged AppLocalizations.of(context)! for all UI strings
Added proper ARB file entries for all translations
UI Components:
Created language selection using ExpansionTile
Used CVDrawerTile consistently for all menu items
Implemented visual feedback with Icons.radio_button_checked
Note: Please check "Allow edits from maintainers" as I would appreciate assistance in reviewing and merging this PR.
Summary by CodeRabbit
New Features
Enhancements
Bug Fixes
Tests
Chores