Skip to content
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

add easy_localization_storage, easy_localization_shared_preferences_storage and easy_localization_helper #719

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

phamconganh
Copy link

@phamconganh phamconganh commented Oct 28, 2024

  • easy_localization_storage, easy_localization_shared_preferences_storage to extend the way locale is saved
  • easy_localization_helper to separate extensions used in easy_localization_shared_preferences_storage and easy_localization

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced localization functionality with in-memory storage options.
    • Introduced new libraries: easy_localization_helper and easy_localization_storage.
  • Bug Fixes

    • Removed reliance on SharedPreferences for localization state management.
  • Documentation

    • Added README files and CHANGELOGs for new packages.
  • Tests

    • Updated tests to utilize new in-memory storage for localization.
    • Refined test cases for better alignment with the new storage mechanism.

Copy link

coderabbitai bot commented Oct 28, 2024

Walkthrough

The changes in this pull request primarily enhance localization functionality within a Flutter application. Key updates include the introduction of in-memory storage for localization data, replacing the previous reliance on SharedPreferences. Several classes and methods have been modified to support this new storage mechanism, including constructors and method signatures. Additionally, new packages for localization storage and helper functions have been added, alongside the removal of deprecated functions and extensions. The overall structure of the project has been refined to improve code consistency and efficiency.

Changes

File Change Summary
example/lib/main.dart Updated EasyLocalization.ensureInitialized() to accept EasyLocalizationInMemoryStorage(). Marked supportedLocales as const. Updated constructors for MyApp and MyHomePage to be constant. Updated home property in MaterialApp to use constant constructor.
example/macos/Flutter/GeneratedPluginRegistrant.swift Removed import and registration for SharedPreferencesPlugin.
example/pubspec.yaml Added dependency for easy_localization_storage.
lib/src/asset_loader.dart Added import for easy_localization_helper.
lib/src/easy_localization_app.dart Modified ensureInitialized method to require EasyLocalizationStorage parameter.
lib/src/easy_localization_controller.dart Added _storage variable, updated constructor, and modified methods to use new storage mechanism.
lib/src/utils.dart Deprecated localeFromString and localeToString methods; removed LocaleToStringHelper and StringToLocaleHelper extensions.
packages/easy_localization_helper/.gitignore Created .gitignore file for ignoring specific files and directories.
packages/easy_localization_helper/.metadata Created metadata file for tracking project properties.
packages/easy_localization_helper/CHANGELOG.md Added initial release entry for version 0.0.1.
packages/easy_localization_helper/LICENSE Added placeholder comment for license.
packages/easy_localization_helper/README.md Created README with sections for package overview and usage.
packages/easy_localization_helper/analysis_options.yaml Included Flutter lints configuration.
packages/easy_localization_helper/lib/easy_localization_helper.dart Introduced extensions for locale conversions.
packages/easy_localization_helper/pubspec.yaml Declared new package with version 0.0.1 and dependencies.
packages/easy_localization_helper/test/easy_localization_helper_test.dart Created test file with commented-out test cases.
packages/easy_localization_shared_preferences_storage/.gitignore Created .gitignore file.
packages/easy_localization_shared_preferences_storage/.metadata Created metadata file.
packages/easy_localization_shared_preferences_storage/CHANGELOG.md Added initial release entry for version 0.0.1.
packages/easy_localization_shared_preferences_storage/LICENSE Added placeholder comment for license.
packages/easy_localization_shared_preferences_storage/README.md Created README with template sections.
packages/easy_localization_shared_preferences_storage/analysis_options.yaml Included Flutter lints configuration.
packages/easy_localization_shared_preferences_storage/lib/easy_localization_shared_preferences_storage.dart Introduced EasyLocalizationSharedPreferencesStorage class for managing localization data.
packages/easy_localization_shared_preferences_storage/lib/src/easy_localization_shared_preferences_storage.dart Added methods for version and locale management.
packages/easy_localization_shared_preferences_storage/pubspec.yaml Declared new package with version 0.0.1 and dependencies.
packages/easy_localization_shared_preferences_storage/test/easy_localization_shared_preferences_storage_test.dart Created test file with commented-out test cases.
packages/easy_localization_storage/.gitignore Created .gitignore file.
packages/easy_localization_storage/.metadata Created metadata file.
packages/easy_localization_storage/CHANGELOG.md Added initial release entry for version 0.0.1.
packages/easy_localization_storage/LICENSE Added placeholder comment for license.
packages/easy_localization_storage/README.md Created README with template sections.
packages/easy_localization_storage/analysis_options.yaml Included Flutter lints configuration.
packages/easy_localization_storage/lib/easy_localization_storage.dart Introduced library exporting storage functionalities.
packages/easy_localization_storage/lib/src/easy_localization_in_memory_storage.dart Added EasyLocalizationInMemoryStorage class for in-memory storage.
packages/easy_localization_storage/lib/src/easy_localization_storage.dart Introduced abstract class EasyLocalizationStorage with methods for version and locale management.
packages/easy_localization_storage/pubspec.yaml Declared new package with version 0.0.1 and dependencies.
packages/easy_localization_storage/test/easy_localization_storage_test.dart Created test file with commented-out test cases.
pubspec.yaml Removed shared_preferences dependency; added easy_localization_storage and easy_localization_helper.
test/easy_localization_context_test.dart Updated tests to use EasyLocalizationInMemoryStorage.
test/easy_localization_test.dart Added imports for new dependencies and updated tests for localization handling.
test/easy_localization_utils_test.dart Added import for easy_localization_helper.
test/easy_localization_widget_test.dart Updated tests to use EasyLocalizationInMemoryStorage.

Poem

In the meadow where we play,
New changes hop in bright array.
Localization now in memory stored,
With helpers and functions, we’re never bored!
So let’s leap and cheer with glee,
For a smoother app, just wait and see! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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

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: 10

🧹 Outside diff range and nitpick comments (12)
packages/easy_localization_storage/lib/src/easy_localization_storage.dart (2)

3-9: Add documentation to improve API clarity.

Consider adding documentation to:

  1. Explain the purpose of the storage interface
  2. Document the version management purpose and valid values
  3. Document method parameters and return values

Example documentation:

/// Abstract interface for managing localization storage.
/// Provides methods to persist and retrieve locale settings and version information.
abstract class EasyLocalizationStorage {
  /// Retrieves the stored version number.
  /// Returns null if no version is stored.
  Future<int?> getVersion();

