Skip to content

Conversation

@JatsuAkaYashvant
Copy link
Member

@JatsuAkaYashvant JatsuAkaYashvant commented Jun 17, 2025

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:

WhatsApp Image 2025-06-15 at 16 05 04_d31db9b8
WhatsApp Image 2025-06-15 at 16 05 04_4a67d3b5

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

    • Added comprehensive localization and language selection support, enabling users to switch between English and Hindi throughout the app.
    • Introduced a language selection menu in the app drawer for seamless language switching.
  • Enhancements

    • Fully localized all user-facing text across the app, including UI labels, buttons, dialogs, error messages, and notifications.
    • Improved internationalization support in key views such as authentication, profile, groups, projects, notifications, interactive book, and teachers.
    • Updated localization keys with consistent prefixes for better organization.
    • Refined UI components to use localized strings and improved language selection UI behavior.
  • Bug Fixes

    • Fixed dropdown and filtering logic in notifications to correctly handle localized text values.
  • Tests

    • Enhanced UI tests to support localization, replacing hardcoded strings with localized equivalents and verifying navigation flows in multiple languages.
  • Chores

    • Expanded and reorganized English and Hindi localization resource files with extensive new UI text entries.
    • Added a new language controller to manage locale state and UI expansion for language selection.

@coderabbitai
Copy link

coderabbitai bot commented Jun 17, 2025

Walkthrough

The 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

