aissat / easy_localization

Easy and Fast internationalizing your Flutter Apps
https://pub.dev/packages/easy_localization
MIT License
923 stars 329 forks source link

setLocale() or context.locale=Locale("...") is not Updating app locale instantly #370

Closed red-star25 closed 1 year ago

red-star25 commented 3 years ago

I've updated easy_localization: 3.0.0 package from easy_localization: ^2.3.3. When i was using easy_localization: ^2.3.3 it is updating my app locale instantly whenever i called

setState(() {
   context.locale = Locale("en");
});

But since I've updated the package version to 3.0.0 it is not updating the locale instantly. I've to hot restart the whole app to see the changes.

Here is what im doing in version 3.0.0

onTap: () async { await homeController .setLanguage("EN") .then((value) async { await context .setLocale( const Locale( "en")); }) },

And here is what im doing in version 2.3.3 (which is working,and updating locale instantly) onTap: () { homeController .setLanguage("EN"); setState(() { context.locale = const Locale( "en"); }); },

Faaatman commented 2 years ago

This solution worked for me

byazit commented 2 years ago

Found the solution. When you change the language use this:

context.setLocale(locale); Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (BuildContext context) => Login()), ModalRoute.withName('/') );

oussamabelabbas commented 2 years ago

For anyone who's still struggling with this problem. I found a workaround that works for me (at least for now) but I don't know if It may cost some performance issues (in fact I would appreciate an answer to that question)

while I was trying to figure out a solution I found out that some of my widgets are rebuilt when changing the langue while others are not. so the question became why these widgets are being rebuilt instead of why those are not being rebuilt...

The short answer is context.

The only widgets that are rebuilt in my app and their language are changed are widgets that are depending on context.

here's an example of what I mean.

This is the code of a text widget that was being rebuilt and language being changed normally Text("best_sellings".tr(), style: Theme.of(context).textTheme.headlineSmall),

and this is the widget that weren't changing at all ( until I hot reload ) ElevatedButton.icon( label: Text("explore_all_products".tr(), overflow: TextOverflow.ellipsis), icon: const Icon(Icons.manage_search_rounded), onPressed: () => context.router.push(const ExploreScreenRoute()), ),

the solution that made the button being forced to rebuild after language change is adding this line to the ElevatedButton style: Theme.of(context).elevatedButtonTheme.style,

Again, I don't know if this is costly in performancee but it's doing the job for now. I am not sure this will work when changing between two languages that have the same directionality. my case is between arabic(ar) and english(en)

FelixMittermeier commented 2 years ago

All of the solutions provided above somehow did not work for me and I wasted a whole night for this but I think I found a fix for it finally. In the end it is surprisingly simple but you have to know it - like nearly every time.

For some reasons (which are not obvious to me) the MaterialApp widget somehow keeps its state even if the top level widget gets rebuild. That is the difference to GetMaterialApp what is the reason while there are some solutions for it above. I am using GetX but only some features and therefore still have the default MaterialApp in my widget tree.

Long story short: add key: UniqueKey() to your MaterialApp. Then calling context.setLocale() also leads to a complete rebuild of the MaterialApp widget and now the new locale is applied.

Maybe it helps someone out there who also struggled with this issue for hours :-)

alvindrakes commented 2 years ago

All of the solutions provided above somehow did not work for me and I wasted a whole night for this but I think I found a fix for it finally. In the end it is surprisingly simple but you have to know it - like nearly every time.

For some reasons (which are not obvious to me) the MaterialApp widget somehow keeps its state even if the top level widget gets rebuild. That is the difference to GetMaterialApp what is the reason while there are some solutions for it above. I am using GetX but only some features and therefore still have the default MaterialApp in my widget tree.

Long story short: add key: UniqueKey() to your MaterialApp. Then calling context.setLocale() also leads to a complete rebuild of the MaterialApp widget and now the new locale is applied.

Maybe it helps someone out there who also struggled with this issue for hours :-)

@FelixMittermeier Your solutions works but if the app is wrapped inside GetMaterialApp. This soulution will restart the app instantly, which might not be ideal.