  /// Stores the version number.
  /// Set [value] to null to clear the stored version.
  Future<void> setVersion({int? value});

  /// Retrieves the stored locale.
  /// Returns null if no locale is stored.
  Future<Locale?> getLocale();

  /// Stores the locale setting.
  /// Set [value] to null to clear the stored locale.
  Future<void> setLocale({Locale? value});
}

3-18: Consider adding error handling guidelines.

As this is a core storage interface that will be implemented by different storage backends, consider:

  1. Documenting expected error scenarios and how implementations should handle them
  2. Adding guidance for error propagation (e.g., when storage is unavailable)
  3. Defining error types that implementations should throw

This will help ensure consistent behavior across different storage implementations.

packages/easy_localization_storage/lib/src/easy_localization_in_memory_storage.dart (2)

5-7: Enhance class documentation with usage examples and memory implications.

While the documentation indicates this is for testing, it would be helpful to:

  1. Add example usage code
  2. Document that data is lost when the app restarts
  3. Mention any performance characteristics or memory implications

Add the following documentation:

 /// An in-memory implementation of [EasyLocalizationStorage].
 /// Useful for testing.
+/// 
+/// Note: Data is stored in memory and will be lost when the app restarts.
+/// 
+/// Example usage:
+/// ```dart
+/// final storage = EasyLocalizationInMemoryStorage();
+/// await EasyLocalization.ensureInitialized(
+///   storage: storage,
+///   // ... other parameters
+/// );
+/// ```

8-9: Document the significance of initial version value.

The initial version value of 1 should be documented to explain its significance and why it's not 0 or null by default.

Add documentation above the version declaration:

+  /// The storage version, initialized to 1.
+  /// This helps track and manage storage format changes.
   int? _version = 1;
packages/easy_localization_helper/lib/easy_localization_helper.dart (1)

5-11: Documentation and edge cases need attention.

While the implementation is straightforward, consider these improvements:

  1. Enhance documentation to include:
    • Example usage
    • Parameter description
    • Return value format
  2. Add validation for the separator parameter
  3. Handle potential edge cases in the Locale's toString() output

Consider updating the documentation and implementation:

 /// [Easy Localization] locale helper
 extension LocaleToStringHelper on Locale {
-  /// Convert [locale] to String with custom separator
+  /// Converts a Locale to a string representation using a custom separator.
+  /// 
+  /// Example:
+  /// ```dart
+  /// final locale = Locale('en', 'US');
+  /// print(locale.toStringWithSeparator(separator: '-')); // outputs: en-US
+  /// ```
+  /// 
+  /// Parameters:
+  ///   separator: The string to use as separator between locale components.
+  ///              Defaults to '_'.
+  /// 
+  /// Returns:
+  ///   A string representation of the locale with the specified separator.
   String toStringWithSeparator({String separator = '_'}) {
+    if (separator.isEmpty) {
+      throw ArgumentError('Separator cannot be empty');
+    }
     return toString().split('_').join(separator);
   }
 }
packages/easy_localization_shared_preferences_storage/lib/src/easy_localization_shared_preferences_storage.dart (2)

8-16: Add documentation for the constructor parameters and fields.

Consider adding dartdoc comments to explain:

  • The purpose of version parameter
  • The format and usage of storagePrefix
  • The significance of the generated keys