Files/Groups Change Summary
lib/gen_l10n/app_localizations.dart, lib/gen_l10n/app_localizations_en.dart, lib/gen_l10n/app_localizations_hi.dart Massive expansion of localization classes: added numerous string getters for UI text in both English and Hindi.
lib/l10n/app_en.arb, lib/l10n/app_hi.arb Added extensive new localization string keys and values for English and Hindi.
lib/controllers/language_controller.dart Introduced new LanguageController for managing locale and language selection UI state.
lib/main.dart Registered LanguageController and updated app setup for localization.
lib/ui/components/cv_drawer.dart Added language selection UI and integrated language controller into navigation drawer.
lib/ui/views/authentication/, lib/ui/views/contributors/contributors_view.dart, lib/ui/views/groups/, lib/ui/views/home/home_view.dart, lib/ui/views/ib/, lib/ui/views/notifications/notifications_view.dart, lib/ui/views/profile/, lib/ui/views/projects/*, lib/ui/views/teachers/teachers_view.dart Replaced all hardcoded UI strings with calls to localized resources via AppLocalizations.
lib/ui/views/profile/user_favourites_view.dart, lib/ui/views/profile/user_projects_view.dart Added static route id constants to respective classes.
test/ui_tests/* Updated test setup to support localization, replaced hardcoded string assertions with localized equivalents, and adapted widget finders.

Assessment against linked issues

Objective (Issue #) Addressed Explanation
Add multilingual support and internationalization to the app (#371)
Integrate updated modules and crowdin-style localization (#371)
Replace hardcoded UI strings with localized resources (#371)
Provide localization for both English and Hindi (#371)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Addition of static id constants in UserFavouritesView and UserProjectsView (lib/ui/views/profile/user_favourites_view.dart, lib/ui/views/profile/user_projects_view.dart) These changes introduce static route identifiers, which are unrelated to the localization objectives.

Possibly related PRs

Suggested labels

released, GSoC 25'

Suggested reviewers

  • tachyons
  • hardik17771

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@JatsuAkaYashvant JatsuAkaYashvant changed the title feat: modernize codebase for robust multilingual support Modernized codebase for multilingual support Jun 17, 2025
Copy link

@coderabbitai coderabbitai bot left a 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

DropdownButtonFormField is fed with localized strings (public, private, limited_access) but its value is still the raw backend value coming from widget.project.attributes.projectAccessType (e.g. "Public").
When the current locale is not English the value will not be found in items, causing an exception at build time.
Even worse, after the user selects an option the string stored in _projectAccessType becomes the localized label, so updateProject() 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 added ProfileViewModel listener to avoid leaks

context.watch<ProfileViewModel>().addListener(...) is registered in onModelReady but never removed. Because the BaseView can 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_error exists 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 layer

Although 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 calling onPressed directly

Invoking the callback bypasses gesture detection and semantics.
Prefer tester.tap to 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 failures

The first verify after pumpWidget already satisfies Mockito’s “called ≥ 1” rule.
A second bare verify therefore 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 blind toUpperCase() 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:

  1. Provide an explicit upper-case variant in the ARB files, or
  2. 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 repeated AppLocalizations.of(context)! look-ups & locale-insensitive toUpperCase()

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:

  1. Caching the localizations object at the top of build:
final l10n = AppLocalizations.of(context)!;
  1. Providing pre-capitalised keys in the ARB files (e.g. save_caps) or using toUpperCase(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-coding supportedLocales to English may hide i18n regressions

The test fixes the immediate failure, but locking supportedLocales to [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 deterministic

This 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 instance

Large 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 explicit locale makes the test rely on host settings

The 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.delegate and 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
Injected localizationsDelegates and supportedLocales into GetMaterialApp. 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 / _instituteController have active listeners added in initState, but they are not removed in dispose. Disposing the controller implicitly removes them, yet calling removeListener first 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 same supportedLocales list as production code

Hard-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: cache AppLocalizations inside build

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 delegates

Only FlutterQuillLocalizations and AppLocalizations are registered. While this works for most text, widgets such as date pickers rely on GlobalMaterialLocalizations. 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 .arb file and using {value} interpolation.


190-204: Minor performance / readability – fetch localisation once
Inside the same build scope you invoke AppLocalizations.of(context)! six times. Cache it once to a local final 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
_buildNameInput splits 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 like already_registered_login with a {login} placeholder or consider a RichText builder that inserts the styled “Login” inside the translated sentence.


145-149: Hard-coded generic “error” title could be more specific
You always show the title error for 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: delegates mutation is unnecessary
You copy the list returned by AppLocalizations.localizationsDelegates only 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 be const
Both the Icon and the translated Text are rebuilt every frame. The Icon can be const, and you can cache l10n as 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 clarity

The file is still called forgot_password_view_test.dart but now exclusively exercises LoginView. This mis-match makes grepping / IDE look-ups confusing and will trip up new contributors.


30-43: Consider declaring the locale: explicitly in every test for determinism

Other 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

supportedLocales is set but no explicit locale: 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 localisations

You 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 locale

Same 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 delegates

Only AppLocalizations.delegate is 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 locale

As 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.

changeLanguage already sets isLanguageExpanded.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 – use tester.element(find.byType(GetMaterialApp)).

GetMaterialApp is the root widget; MaterialApp may disappear if GetX changes its internals. Grab the context directly from GetMaterialApp to 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:

  1. Add self-referential keys (language_english, language_hindi) to each ARB file, or
  2. Derive the display name from Locale using intl’s toLanguageTag() / LocaleNames.

108-112: Duplicate collapse call.

langController.changeLanguage already sets isLanguageExpanded.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 Map every time _buildSubHeader is called is minor but avoidable. A simple switch (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

AppLocalizations already exposes localizationsDelegates and supportedLocales. 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: capture localizations once

Each test re-derives the localizations instance. 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 tests

Keep 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: Leverage AppLocalizations.localizationsDelegates

Minor: 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 of IB_FETCH_PAGE_DATA

The 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 group

The file is signup_view_test.dart but the group is "LoginViewTest -" – this is confusing when scanning test output.


36-45: Delegate list duplication

As in other files, prefer the constants offered by AppLocalizations.

test/ui_tests/groups/update_assignment_view_test.dart (2)

51-51: Use final instead of var for immutable variables

For consistency with Dart best practices, consider using final for variables that won't be reassigned.

-var assignment = Assignment.fromJson(mockAssignment);
+final assignment = Assignment.fromJson(mockAssignment);

118-119: Use final for mock service declarations

For consistency and to follow Dart conventions, use final for 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7be5fb1 and e4e489e.

📒 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 cleanly

The 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 addition

Including 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
Imported app_localizations.dart to enable use of AppLocalizations.of(context).


20-25: Enable dynamic localization by removing const
Changing the children list from a const to a non-const list is necessary for runtime calls to AppLocalizations.of(context) so that the UI updates when the locale changes.


28-31: ```shell
#!/bin/bash

Correctly 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
done

Also applies to: 136-136, 148-149, 164-164

lib/ui/views/profile/user_favourites_view.dart (3)

11-11: Import localization package
Added app_localizations.dart import to support localized UI strings in the empty state.


16-16: Add static route identifier
Introduced static const String id = 'user_favourites_view'; for consistent navigation usage.


37-38: ```shell
#!/bin/bash

Search 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
done

Also applies to: 125-128, 139-140, 160-160

lib/ui/views/profile/user_projects_view.dart (1)

35-37: 👍 Localised empty-state copy – looks good

Hard-coded strings have been replaced by AppLocalizations keys. 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 using Get.put(LanguageController(), permanent: true) explicitly or Get.lazyPut with fenix to clarify intent, and ensure you close any streams inside onClose.

test/ui_tests/profile/profile_view_test.dart (1)

95-98: Assertion looseness could mask regressions

findsAtLeastNWidgets(4) on RichText is 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
_buildDetailComponent declares 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 a static const (or convert the logic to a switch) 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 the Intl.plural API or add separate pluralised ARB keys.
[ request_verification ]

lib/ui/views/notifications/notifications_view.dart (2)

95-106: model.value still stores a localised string → breaks after a language switch
Although you introduced allValue / unreadValue, onChanged writes the display text back into model.value, and later comparisons (lines 98, 164) again rely on the localised string.
If the user changes the app language, model.value will 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
Once model.filter holds 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’s gen-l10n prints 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 i18n

Localising every visible string is the whole point of this PR. Please expose/consume a total_members key (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 rigid

The view could legitimately gain another CVTextField without breaking functionality. Consider using findsAtLeastNWidgets or asserting on specific keys instead.

test/ui_tests/groups/update_assignment_view_test.dart (2)

41-49: Good practice: Service cleanup in tearDown

Excellent addition of the tearDown method to unregister services. This prevents state leakage between tests and ensures proper test isolation.


57-75: Well-implemented localization setup

The 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 initialization

Good use of didChangeDependencies with the _isInitialized flag 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 localization

Using 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 builder

Great approach to parameterize the CustomImageBuilder with 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 elements

All user-facing strings including copyright notice, table of contents, and navigation hints have been correctly replaced with localized versions.

Copy link

@coderabbitai coderabbitai bot left a 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

currentLocale is only stored in memory; the user’s choice will be lost on every fresh run or hot-restart.
Persist the code using GetStorage, SharedPreferences, or any other preferred cache so that LanguageController.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-in toggle() helper for brevity

Minor 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_description currently 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.arb and 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 & hyphenation

UI 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.dart is produced by flutter gen-l10n. Committing this 2 500-line artefact bloats diffs and creates frequent merge conflicts.
Consider committing only the .arb resource files and adding lib/gen_l10n/** to .gitignore; let CI/regeneration recreate it when building.


305-310: Minor nit – prefer title-case for visible strings

Slack channel is exposed to the UI through this key; consider Slack Channel for consistency with other title-case labels like Email IDs, Terms Of Service, etc.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4e489e and fdfcbf0.

📒 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 files

Run once per CI to avoid regressions.

#!/bin/bash
# List duplicate keys in every ARB file
fd .arb -x jq -r 'keys[]' {} | sort | uniq -d
lib/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 edits

Remember 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 (and app_hi.arb) files instead, then run flutter gen-l10n.

lib/gen_l10n/app_localizations_hi.dart (1)

11-24: Strings look consistent – no issues spotted

All 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 present

Verified that the sample value for home_header_title/_subtitle is proper English.
Nice fix to the earlier ARB pollution problem.


2435-2468: Sanity-check ARB files for stray Devanagari characters

Given 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 key language added – remember to use it consistently

The 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/**"

@coveralls
Copy link

coveralls commented Jun 17, 2025

Pull Request Test Coverage Report for Build 15715483238

Details

  • 561 of 1517 (36.98%) changed or added relevant lines in 30 files are covered.
  • 45 unchanged lines in 14 files lost coverage.
  • Overall coverage decreased (-4.1%) to 58.315%

Changes Missing Coverage Covered Lines Changed/Added Lines %
lib/ui/views/projects/featured_projects_view.dart 8 9 88.89%
lib/ui/views/profile/user_favourites_view.dart 0 2 0.0%
lib/ui/views/profile/user_projects_view.dart 0 2 0.0%
lib/ui/views/cv_landing_view.dart 1 4 25.0%
lib/ui/views/groups/edit_group_view.dart 10 13 76.92%
lib/ui/views/groups/new_group_view.dart 11 14 78.57%
lib/ui/views/groups/add_assignment_view.dart 38 42 90.48%
lib/ui/views/profile/edit_profile_view.dart 14 18 77.78%
lib/viewmodels/cv_landing_viewmodel.dart 0 5 0.0%
lib/controllers/language_controller.dart 0 6 0.0%
Files with Coverage Reduction New Missed Lines %
lib/ui/components/cv_drawer.dart 1 0.83%
lib/ui/views/groups/assignment_details_view.dart 1 64.49%
lib/ui/views/groups/update_assignment_view.dart 1 62.32%
lib/ui/views/profile/profile_view.dart 1 88.52%
lib/ui/views/projects/edit_project_view.dart 1 0.0%
lib/ui/views/projects/featured_projects_view.dart 1 72.16%
lib/ui/views/authentication/login_view.dart 2 78.41%
lib/ui/views/groups/group_details_view.dart 2 43.94%
lib/ui/views/groups/my_groups_view.dart 2 63.41%
lib/ui/views/notifications/notifications_view.dart 2 1.22%
Totals Coverage Status
Change from base Build 15634229699: -4.1%
Covered Lines: 4685
Relevant Lines: 8034

💛 - Coveralls

Copy link

@coderabbitai coderabbitai bot left a 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 collisions

With ~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 views

Given 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 inconsistency

In “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.dart is produced by flutter gen-l10n and will be regenerated on every localization change. Committing this 2 600-line artifact bloats the repo and causes noisy diffs.
Move the entire lib/gen_l10n/* directory to .gitignore and regenerate it in CI / local builds instead.

lib/gen_l10n/app_localizations_hi.dart (1)

1-4: Import can be dropped – the intl package is never referenced

The generated file adds intl only to immediately silence the “unused import” warning.
Since no intl APIs 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

📥 Commits

Reviewing files that changed from the base of the PR and between fdfcbf0 and 03b2d16.

📒 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/bash

Correctly 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"' -n
lib/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

Copy link

@coderabbitai coderabbitai bot left a 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 file

The 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.dart is fully generated by flutter gen-l10n and sprawls over 1 400+ lines. Committing it inflates diffs and invites accidental edits (as happened here).
Typical practice is to:

  1. Add lib/gen_l10n/*.dart to .gitignore.
  2. Generate the file in CI (flutter pub run flutter_localizations or via flutter 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: Ensure supportedLocales stays in sync with your ARB files

supportedLocales is hard-coded. Whenever you introduce a new .arb file (e.g., app_es.arb), remember to update this list; otherwise that locale will silently fail at runtime. Automating this via the flutter gen-l10n task (instead of committing the generated file) prevents this class of drift.


2788-2799: Generated file – consider excluding from VCS

app_localizations.dart is auto-generated by flutter 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 after flutter pub get.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03b2d16 and 1fcc158.

📒 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 import

The file grew from ~50 to 500 + keys in one shot. A single duplicate or trailing comma will break flutter gen-l10n and 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 -d

This 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 both slack_channel and about_slack_channel are still required

slack_channel survives here even after the earlier refactor that introduced about_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' | head

If 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_circuits vs cv_featured_circuits)

Having both featured_circuits and the namespaced cv_featured_circuits increases 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.

@hardik17771 hardik17771 merged commit b71b91e into CircuitVerse:master Jun 18, 2025
7 checks passed
This was referenced Jul 28, 2025
@github-actions
Copy link
Contributor

🎉 This PR is included in version 1.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@coderabbitai coderabbitai bot mentioned this pull request Aug 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multilingual support (Internationalization)

3 participants