mrgzi commented 2 years ago

I solved this issue with push the same route and remove other routes after setting locale.

await context.setLocale(Locale('ru'));
await Navigator.pushNamedAndRemoveUntil(context, '/main', (route) => false);
satyaattili commented 2 years ago

The below workaround works for me

localeResolutionCallback: (locale, supportedLocales) { if (supportedLocales.contains(locale)) { context.setLocale(locale!); return locale; } else { context.setLocale(supportedLocales.first); return supportedLocales.first; } },

The above code need to add for material app, this changes app launguage on ddevice launguage changed.

quydn720 commented 2 years ago

Hmm, I also have this problem. I have a list view with my custom widget tile, It's only change the widget without the const keyword, I can't get this off. example @override Widget build(BuildContext context, WidgetRef ref) { return ListView( children: [ const ProfileTile(), const LanguageTile(), const SignOutTile(), ], ); }

quydn720 commented 2 years ago

Hmm, I also have this problem. I have a list view with my custom widget tile, It's only change the widget without the const keyword, I can't get this off. example @override Widget build(BuildContext context, WidgetRef ref) { return ListView( children: [ const ProfileTile(), const LanguageTile(), const SignOutTile(), ], ); }

If I remove the const, it will be redrawn and change the text, wonder why

mrgzi commented 2 years ago

Hmm, I also have this problem. I have a list view with my custom widget tile, It's only change the widget without the const keyword, I can't get this off. example @override Widget build(BuildContext context, WidgetRef ref) { return ListView( children: [ const ProfileTile(), const LanguageTile(), const SignOutTile(), ], ); }

If I remove the const, it will be redrawn and change the text, wonder why

@quydn720 Yes, I was trying to change language in a dialog. When I removed const, it changed. But for some reason, it was setting the previous locale's strings in the dialog, it sets the other widgets on the screen correctly. I solved this using setState.

    await context.setLocale(Locale('ru'));
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      setState(() {});
    });
Davywiz commented 2 years ago

I solved this by wrapping MaterialApp with a BlocListener, then calling listener: (context, state) async { await context.setLocale(state.locale); }

saifalyiu7995 commented 2 years ago

my texts/Strings are not updating while it is switching to ltr to rtl, but somehow unable to switch texts on screen. I can see the change after hot reload.

EhabSalah commented 2 years ago

Any updates according this issue?

Randomly the layout direction changes but the text translation keep the same! 🤷🏻‍♂️

manoj-gslab commented 2 years ago

Getting this error? any idea?

Unhandled Exception: 'package:easy_localization/src/easy_localization_app.dart': Failed assertion: line 216 pos 14: 'parent.supportedLocales.contains(locale)': is not true.

thinhlh commented 2 years ago

For me, I was missing supportedLocales: ctx.supportedLocales inside MaterialApp setup

htetaunglin commented 2 years ago

Just posting again to say that this actually works: https://stackoverflow.com/questions/43778488/how-to-force-flutter-to-rebuild-redraw-all-widgets