+ /// Storage implementation using SharedPreferences.
+ ///
+ /// @param store The SharedPreferences instance to use for storage
+ /// @param version The storage version, used for migration (defaults to 1)
+ /// @param storagePrefix The prefix for storage keys (defaults to 'easy_localization.locale')
 class EasyLocalizationSharedPreferencesStorage extends EasyLocalizationStorage {

19-19: Consider implementing the migration method.

The TODO comment indicates a need for version-based migration. This is important for handling storage format changes across app updates.

Would you like me to help design and implement the migration mechanism?

example/pubspec.yaml (1)

28-29: Add version constraints for the new dependency.

While using relative paths is fine for local development, it's recommended to specify version constraints for better dependency management.

 easy_localization_storage:
-    path: ../packages/easy_localization_storage
+    path: ../packages/easy_localization_storage
+    version: ^1.0.0  # Replace with appropriate version
test/easy_localization_context_test.dart (1)

Line range hint 183-248: Remove duplicate test cases

The following test cases are duplicated:

  • "[EasyLocalization] device locale test"
  • "[EasyLocalization] reset device locale test"

The only difference is the use of pumpAndSettle() instead of pump(). Consider:

  1. Removing the duplicates
  2. If different timing behavior needs testing, rename the tests to clarify the distinction
test/easy_localization_test.dart (1)

Line range hint 222-254: Fix typo in test name and LGTM for locale matching tests.

The new test cases thoroughly cover locale matching scenarios, addressing issue #674. However, there's a minor typo in the test name.

-      test('select best lenguage match if no perfect match exists', () {
+      test('select best language match if no perfect match exists', () {
test/easy_localization_widget_test.dart (1)

426-430: Consider removing commented-out code.

The SharedPreferences mock setup is commented out but no longer needed since we've moved to in-memory storage.

-    // setUp(() {
-    //   SharedPreferences.setMockInitialValues({
-    //     'locale': '',
-    //   });
-    // });
lib/src/easy_localization_controller.dart (1)

177-181: Consider renaming 'useOnlyLangCode' to 'ignoreCountryCode' for clarity

The variable useOnlyLangCode controls whether the countryCode is used when constructing the desiredLocale. Since the scriptCode is still included, renaming it to ignoreCountryCode might better reflect its purpose and improve code readability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0e88c1c and df0eaac.

📒 Files selected for processing (42)
  • example/lib/main.dart (8 hunks)
  • example/macos/Flutter/GeneratedPluginRegistrant.swift (0 hunks)
  • example/pubspec.yaml (1 hunks)
  • lib/src/asset_loader.dart (1 hunks)
  • lib/src/easy_localization_app.dart (2 hunks)
  • lib/src/easy_localization_controller.dart (3 hunks)
  • lib/src/utils.dart (0 hunks)
  • packages/easy_localization_helper/.gitignore (1 hunks)
  • packages/easy_localization_helper/.metadata (1 hunks)
  • packages/easy_localization_helper/CHANGELOG.md (1 hunks)
  • packages/easy_localization_helper/LICENSE (1 hunks)
  • packages/easy_localization_helper/README.md (1 hunks)
  • packages/easy_localization_helper/analysis_options.yaml (1 hunks)
  • packages/easy_localization_helper/lib/easy_localization_helper.dart (1 hunks)
  • packages/easy_localization_helper/pubspec.yaml (1 hunks)
  • packages/easy_localization_helper/test/easy_localization_helper_test.dart (1 hunks)
  • packages/easy_localization_shared_preferences_storage/.gitignore (1 hunks)
  • packages/easy_localization_shared_preferences_storage/.metadata (1 hunks)
  • packages/easy_localization_shared_preferences_storage/CHANGELOG.md (1 hunks)
  • packages/easy_localization_shared_preferences_storage/LICENSE (1 hunks)
  • packages/easy_localization_shared_preferences_storage/README.md (1 hunks)
  • packages/easy_localization_shared_preferences_storage/analysis_options.yaml (1 hunks)
  • packages/easy_localization_shared_preferences_storage/lib/easy_localization_shared_preferences_storage.dart (1 hunks)
  • packages/easy_localization_shared_preferences_storage/lib/src/easy_localization_shared_preferences_storage.dart (1 hunks)
  • packages/easy_localization_shared_preferences_storage/pubspec.yaml (1 hunks)
  • packages/easy_localization_shared_preferences_storage/test/easy_localization_shared_preferences_storage_test.dart (1 hunks)
  • packages/easy_localization_storage/.gitignore (1 hunks)
  • packages/easy_localization_storage/.metadata (1 hunks)
  • packages/easy_localization_storage/CHANGELOG.md (1 hunks)
  • packages/easy_localization_storage/LICENSE (1 hunks)
  • packages/easy_localization_storage/README.md (1 hunks)
  • packages/easy_localization_storage/analysis_options.yaml (1 hunks)
  • packages/easy_localization_storage/lib/easy_localization_storage.dart (1 hunks)
  • packages/easy_localization_storage/lib/src/easy_localization_in_memory_storage.dart (1 hunks)
  • packages/easy_localization_storage/lib/src/easy_localization_storage.dart (1 hunks)
  • packages/easy_localization_storage/pubspec.yaml (1 hunks)
  • packages/easy_localization_storage/test/easy_localization_storage_test.dart (1 hunks)
  • pubspec.yaml (2 hunks)
  • test/easy_localization_context_test.dart (3 hunks)
  • test/easy_localization_test.dart (9 hunks)
  • test/easy_localization_utils_test.dart (1 hunks)
  • test/easy_localization_widget_test.dart (6 hunks)
💤 Files with no reviewable changes (2)
  • example/macos/Flutter/GeneratedPluginRegistrant.swift
  • lib/src/utils.dart
✅ Files skipped from review due to trivial changes (25)
  • lib/src/asset_loader.dart
  • packages/easy_localization_helper/.gitignore
  • packages/easy_localization_helper/.metadata
  • packages/easy_localization_helper/CHANGELOG.md
  • packages/easy_localization_helper/LICENSE
  • packages/easy_localization_helper/analysis_options.yaml
  • packages/easy_localization_helper/pubspec.yaml
  • packages/easy_localization_helper/test/easy_localization_helper_test.dart
  • packages/easy_localization_shared_preferences_storage/.gitignore
  • packages/easy_localization_shared_preferences_storage/.metadata
  • packages/easy_localization_shared_preferences_storage/CHANGELOG.md
  • packages/easy_localization_shared_preferences_storage/LICENSE
  • packages/easy_localization_shared_preferences_storage/analysis_options.yaml
  • packages/easy_localization_shared_preferences_storage/lib/easy_localization_shared_preferences_storage.dart
  • packages/easy_localization_shared_preferences_storage/pubspec.yaml
  • packages/easy_localization_shared_preferences_storage/test/easy_localization_shared_preferences_storage_test.dart
  • packages/easy_localization_storage/.gitignore
  • packages/easy_localization_storage/.metadata
  • packages/easy_localization_storage/CHANGELOG.md
  • packages/easy_localization_storage/LICENSE
  • packages/easy_localization_storage/analysis_options.yaml
  • packages/easy_localization_storage/lib/easy_localization_storage.dart
  • packages/easy_localization_storage/pubspec.yaml
  • packages/easy_localization_storage/test/easy_localization_storage_test.dart
  • test/easy_localization_utils_test.dart
🧰 Additional context used
🪛 LanguageTool
packages/easy_localization_helper/README.md

[uncategorized] ~29-~29: You might be missing the article “the” here.
Context: ...r package users. Add longer examples to /example folder. ```dart const like = ...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

packages/easy_localization_shared_preferences_storage/README.md

[style] ~14-~14: Consider using the synonym “brief” (= concise, using a few words, not lasting long) to strengthen your wording.
Context: .../to/develop-packages). --> TODO: Put a short description of the package here that he...

(QUICK_BRIEF)


[uncategorized] ~29-~29: You might be missing the article “the” here.
Context: ...r package users. Add longer examples to /example folder. ```dart const like = ...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

packages/easy_localization_storage/README.md

[style] ~14-~14: Consider using the synonym “brief” (= concise, using a few words, not lasting long) to strengthen your wording.
Context: .../to/develop-packages). --> TODO: Put a short description of the package here that he...

(QUICK_BRIEF)


[uncategorized] ~29-~29: You might be missing the article “the” here.
Context: ...r package users. Add longer examples to /example folder. ```dart const like = ...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

🔇 Additional comments (24)
packages/easy_localization_storage/lib/src/easy_localization_storage.dart (1)

1-1: LGTM!

The import is correctly specified and necessary for the Locale type.

packages/easy_localization_storage/lib/src/easy_localization_in_memory_storage.dart (1)

11-20: LGTM! Well-implemented version management.

The version management implementation includes:

  • Proper error handling for negative values
  • Clear error messages
  • Correct async/Future implementation
pubspec.yaml (2)

20-23: Consider version constraints for local packages.

While using path dependencies is fine for development, consider adding version constraints for these packages to ensure compatibility when published:

dependencies:
  easy_localization_storage: ^1.0.0
  easy_localization_helper: ^1.0.0

This change aligns with the architectural improvements mentioned in the PR objectives, separating storage and helper functionalities into dedicated packages.

✅ Verification successful

Version constraints should be added based on existing package versions

Since both packages are at version 0.0.1, I recommend using version constraints that reflect their early development stage:

dependencies:
  easy_localization_storage: ^0.0.1
  easy_localization_helper: ^0.0.1

This ensures compatibility with the current package versions while allowing for patch updates. As these packages mature, you can update the constraints accordingly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the new packages have their own pubspec.yaml with version info
echo "Checking easy_localization_storage package:"
cat packages/easy_localization_storage/pubspec.yaml 2>/dev/null | grep "version:"
echo -e "\nChecking easy_localization_helper package:"
cat packages/easy_localization_helper/pubspec.yaml 2>/dev/null | grep "version:"

Length of output: 366


6-6: Verify if publish_to: none is intended for development only.

The uncommented publish_to: none prevents publishing to pub.dev. Ensure this is intended only for development and should be commented out or removed before the final release.

✅ Verification successful

publish_to: none is correctly configured across all packages

The publish_to: none setting is consistently applied across all packages in the monorepo structure (main package, storage, helper, shared preferences storage, and example). This is the intended configuration for a monorepo where packages are meant to be used together and not published individually to pub.dev.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if this is consistent with other packages in the repository
fd -e yaml pubspec.yaml -x grep -l "publish_to:"

Length of output: 254


Script:

#!/bin/bash
# Check the actual publish_to configuration in all pubspec.yaml files
echo "=== Main package ==="
grep -A 1 "publish_to:" ./pubspec.yaml || true

echo -e "\n=== Storage package ==="
grep -A 1 "publish_to:" ./packages/easy_localization_storage/pubspec.yaml || true

echo -e "\n=== Helper package ==="
grep -A 1 "publish_to:" ./packages/easy_localization_helper/pubspec.yaml || true

echo -e "\n=== Shared Preferences Storage package ==="
grep -A 1 "publish_to:" ./packages/easy_localization_shared_preferences_storage/pubspec.yaml || true

echo -e "\n=== Example package ==="
grep -A 1 "publish_to:" ./example/pubspec.yaml || true

Length of output: 828

packages/easy_localization_helper/lib/easy_localization_helper.dart (2)

1-4: LGTM! Clean library declaration with appropriate imports.


13-36: ⚠️ Potential issue

Add input validation and improve error handling.

The current implementation makes assumptions about the input format without proper validation, which could lead to runtime errors.

Consider implementing these improvements:

 /// [Easy Localization] string locale helper
 extension StringToLocaleHelper on String {
-  /// Convert string to [Locale] object
+  /// Converts a string to a Locale object based on a specified separator.
+  /// 
+  /// Format: <languageCode>[separator][scriptCode/countryCode][separator][countryCode]
+  /// 
+  /// Examples:
+  /// ```dart
+  /// 'en_US'.toLocale()               // Locale('en', 'US')
+  /// 'zh_Hant_TW'.toLocale()          // Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW')
+  /// 'sr_Cyrl'.toLocale()             // Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Cyrl')
+  /// ```
+  /// 
+  /// Throws:
+  ///   FormatException if the string format is invalid
   Locale toLocale({String separator = '_'}) {
+    if (isEmpty) {
+      throw FormatException('Locale string cannot be empty');
+    }
+    if (separator.isEmpty) {
+      throw ArgumentError('Separator cannot be empty');
+    }
+
     final localeList = split(separator);
+    
+    // Validate language code
+    if (localeList.first.isEmpty || !RegExp(r'^[a-zA-Z]{2,3}$').hasMatch(localeList.first)) {
+      throw FormatException('Invalid language code: ${localeList.first}');
+    }
+
     switch (localeList.length) {
       case 2:
-        return localeList.last.length == 4 // scriptCode length is 4
+        final secondPart = localeList.last;
+        if (secondPart.isEmpty) {
+          throw FormatException('Second part cannot be empty');
+        }
+        
+        return secondPart.length == 4 // scriptCode length is 4
             ? Locale.fromSubtags(
                 languageCode: localeList.first,
-                scriptCode: localeList.last,
+                scriptCode: secondPart,
               )
-            : Locale(localeList.first, localeList.last);
+            : Locale(localeList.first, secondPart);
       case 3:
+        if (localeList[1].isEmpty || localeList.last.isEmpty) {
+          throw FormatException('Script code and country code cannot be empty');
+        }
+        if (localeList[1].length != 4) {
+          throw FormatException('Invalid script code: ${localeList[1]}');
+        }
         return Locale.fromSubtags(
           languageCode: localeList.first,
           scriptCode: localeList[1],
           countryCode: localeList.last,
         );
       default:
+        if (localeList.length > 3) {
+          throw FormatException('Too many components in locale string');
+        }
         return Locale(localeList.first);
     }
   }
 }

Let's verify the usage patterns in the codebase:

packages/easy_localization_shared_preferences_storage/lib/src/easy_localization_shared_preferences_storage.dart (2)

1-7: LGTM! Clean imports and proper class hierarchy.

The imports are well-organized and the class correctly extends EasyLocalizationStorage.


7-7: Verify storage interface implementation completeness.

Let's ensure this implementation satisfies all requirements of the storage interface.

✅ Verification successful

Storage interface implementation is complete and correct

The implementation properly implements all required methods from the EasyLocalizationStorage interface:

  • Future<int?> getVersion()
  • Future<void> setVersion({int? value})
  • Future<Locale?> getLocale()
  • Future<void> setLocale({Locale? value})

Each method has appropriate error handling and correctly uses the underlying SharedPreferences store for persistence.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find and analyze the storage interface definition
ast-grep --pattern 'abstract class EasyLocalizationStorage {
  $$$
}'

# Check for any additional interface methods that might need implementation
rg -A 5 "abstract.*Future" packages/easy_localization_storage

Length of output: 855


Script:

#!/bin/bash
# Check the implementation of the required methods in SharedPreferences storage
ast-grep --pattern 'class EasyLocalizationSharedPreferencesStorage extends EasyLocalizationStorage {
  $$$
}'

# Also check for method implementations specifically
ast-grep --pattern 'Future<int?> getVersion() {
  $$$
}'

