Repository: aissat/easy_localization

Issue:
strict countryCode problem
countryCode is the system region option which can be changed by the user, lots of users would set this region to where he lives. for example, in my system(macOS) language is en,  my region is chinese, then my deviceLocale is en_CN. i think countryCode is designed to be used for precisely specifying the language texts for each region. since user option of region can not be expected as in a range, it can be any option, but it's impossible to supply language texts for all regions (hundreds of countries). i think, it' better to fallback to the same language without country code first, then the global fallback option. 
For example, fallback locale is zh_CN(any other locale), when en_FR.json(a user who uses English and lives in France) is not found, it will fallback to zh_CN, but actually it's better to fallback to en.json(the same language without country code)

For a temporary solution, i use Locale('en') instead of Locale('en', 'US') and set useOnlyLangCode: true. 
- use Locale('en'), because the countryCode==null will make the locale.supports(anotherLocale) skips the countryCode check (i think it's better to skip this check by useOnlyLangCode == true)
- useOnlyLangCode: true to make it loading the en.json instead of en_REGION.json

But in this solution, it's not possible to specify the language texts for each region, for example, en_US, en_GB


Discussion: Thanks for describing this issue, it definitely makes sense.
Let me rephrase:

Given that en_US and zh_CN are the supported languages and zh_CN is the fallback language.
A user has en_FR as its device locale.

Right now, the fallback zh_CN would be used.

It would be better to check if there is any supported locale that matches the language, in this case en_US, and use this.

It would be great if you submit a PR for this! :)
> Thanks for describing this issue, it definitely makes sense. Let me rephrase:
> 
> Given that en_US and zh_CN are the supported languages and zh_CN is the fallback language. A user has en_FR as its device locale.
> 
> Right now, the fallback zh_CN would be used.
> 
> It would be better to check if there is any supported locale that matches the language, in this case en_US, and use this.
> 
> It would be great if you submit a PR for this! :)

sorry for not being able to submit a PR for this issue, not familiar with the code and really have no time to completely figure this out and test. Another 2 PRs of simple modification had been submitted, and thanks for your work in this repo, which did help a lot

Fix patch:
diff --git a/lib/src/easy_localization_controller.dart b/lib/src/easy_localization_controller.dart
index 494c344b..2e62e2f8 100644
--- a/lib/src/easy_localization_controller.dart
+++ b/lib/src/easy_localization_controller.dart
@@ -72,14 +72,29 @@ class EasyLocalizationController extends ChangeNotifier {
   }) {
     final selectedLocale = supportedLocales.firstWhere(
       (locale) => locale.supports(deviceLocale),
-      orElse: () => _getFallbackLocale(supportedLocales, fallbackLocale),
+      orElse: () => _getFallbackLocale(
+        supportedLocales,
+        fallbackLocale,
+        deviceLocale: deviceLocale,
+      ),
     );
     return selectedLocale;
   }
 
   //Get fallback Locale
   static Locale _getFallbackLocale(
-      List<Locale> supportedLocales, Locale? fallbackLocale) {
+      List<Locale> supportedLocales, Locale? fallbackLocale,
+      {final Locale? deviceLocale}) {
+    if (deviceLocale != null) {
+      // a locale that matches the language code of the device locale is
+      // preferred over the fallback locale
+      final deviceLanguage = deviceLocale.languageCode;
+      for (Locale locale in supportedLocales) {
+        if (locale.languageCode == deviceLanguage) {
+          return locale;
+        }
+      }
+    }
     //If fallbackLocale not set then return first from supportedLocales
     if (fallbackLocale != null) {
       return fallbackLocale;


Test patch:
diff --git a/test/easy_localization_test.dart b/test/easy_localization_test.dart
index 59e21213..9078d9ef 100644
--- a/test/easy_localization_test.dart
+++ b/test/easy_localization_test.dart
@@ -225,6 +225,36 @@ void main() {
           zhHans,
         );
       });
+
+      test('select best lenguage match if no perfect match exists', () { // #674
+        const userDeviceLocale = Locale('en', 'FR');
+        const supportedLocale1 = Locale('en', 'US');
+        const supportedLocale2 = Locale('zh', 'CN');
+
+        expect(
+          EasyLocalizationController.selectLocaleFrom(
+            [supportedLocale1, supportedLocale2],
+            userDeviceLocale,
+            fallbackLocale: supportedLocale2,
+          ),
+          supportedLocale1,
+        );
+      });
+
+      test('select perfect match if exists', () { // #674
+        const userDeviceLocale = Locale('en', 'GB');
+        const supportedLocale1 = Locale('en', 'US');
+        const supportedLocale2 = userDeviceLocale;
+
+        expect(
+          EasyLocalizationController.selectLocaleFrom(
+            [supportedLocale1, supportedLocale2],
+            userDeviceLocale,
+            fallbackLocale: supportedLocale2,
+          ),
+          supportedLocale2,
+        );
+      });
     });
 
     group('tr', () {