@override
Widget build(BuildContext context) { 
  rebuildAllChildren(context);
  return MaterialApp( ....
}

void rebuildAllChildren(BuildContext context) {
  void rebuild(Element el) {
    el.markNeedsBuild();
    el.visitChildren(rebuild);
  }
  (context as Element).visitChildren(rebuild);
}

It was posted by the author of the i18n_extension ( https://pub.dev/packages/i18n_extension ) and even though it shouldn't be recommended it seems to be the only way to inmediately refresh the widget tree to show the new locale properly.

I'm fine with this solution.

mosaabseta commented 2 years ago

I have found a possible solution.

In case you are using GetMaterialApp you should when changing the current language call Get.updateLocale() to inform GetMaterialApp about the language change

final _newLocale = Locale('ar', 'EG')
await context.setLocale(_newLocale); // change `easy_localization` locale
Get.updateLocale(_newLocale); // change `Get` locale direction

this one worked for thank you

amrahmed242 commented 2 years ago

For anyone who is not using GetX the solution is basically calling

final engine = WidgetsFlutterBinding.ensureInitialized();
engine.performReassemble();

and that's what GetX basically do when changing locales as @AhmedAbouelkher mentioned above code

rakib205 commented 1 year ago

same here. Some texts are not changing. Need to refresh the app for change. Not using Get. Any solution?

rakib205 commented 1 year ago

For anyone who is not using GetX the solution is basically calling

final engine = WidgetsFlutterBinding.ensureInitialized();
engine.performReassemble();

and that's what GetX basically do when changing locales as @AhmedAbouelkher mentioned above code

This is working for me.

MiladAtef commented 1 year ago

For anyone who is not using GetX the solution is basically calling

final engine = WidgetsFlutterBinding.ensureInitialized();
engine.performReassemble();

and that's what GetX basically do when changing locales as @AhmedAbouelkher mentioned above code

This worked for me , thanks.

MoHortus commented 1 year ago

EDIT: ok actually things dont work after second click of the button as they should. It is just that as I am switching between only 2 languages. Everytime after context.setLocale, "tr()" is still using old values of context.locale (or whole context?). So tr() is kind of all the time one step behind...

Similar problem still on 3.0.1. After the startup value is ok. But then I have a button to switch value to other language. on first click value does not change. But on second and further clicks things work. Tried a few solutions above, nothing works.

In my case for example lets say I have on initialization correct values releated to "firstLocale". Then on first click of the button for language switch I have:

context.setLocale(secondLocale);
setState(() {
  message = messageId.tr();
});

Then if i go in debug mode and stop right after the last line of code, I check: context.locale which is "secondLocale". However, when I use messageId.tr() tr() returns translation for the firstLocale... So somehow tr() does not know yet of the update of context.locale...

But if I click on the button again, then things start to work...

MoHortus commented 1 year ago

OK, to make things work for me I downloaded the package for version 3.0.1, added it to the project locally and slightly changed easy_localization_controller.dart. Note: yes I have no idea if this is correct approach and did not test what would happen when having big json files. So any comment on that is very welcome.

So in easy_localization_controller.dart I added Localization.load to loadTranslations(). For Localization.load, I also had to add to the top import 'localization.dart'. So the whole loadTranslations() looks like this:

  Future loadTranslations() async {
    Map<String, dynamic> data;
    try {
      data = await loadTranslationData(_locale);
      _translations = Translations(data);

      // Added Localization.load. Also needed to add "import 'localization.dart'" at the top.
      Localization.load(_locale,translations:_translations);

      if (useFallbackTranslations && _fallbackLocale != null) {
        data = await loadTranslationData(_fallbackLocale!);
        _fallbackTranslations = Translations(data);

        // also added as the part of the fix. Did not test this, but I presume it should be here.
        Localization.load(_fallbackLocale!,fallbackTranslations:_fallbackTranslations);

      }
    } on FlutterError catch (e) {
      onLoadError(e);
    } catch (e) {
      onLoadError(FlutterError(e.toString()));
    }
  }

Then in my main.dart I have function to change language, which also have to have async. So part of the code which takes care for the language switch would be:

      await context.setLocale(changeToLocale);
      setState(() {
        message = messageId.tr();
      });

So now finally, tr() gets the right translation as assigned by the setLocale two lines before. Without the fix as mention above, it lags one step behind.

AmineZeroual commented 1 year ago

if you are using GetMaterialApp you have 2 solutions that worked for me : 1 : Add key in GetMaterialApp as UniquKey(); GetMaterialApp( key: UniqueKey(), useInheritedMediaQuery: true, locale:context.locale, localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales,

and it should work but it will rebuild the app tree. 2 : After setting the Locale you need to update the Get Local like this ; await context.setLocale(const Locale("en")); Get.updateLocale( const Locale("en")); and this will keep you on the same screen and update Locale over all the app

aissat commented 1 year ago

Duplicate of #448 #552

ViktorKirjanov commented 1 year ago

the only solution i found is to add context.setLocale(context.locale) in the beginning of build method if you widget does not want to change the locale

...
@override
  Widget build(BuildContext context) {
    context.setLocale(context.locale);
    return ...
dimashimko commented 1 year ago

I solved it when wrapped the BottomNavigationBar to Localizations.override bottomNavigationBar: Localizations.override( context: context, locale: context.locale, child: CustomBottomNavigationBar( ), ), It is also important that the elements are inside build method... for example: Widget build(BuildContext context) { List<_BottomNavigationBarItem> items = [ _BottomNavigationBarItem( iconSelectedPath: AppIcons.report_dark, iconNoSelectedPath: AppIcons.report_white, title: LocaleKeys.report.tr(), ), _BottomNavigationBarItem( iconSelectedPath: AppIcons.home_dark, iconNoSelectedPath: AppIcons.home_white, title: LocaleKeys.home.tr(), ), _BottomNavigationBarItem( iconSelectedPath: AppIcons.settings_dark, iconNoSelectedPath: AppIcons.settings_white, title: LocaleKeys.settings.tr(), ), ]; ... }

ptiszai commented 1 year ago

Solved. Using a DropdownButton to set locale

onChanged: (value) async { if (value != null) { await context.setLocale(value); setState(() {}); // very importend } },

AmarchandK commented 1 year ago

I have found a possible solution.

In case you are using GetMaterialApp you should when changing the current language call Get.updateLocale() to inform GetMaterialApp about the language change

final _newLocale = Locale('ar', 'EG')
await context.setLocale(_newLocale); // change `easy_localization` locale
Get.updateLocale(_newLocale); // change `Get` locale direction

it works man

abasu0713 commented 1 year ago

For people coming back to this problem.. You wouldn't believe the fix is so easy. I had the exact same problem.

Simply do this:

onChanged: (value) async { await context.setLocale(Locale.fromSubtags(languageCode: value!.languageCode, countryCode: value.countryCode)); setState((){}); }

Remember to call the set state after the setLocale finishes. It's of type Future<void> and you need to wait. And call setState() after it finishes.

abasu0713 commented 1 year ago

I have found a possible solution. In case you are using GetMaterialApp you should when changing the current language call Get.updateLocale() to inform GetMaterialApp about the language change

final _newLocale = Locale('ar', 'EG')
await context.setLocale(_newLocale); // change `easy_localization` locale
Get.updateLocale(_newLocale); // change `Get` locale direction

it works man

Upvoting because of your async answer!

yagizdo commented 1 year ago

I am currently using Easy Localization 3.0.1 . I tried most of the above solutions.

The method of deleting Const worked for me, but I didn't use it because deleting it throughout the entire application could cause issues. The approach of adding UniqueKey() to MaterialApp also worked, but I didn't like it because it refreshed the entire app as if it had crashed.

But the engine method at the bottom works flawlessly for now. Thank you!

For anyone who is not using GetX the solution is basically calling

final engine = WidgetsFlutterBinding.ensureInitialized();
engine.performReassemble();

and that's what GetX basically do when changing locales as @AhmedAbouelkher mentioned above code

markfili commented 1 year ago

... the engine method at the bottom works flawlessly for now.

Calling performReassemble

await context.setLocale(locale);
await WidgetsBinding.instance.performReassemble();

works, but at what cost? It says it rebuilds the whole application which is not a big deal if you're 2 screens in but if you're 50 screens away and you change your language from, let's say, a drawer, what could happen?

yagizdo commented 1 year ago

... the engine method at the bottom works flawlessly for now.

Calling performReassemble

await context.setLocale(locale);
await WidgetsBinding.instance.performReassemble();

works, but at what cost? It says it rebuilds the whole application which is not a big deal if you're 2 screens in but if you're 50 screens away and you change your language from, let's say, a drawer, what could happen?

I agree with what you said, but this solution worked best among the ones I tried.

amrahmed242 commented 1 year ago

... the engine method at the bottom works flawlessly for now.

Calling performReassemble

await context.setLocale(locale);
await WidgetsBinding.instance.performReassemble();

works, but at what cost? It says it rebuilds the whole application which is not a big deal if you're 2 screens in but if you're 50 screens away and you change your language from, let's say, a drawer, what could happen?

Ideally speaking this would not be good if you navigated 50 routes, practically i can't see a real case scenario were that can happen.

Frankdroid7 commented 1 year ago

I have found a possible solution.

In case you are using GetMaterialApp you should when changing the current language call Get.updateLocale() to inform GetMaterialApp about the language change

final _newLocale = Locale('ar', 'EG')
await context.setLocale(_newLocale); // change `easy_localization` locale
Get.updateLocale(_newLocale); // change `Get` locale direction

Thanks alot, this worked!

SamedHrmn commented 1 year ago

Hi, Is there any update for handle rebuilding internally after setLocale? The solutions I've read about don't seem to be suitable for production applications, and I don't want to use any third-party packages either.

Miguel-A-Jara commented 1 year ago

Well... after trying many things out. I finally found a solution:

Use:

final myTranslatedHelloWorld = context.tr("my_awesome_key");

Instead of any of these

final myTranslatedHelloWorld = "my_awesome_key".tr();
final myTranslatedHelloWorld = tr("my_awesome_key");
final myTranslatedHelloWorld = Text("my_awesome_key").tr();

Or...

Pass context to your tr() functions.

final myTranslatedHelloWorld = "my_awesome_key".tr(context: context);
final myTranslatedHelloWorld = tr("my_awesome_key", context: context);
final myTranslatedHelloWorld = Text("my_awesome_key").tr(context: context);
ongyul commented 1 year ago

This solution worked for me

it is perfect

IbrahimAlqayyas commented 1 year ago

... the engine method at the bottom works flawlessly for now.

Calling performReassemble

await context.setLocale(locale);
await WidgetsBinding.instance.performReassemble();

works, but at what cost? It says it rebuilds the whole application which is not a big deal if you're 2 screens in but if you're 50 screens away and you change your language from, let's say, a drawer, what could happen?

Thank you, I was stuck for 3 months with this problem and thanks Allah he caused you to solve my problem.

Muhammed-Ayad commented 1 year ago

I solved it when wrapped the BottomNavigationBar to Localizations.override bottomNavigationBar: Localizations.override( context: context, locale: context.locale, child: CustomBottomNavigationBar( ), ), It is also important that the elements are inside build method... for example: Widget build(BuildContext context) { List<_BottomNavigationBarItem> items = [ _BottomNavigationBarItem( iconSelectedPath: AppIcons.report_dark, iconNoSelectedPath: AppIcons.report_white, title: LocaleKeys.report.tr(), ), _BottomNavigationBarItem( iconSelectedPath: AppIcons.home_dark, iconNoSelectedPath: AppIcons.home_white, title: LocaleKeys.home.tr(), ), _BottomNavigationBarItem( iconSelectedPath: AppIcons.settings_dark, iconNoSelectedPath: AppIcons.settings_white, title: LocaleKeys.settings.tr(), ), ]; ... }

it is perfect

MohamedSalama0 commented 10 months ago

For anyone who is not using GetX the solution is basically calling

final engine = WidgetsFlutterBinding.ensureInitialized();
engine.performReassemble();

and that's what GetX basically do when changing locales as @AhmedAbouelkher mentioned above code

this worked for me too, But is that can effect on app's performance ?

MoatazNoaman2001 commented 7 months ago

For anyone who is not using GetX the solution is basically calling

final engine = WidgetsFlutterBinding.ensureInitialized();
engine.performReassemble();

and that's what GetX basically do when changing locales as @AhmedAbouelkher mentioned above code

I done this but unfortunately no thing is changed

oguzarapkirli commented 6 months ago
    locale: context.locale,

Damn i feel really dumb rn. Thanks it's worked for me i just forgot to use context

lovenangelo commented 1 month ago

https://stackoverflow.com/questions/78921888/ui-not-updated-when-changing-locale-easy-localization

This one helped me, just passing context to the LocaleKeys generated class does the trick