ast-grep --pattern 'Future<void> setVersion({int? value}) {
  $$$
}'

ast-grep --pattern 'Future<Locale?> getLocale() {
  $$$
}'

ast-grep --pattern 'Future<void> setLocale({Locale? value}) {
  $$$
}'

Length of output: 8893

example/pubspec.yaml (1)

28-29: Verify the dependency structure.

Let's verify if the new packages are properly structured and if there are any circular dependencies.

✅ Verification successful

Let me check the actual content of these pubspec.yaml files to verify the dependency structure and potential circular dependencies.


Dependency structure is properly organized

The package structure is well-organized with no circular dependencies:

  • easy_localization_storage and easy_localization_helper are independent base packages with only Flutter SDK dependencies
  • easy_localization_shared_preferences_storage properly depends on both base packages and shared_preferences
  • All packages have consistent SDK constraints and proper path references
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the structure of the new packages and their dependencies

# Test 1: Verify the existence of the new packages
echo "Checking package directories..."
fd -t d "easy_localization_(storage|helper)" packages/

# Test 2: Check for pubspec.yaml files in the new packages
echo -e "\nChecking pubspec.yaml files..."
fd -t f "pubspec.yaml" packages/easy_localization_storage packages/easy_localization_helper

# Test 3: Look for potential circular dependencies
echo -e "\nChecking for circular dependencies..."
rg -l "easy_localization(_storage|_helper)?" packages/easy_localization_*/pubspec.yaml

Length of output: 926


Script:

#!/bin/bash
# Check the content of pubspec.yaml files to analyze dependencies
echo "Content of easy_localization_storage/pubspec.yaml:"
cat packages/easy_localization_storage/pubspec.yaml

echo -e "\nContent of easy_localization_helper/pubspec.yaml:"
cat packages/easy_localization_helper/pubspec.yaml

echo -e "\nContent of easy_localization_shared_preferences_storage/pubspec.yaml:"
cat packages/easy_localization_shared_preferences_storage/pubspec.yaml

Length of output: 5432

example/lib/main.dart (4)

2-2: Verify storage persistence requirements.

The switch to EasyLocalizationInMemoryStorage means locale settings won't persist across app restarts. Consider if this meets your application's requirements.

If persistence is needed, consider using EasyLocalizationSharedPreferencesStorage instead:

- await EasyLocalization.ensureInitialized(EasyLocalizationInMemoryStorage());
+ await EasyLocalization.ensureInitialized(EasyLocalizationSharedPreferencesStorage());

Also applies to: 11-12


Line range hint 14-43: LGTM! Performance improvements with const.

Good use of const for supportedLocales and child property, which helps with widget rebuilding optimization.


Line range hint 44-58: LGTM! Proper usage of const constructor.

Good implementation of const constructor for the stateless widget, which helps with widget rebuilding optimization.


131-134: LGTM! Proper usage of const for static text widgets.

Good use of const for Text widgets that have static content or use tr()/plural() for translation.

Also applies to: 139-139, 155-155, 163-163

test/easy_localization_context_test.dart (2)

4-4: LGTM: Import added for new storage package

The import aligns with the PR's objective of introducing new storage components.


54-54: Verify initialization behavior with in-memory storage

The switch to explicit in-memory storage is a breaking change. Ensure that:

  1. Test isolation is maintained between test cases
  2. Storage state is properly reset between tests
✅ Verification successful

Let me search for test cases to verify test isolation and state reset.


Let me check how the in-memory storage is implemented to verify its state management.


Let me try a different search pattern to locate the storage implementation.


Storage implementation is safe for test isolation

The switch to EasyLocalizationInMemoryStorage is actually beneficial for test isolation:

  1. Each test group properly initializes its own storage instance in setUpAll
  2. The storage implementation is simple with in-memory variables that don't persist between tests
  3. Storage state is scoped to individual test groups through separate initialization

The implementation in easy_localization_in_memory_storage.dart shows it's just maintaining state in memory variables (_version and _locale) which are automatically cleaned up between test runs, ensuring proper test isolation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any test setup/teardown methods that might need updating
rg -A 5 "setUp|tearDown" test/

Length of output: 5486


Script:

#!/bin/bash
# Search for test cases in the context file to verify isolation
rg -B 2 -A 10 "testWidgets|test\(" test/easy_localization_context_test.dart

Length of output: 3877


Script:

#!/bin/bash
# Search for the implementation of EasyLocalizationInMemoryStorage
rg -A 10 "class EasyLocalizationInMemoryStorage" lib/

Length of output: 55


Script:

#!/bin/bash
# Search for in-memory storage implementation with a broader pattern
rg -A 10 "InMemoryStorage" .

Length of output: 9003

lib/src/easy_localization_app.dart (2)

5-5: LGTM: Import aligns with new storage functionality.

The addition of the easy_localization_storage import is appropriate for the new storage management functionality being introduced.


163-164: ⚠️ Potential issue

Breaking change: Update documentation and consider null safety.

This modification to ensureInitialized introduces a breaking change that requires attention:

  1. The method now requires a storage parameter, which will affect all existing implementations
  2. The documentation above the method (lines 159-162) needs to be updated to reflect the new parameter
  3. Consider adding null safety checks for the storage parameter

Let's verify the impact of this breaking change:

Consider the following recommendations:

  1. Update the documentation to include:
    • Description of the storage parameter
    • Migration guide for existing implementations
    • Example usage with the new storage parameter
  2. Consider providing a default storage implementation to maintain backward compatibility
✅ Verification successful

Let me verify the documentation and null safety aspects with additional scripts:


Let me check one more aspect - the implementation of initEasyLocation method since it's being called:


Breaking change is properly documented and handled

The breaking change is already well-documented and implemented correctly:

  1. The CHANGELOG.md explicitly marks breaking changes
  2. The documentation above the method clearly states its initialization requirement
  3. The implementation in EasyLocalizationController properly handles the storage parameter:
    • Storage is set and validated
    • Error message exists for uninitialized state
    • Clear examples exist in README.md showing usage

No additional action is needed as the changes follow best practices for breaking changes with proper documentation, error handling, and migration examples.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing calls to ensureInitialized to assess migration needs
rg "EasyLocalization\.ensureInitialized\(\)" -A 2

Length of output: 885


Script:

#!/bin/bash
# Check the documentation of ensureInitialized method
rg -B 5 "static Future<void> ensureInitialized" lib/

# Check if there are any null checks in the implementation
ast-grep --pattern 'static Future<void> ensureInitialized(EasyLocalizationStorage storage) {
  $$$
}'

# Check the implementation of initEasyLocation
ast-grep --pattern 'static Future<void> initEasyLocation(EasyLocalizationStorage storage) {
  $$$
}'

Length of output: 717


Script:

#!/bin/bash
# Search for initEasyLocation implementation and its usage
rg "initEasyLocation" -A 5 lib/

Length of output: 1443

test/easy_localization_test.dart (4)

6-7: LGTM: New package imports align with PR objectives.

The addition of helper and storage packages aligns with the PR's goal of enhancing localization storage capabilities.


136-137: LGTM: Clean transition to in-memory storage.

The initialization properly uses the new storage mechanism while maintaining the same functionality.


156-157: LGTM: Good coverage of fallback locale behavior.

The test properly verifies the fallback behavior when a saved locale is no longer supported.


Line range hint 554-678: LGTM: Comprehensive testing of empty resource handling.

The test cases thoroughly verify:

  • Empty resource fallback behavior
  • Proper error reporting
  • Edge cases in plural form handling
test/easy_localization_widget_test.dart (2)

6-6: LGTM: Import added for the new storage package.

The import of easy_localization_storage is correctly placed and aligns with the PR objectives of introducing new storage components.


78-78: LGTM: Initialization updated to use in-memory storage.

The EasyLocalization.ensureInitialized() call has been correctly updated to use the new storage mechanism.

lib/src/easy_localization_controller.dart (1)

240-241: ⚠️ Potential issue

Fix variable interpolation in the logging statement

The logging statement escapes the dollar signs, preventing variable interpolation. Remove the backslashes to correctly interpolate the variables.

Apply this diff to fix the interpolation:

- 'Reset locale to \$locale while the platform locale is \$_deviceLocale and the fallback locale is \$_fallbackLocale');
+ 'Reset locale to $locale while the platform locale is $_deviceLocale and the fallback locale is $_fallbackLocale');

Likely invalid or redundant comment.

Comment on lines +11 to +18
extension XEasyLocalizationStorage on EasyLocalizationStorage {
Future reset() {
return Future.value([
setVersion(value: null),
setLocale(value: null),
]);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix concurrent operations handling and naming convention.

The current implementation has two issues:

  1. The reset method doesn't wait for the futures to complete
  2. The 'X' prefix in the extension name doesn't follow Dart naming conventions

Apply this diff to fix the issues:

-extension XEasyLocalizationStorage on EasyLocalizationStorage {
-  Future reset() {
-    return Future.value([
-      setVersion(value: null),
-      setLocale(value: null),
-    ]);
+extension EasyLocalizationStorageExtension on EasyLocalizationStorage {
+  /// Resets both version and locale to null.
+  Future<void> reset() async {
+    await Future.wait([
+      setVersion(value: null),
+      setLocale(value: null),
+    ]);
   }
 }

This change:

  1. Uses Future.wait to properly await both operations
  2. Follows Dart naming conventions for extensions
  3. Adds return type void for clarity
  4. Adds documentation
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
extension XEasyLocalizationStorage on EasyLocalizationStorage {
Future reset() {
return Future.value([
setVersion(value: null),
setLocale(value: null),
]);
}
}
extension EasyLocalizationStorageExtension on EasyLocalizationStorage {
/// Resets both version and locale to null.
Future<void> reset() async {
await Future.wait([
setVersion(value: null),
setLocale(value: null),
]);
}
}

Comment on lines +22 to +27
@override
Future<Locale?> getLocale() async => _locale;

@override
Future<void> setLocale({Locale? value}) async => _locale = value;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding locale validation.

The setLocale method accepts any locale without validation. Consider adding checks for:

  • Null locale handling strategy
  • Invalid locale formats
  • Unsupported locales

Here's a suggested implementation with validation:

   @override
   Future<Locale?> getLocale() async => _locale;

   @override
-  Future<void> setLocale({Locale? value}) async => _locale = value;
+  Future<void> setLocale({Locale? value}) async {
+    if (value != null) {
+      // Validate locale format
+      if (value.languageCode.isEmpty) {
+        throw ArgumentError('Language code cannot be empty');
+      }
+      
+      // Optional: Add validation for supported locales
+      // if (!supportedLocales.contains(value)) {
+      //   throw ArgumentError('Unsupported locale: $value');
+      // }
+    }
+    _locale = value;
+  }

Committable suggestion was skipped due to low confidence.

Comment on lines +1 to +27
import 'dart:ui';

import 'easy_localization_storage.dart';

/// An in-memory implementation of [EasyLocalizationStorage].
/// Useful for testing.
class EasyLocalizationInMemoryStorage implements EasyLocalizationStorage {
int? _version = 1;
Locale? _locale;

@override
Future<int?> getVersion() async => _version;

@override
Future<void> setVersion({int? value}) async {
if (value != null && value < 0) {
throw ArgumentError('Value must be null or non-negative');
}
_version = value;
}

@override
Future<Locale?> getLocale() async => _locale;

@override
Future<void> setLocale({Locale? value}) async => _locale = value;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Missing unit tests for EasyLocalizationInMemoryStorage

While the implementation is used in integration tests across the codebase (easy_localization_test.dart, easy_localization_widget_test.dart, and easy_localization_context_test.dart), there are no dedicated unit tests for the storage implementation itself. The storage test file exists but is commented out and contains unrelated test code.

  • packages/easy_localization_storage/test/easy_localization_storage_test.dart: Add unit tests to verify:
    • Version getter/setter with valid and invalid values
    • Locale getter/setter functionality
    • Error handling for negative version values
🔗 Analysis chain

Verify test coverage for the storage implementation.

Since this is a test-focused implementation, we should ensure proper test coverage exists.


Let me gather more information about the test coverage by examining the actual test implementations.


Let me check if there are specific tests for the storage implementation itself, as the current results only show integration tests.


Let me check the content of the storage test file we found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for test files covering this implementation
echo "Searching for test files..."
fd -e dart test easy_localization_in_memory_storage

# Search for usage in test files
echo "Searching for usage in tests..."
rg "EasyLocalizationInMemoryStorage" -t dart

Length of output: 1536


Script:

#!/bin/bash
# Check the test implementations that use EasyLocalizationInMemoryStorage
echo "Checking test/easy_localization_test.dart..."
rg -A 10 "EasyLocalizationInMemoryStorage" test/easy_localization_test.dart

echo "\nChecking test/easy_localization_widget_test.dart..."
rg -A 10 "EasyLocalizationInMemoryStorage" test/easy_localization_widget_test.dart

echo "\nChecking test/easy_localization_context_test.dart..."
rg -A 10 "EasyLocalizationInMemoryStorage" test/easy_localization_context_test.dart

Length of output: 4125


Script:

#!/bin/bash
# Look for storage-specific tests
echo "Checking for storage-specific tests..."
fd -e dart test packages/easy_localization_storage

# Check the implementation details of storage tests if they exist
echo "Checking storage test implementations..."
rg -t dart "describe|test.*EasyLocalizationInMemoryStorage" packages/easy_localization_storage/test

Length of output: 408


Script:

#!/bin/bash
# Check the content of the storage test file
echo "Checking storage test file content..."
cat packages/easy_localization_storage/test/easy_localization_storage_test.dart

Length of output: 534

Comment on lines +25 to +43
@override
Future<int?> getVersion() async {
int? value;
try {
value = _store.getInt(versionKey);
} catch (_) {}
return value;
}

@override
Future<void> setVersion({int? value}) async {
try {
if (value == null) {
_store.remove(versionKey);
} else {
_store.setInt(versionKey, value);
}
} catch (_) {}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling in version management methods.

The empty catch blocks silently swallow all errors, which could make debugging difficult. Consider:

  1. Logging errors for debugging
  2. Handling specific exceptions
  3. Potentially propagating critical errors
   Future<int?> getVersion() async {
     int? value;
     try {
       value = _store.getInt(versionKey);
-    } catch (_) {}
+    } catch (e) {
+      debugPrint('Failed to get version: $e');
+    }
     return value;
   }

   Future<void> setVersion({int? value}) async {
     try {
       if (value == null) {
         _store.remove(versionKey);
       } else {
         _store.setInt(versionKey, value);
       }
-    } catch (_) {}
+    } catch (e) {
+      debugPrint('Failed to set version: $e');
+      rethrow;  // Consider rethrowing as this is a critical operation
+    }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@override
Future<int?> getVersion() async {
int? value;
try {
value = _store.getInt(versionKey);
} catch (_) {}
return value;
}
@override
Future<void> setVersion({int? value}) async {
try {
if (value == null) {
_store.remove(versionKey);
} else {
_store.setInt(versionKey, value);
}
} catch (_) {}
}
@override
Future<int?> getVersion() async {
int? value;
try {
value = _store.getInt(versionKey);
} catch (e) {
debugPrint('Failed to get version: $e');
}
return value;
}
@override
Future<void> setVersion({int? value}) async {
try {
if (value == null) {
_store.remove(versionKey);
} else {
_store.setInt(versionKey, value);
}
} catch (e) {
debugPrint('Failed to set version: $e');
rethrow; // Consider rethrowing as this is a critical operation
}
}

Comment on lines +45 to +67
@override
Future<Locale?> getLocale() async {
Locale? value;
try {
final raw = _store.getString(valueKey);
if (raw == null) {
return null;
}
return raw.toLocale();
} catch (_) {}
return value;
}

@override
Future<void> setLocale({Locale? value}) async {
try {
if (value == null) {
_store.remove(valueKey);
} else {
_store.setString(valueKey, value.toString());
}
} catch (_) {}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix error handling and simplify locale management methods.

Several issues to address:

  1. Empty catch blocks hide errors
  2. No validation of locale string format
  3. Redundant value variable in getLocale()
   Future<Locale?> getLocale() async {
-    Locale? value;
     try {
       final raw = _store.getString(valueKey);
       if (raw == null) {
         return null;
       }
+      // Validate locale format before parsing
+      if (!raw.contains('_') && !raw.contains('-')) {
+        debugPrint('Invalid locale format: $raw');
+        return null;
+      }
       return raw.toLocale();
-    } catch (_) {}
-    return value;
+    } catch (e) {
+      debugPrint('Failed to get locale: $e');
+      return null;
+    }
   }

   Future<void> setLocale({Locale? value}) async {
     try {
       if (value == null) {
         _store.remove(valueKey);
       } else {
         _store.setString(valueKey, value.toString());
       }
-    } catch (_) {}
+    } catch (e) {
+      debugPrint('Failed to set locale: $e');
+      rethrow;  // Consider rethrowing as this is a critical operation
+    }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@override
Future<Locale?> getLocale() async {
Locale? value;
try {
final raw = _store.getString(valueKey);
if (raw == null) {
return null;
}
return raw.toLocale();
} catch (_) {}
return value;
}
@override
Future<void> setLocale({Locale? value}) async {
try {
if (value == null) {
_store.remove(valueKey);
} else {
_store.setString(valueKey, value.toString());
}
} catch (_) {}
}
@override
Future<Locale?> getLocale() async {
try {
final raw = _store.getString(valueKey);
if (raw == null) {
return null;
}
// Validate locale format before parsing
if (!raw.contains('_') && !raw.contains('-')) {
debugPrint('Invalid locale format: $raw');
return null;
}
return raw.toLocale();
} catch (e) {
debugPrint('Failed to get locale: $e');
return null;
}
}
@override
Future<void> setLocale({Locale? value}) async {
try {
if (value == null) {
_store.remove(valueKey);
} else {
_store.setString(valueKey, value.toString());
}
} catch (e) {
debugPrint('Failed to set locale: $e');
rethrow; // Consider rethrowing as this is a critical operation
}
}

Comment on lines +28 to +29
easy_localization_storage:
path: ../packages/easy_localization_storage
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Missing dependency: easy_localization_helper

According to the PR objectives and AI summary, this PR introduces both easy_localization_storage and easy_localization_helper packages. However, the easy_localization_helper dependency is missing from the pubspec.yaml.

Add the missing dependency:

 easy_localization_storage:
   path: ../packages/easy_localization_storage
+easy_localization_helper:
+  path: ../packages/easy_localization_helper
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
easy_localization_storage:
path: ../packages/easy_localization_storage
easy_localization_storage:
path: ../packages/easy_localization_storage
easy_localization_helper:
path: ../packages/easy_localization_helper

Comment on lines 124 to 128
children: <Widget>[
FaIcon(FontAwesomeIcons.male),
const FaIcon(FontAwesomeIcons.male),
Switch(value: _gender, onChanged: switchGender),
FaIcon(FontAwesomeIcons.female),
const FaIcon(FontAwesomeIcons.female),
],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance gender switch accessibility.

The gender switch lacks proper accessibility labels which could make it difficult for screen readers.

Add semantic labels:

- const FaIcon(FontAwesomeIcons.male),
- Switch(value: _gender, onChanged: switchGender),
- const FaIcon(FontAwesomeIcons.female),
+ const FaIcon(FontAwesomeIcons.male, semanticsLabel: 'Male'),
+ Switch(
+   value: _gender,
+   onChanged: switchGender,
+   semanticLabel: 'Switch gender',
+ ),
+ const FaIcon(FontAwesomeIcons.female, semanticsLabel: 'Female'),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
children: <Widget>[
FaIcon(FontAwesomeIcons.male),
const FaIcon(FontAwesomeIcons.male),
Switch(value: _gender, onChanged: switchGender),
FaIcon(FontAwesomeIcons.female),
const FaIcon(FontAwesomeIcons.female),
],
children: <Widget>[
const FaIcon(FontAwesomeIcons.male, semanticsLabel: 'Male'),
Switch(
value: _gender,
onChanged: switchGender,
semanticLabel: 'Switch gender',
),
const FaIcon(FontAwesomeIcons.female, semanticsLabel: 'Female'),
],

Comment on lines 106 to 110
setUpAll(() async {
SharedPreferences.setMockInitialValues({
'locale': 'ar_DZ',
});
await EasyLocalization.ensureInitialized();
await EasyLocalization.ensureInitialized(
EasyLocalizationInMemoryStorage()
..setLocale(value: const Locale('ar', 'DZ')));
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update group name and improve test isolation

Several issues need attention:

  1. The group name "SharedPreferences deleteSaveLocale()" is outdated as it now uses in-memory storage
  2. The storage state might persist between tests, potentially causing interference

Consider these changes:

-    group('SharedPreferences deleteSaveLocale()', () {
+    group('EasyLocalizationStorage deleteSaveLocale()', () {
       setUpAll(() async {
         await EasyLocalization.ensureInitialized(
             EasyLocalizationInMemoryStorage()
               ..setLocale(value: const Locale('ar', 'DZ')));
       });
+      tearDown(() async {
+        // Reset storage state between tests
+        await EasyLocalization.ensureInitialized(EasyLocalizationInMemoryStorage());
+      });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setUpAll(() async {
SharedPreferences.setMockInitialValues({
'locale': 'ar_DZ',
});
await EasyLocalization.ensureInitialized();
await EasyLocalization.ensureInitialized(
EasyLocalizationInMemoryStorage()
..setLocale(value: const Locale('ar', 'DZ')));
});
group('EasyLocalizationStorage deleteSaveLocale()', () {
setUpAll(() async {
await EasyLocalization.ensureInitialized(
EasyLocalizationInMemoryStorage()
..setLocale(value: const Locale('ar', 'DZ')));
});
tearDown(() async {
// Reset storage state between tests
await EasyLocalization.ensureInitialized(EasyLocalizationInMemoryStorage());
});

Comment on lines +505 to +506
await EasyLocalization.ensureInitialized(EasyLocalizationInMemoryStorage()
..setLocale(value: const Locale('ar', 'DZ')));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

🛠️ Refactor suggestion

Based on the gathered evidence, I can now provide a final response:

Test isolation needs improvement to prevent state sharing

The codebase shows multiple test groups in easy_localization_widget_test.dart using setUpAll with EasyLocalizationInMemoryStorage initialization, but no corresponding tearDown blocks for cleanup. This confirms the original concern about potential state bleeding between test groups, specifically:

  • Three separate groups reuse the same storage initialization in setUpAll
    • 'SharedPreferences saveLocale' (two instances)
    • 'SharedPreferences deleteSaveLocale()'

The suggested refactoring in the original review comment is correct and necessary to ensure proper test isolation.

🔗 Analysis chain

Verify test isolation in setup blocks.

The storage initialization is repeated in multiple setUpAll blocks. This could potentially lead to state bleeding between test groups.

Consider refactoring to ensure proper test isolation:

  1. Use setUp instead of setUpAll to ensure a fresh storage instance for each test
  2. Or clear the storage state in tearDown
-  setUpAll(() async {
+  setUp(() async {
     await EasyLocalization.ensureInitialized(EasyLocalizationInMemoryStorage()
       ..setLocale(value: const Locale('ar', 'DZ')));
   });
+  tearDown(() async {
+    final storage = EasyLocalizationInMemoryStorage();
+    await storage.clear();
+  });

Also applies to: 536-537, 592-593


Let me analyze the test files further to understand the test group structure and potential state sharing.


Let me check if there are any tearDown blocks that might be handling cleanup.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all setUpAll blocks in test files
rg -A 5 "setUpAll\(" --type dart

Length of output: 5127


Script:

#!/bin/bash
# Search for test groups and their setUpAll blocks to understand isolation
rg -B 2 -A 10 "group\(.*\{" --type dart test/easy_localization_widget_test.dart

Length of output: 2107


Script:

#!/bin/bash
# Search for tearDown blocks in test files
rg "tearDown\(" -A 5 --type dart test/easy_localization_widget_test.dart

Length of output: 74

Comment on lines +16 to +17
throw StateError(
'EasyLocalizationController not initialized. Call initEasyLocation first.');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct method name from 'initEasyLocation' to 'initEasyLocalization'

The method name initEasyLocation seems inconsistent with the class name EasyLocalizationController. It should be initEasyLocalization to match the naming convention and avoid confusion.

Apply this diff to correct the method name and error message:

- throw StateError(
-     'EasyLocalizationController not initialized. Call initEasyLocation first.');
+ throw StateError(
+     'EasyLocalizationController not initialized. Call initEasyLocalization first.');
-static Future<void> initEasyLocation(EasyLocalizationStorage storage) async {
+static Future<void> initEasyLocalization(EasyLocalizationStorage storage) async {

Update all references to this method accordingly.

Also applies to: 219-221

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant