aissat / easy_localization

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

Using Easy Localization witout context #210

Open Overman775 opened 4 years ago

pumuckelo commented 4 years ago

@Overman775 Hi Overmann, did you find a workaround to use localization outside of widgets?

Overman775 commented 4 years ago

@pumuckelo nope, have an idea, I’ll implement it later

ncuillery commented 4 years ago

Isn't that already done?

I see a tr function in public.dart that can be called without context argument.

VictorCamargo commented 4 years ago

That would be very helpful.

chunlee-thong commented 4 years ago

@ncuillery even tr function doesn't need context but using tr outside the widget tree doesn't work

6h4n3m commented 4 years ago

@ncuillery even tr function doesn't need context but using tr outside the widget tree doesn't work

Can you provide an example of your code and the errors you're getting? because the static tr() function works fine outside widget trees.

pumuckelo commented 4 years ago

but how does it get access to the language files

Overman775 commented 3 years ago

@aissat #321 may be delete Localization.of(context) from public.dart ?

Overman775 commented 3 years ago

context removed from tr() and plural() in PR #343

jonasN5 commented 3 years ago

Can someone give an implementation example? The only way I can think of at the moment is:

  1. Create an instance of EasyLocalizationController
  2. Call controller.loadTranslations()
  3. Create an instance of Localization
  4. Call localization.load(translations)
  5. Translate with localization.tr That would also mean exposing EasyLocalizationController and Translations and Localization.
jonasN5 commented 3 years ago

I finally got it to work. Someone should document the following somewhere, because it definitely is not currently. First off: findSystemLocale() will not work when called from an isolate in the background. Don't ask me why, I just had to debug that to find out it is the case. This is important, because therefore EasyLocalizationController.deviceLocale will not be set. Thus, you have to use the following code from an isolate:

Step 1:

Call context.resetLocale(); somewhere in MyApp.build(), because the currently documented saveLocale will not save anything anywhere. Calling resetLocale will actually save the deviceLocale to SharedPreferences, which we can retrieve even in an isolate.

Step 2: code executed in the isolate

import 'package:easy_localization/src/easy_localization_controller.dart';
import 'package:easy_localization/src/localization.dart';
  /// A manually created instance of Localization to enable translating without context.
  final Localization L = Localization.instance;
/// Method to load translations since context is not available in isolate.
  Future<void> loadTranslations() async {

    //this will only set EasyLocalizationController.savedLocale
    await EasyLocalizationController.initEasyLocation();

    final controller = EasyLocalizationController(
      saveLocale: true, //mandatory to use EasyLocalizationController.savedLocale
      fallbackLocale: AppConstants.SUPPORTED_LOCALES[0],
      supportedLocales: AppConstants.SUPPORTED_LOCALES,
      assetLoader: const RootBundleAssetLoader(),
      useOnlyLangCode: false,
      useFallbackTranslations: true,
      path: AppConstants.LOCALIZATION_ASSET_PATH,
      onLoadError: (FlutterError e) {
      },
    );

    //Load translations from assets
    await controller.loadTranslations();

    //load translations into exploitable data, kept in memory
    Localization.load(controller.locale, translations: controller.translations, fallbackTranslations: controller.fallbackTranslations);
  }

Step 3: now you can actually translate

L.tr(you_string_key);

TarekMedhat commented 3 years ago

I finally got it to work. Someone should document the following somewhere, because it definitely is not currently. First off: findSystemLocale() will not work when called from an isolate in the background. Don't ask me why, I just had to debug that to find out it is the case. This is important, because therefore EasyLocalizationController.deviceLocale will not be set. Thus, you have to use the following code from an isolate:

Step 1:

Call context.resetLocale(); somewhere in MyApp.build(), because the currently documented saveLocale will not save anything anywhere. Calling resetLocale will actually save the deviceLocale to SharedPreferences, which we can retrieve even in an isolate.

Step 2: code executed in the isolate

import 'package:easy_localization/src/easy_localization_controller.dart';
import 'package:easy_localization/src/localization.dart';
  /// A manually created instance of Localization to enable translating without context.
  final Localization L = Localization.instance;
/// Method to load translations since context is not available in isolate.
Future<void> loadTranslations() async {

  //this will only set EasyLocalizationController.savedLocale
  await EasyLocalizationController.initEasyLocation();

  final controller = EasyLocalizationController(
    saveLocale: true, //mandatory to use EasyLocalizationController.savedLocale
    fallbackLocale: AppConstants.SUPPORTED_LOCALES[0],
    supportedLocales: AppConstants.SUPPORTED_LOCALES,
    assetLoader: const RootBundleAssetLoader(),
    useOnlyLangCode: false,
    useFallbackTranslations: true,
    path: AppConstants.LOCALIZATION_ASSET_PATH,
    onLoadError: (FlutterError e) {
    },
  );

  //Load translations from assets
  await controller.loadTranslations();

  //load translations into exploitable data, kept in memory
  Localization.load(controller.locale, translations: controller.translations, fallbackTranslations: controller.fallbackTranslations);
}

Step 3: now you can actually translate

L.tr(you_string_key);

This solution worked but resetLocale saves the deviceLocale and inf deviceLocale wasn't in supportedLocale it will lead to an excpetion (ex: "assets/localizations/fr.json not found").

So instead of using resetLocale I used setLocale(context.locale), this will make sure to save the fallbackLocale instead of deviceLocale if the deviceLocale not supported

nkulic commented 3 years ago

I finally got it to work. Someone should document the following somewhere, because it definitely is not currently. First off: findSystemLocale() will not work when called from an isolate in the background. Don't ask me why, I just had to debug that to find out it is the case. This is important, because therefore EasyLocalizationController.deviceLocale will not be set. Thus, you have to use the following code from an isolate:

Step 1:

Call context.resetLocale(); somewhere in MyApp.build(), because the currently documented saveLocale will not save anything anywhere. Calling resetLocale will actually save the deviceLocale to SharedPreferences, which we can retrieve even in an isolate.

Step 2: code executed in the isolate

import 'package:easy_localization/src/easy_localization_controller.dart';
import 'package:easy_localization/src/localization.dart';
  /// A manually created instance of Localization to enable translating without context.
  final Localization L = Localization.instance;
/// Method to load translations since context is not available in isolate.
Future<void> loadTranslations() async {

  //this will only set EasyLocalizationController.savedLocale
  await EasyLocalizationController.initEasyLocation();

  final controller = EasyLocalizationController(
    saveLocale: true, //mandatory to use EasyLocalizationController.savedLocale
    fallbackLocale: AppConstants.SUPPORTED_LOCALES[0],
    supportedLocales: AppConstants.SUPPORTED_LOCALES,
    assetLoader: const RootBundleAssetLoader(),
    useOnlyLangCode: false,
    useFallbackTranslations: true,
    path: AppConstants.LOCALIZATION_ASSET_PATH,
    onLoadError: (FlutterError e) {
    },
  );

  //Load translations from assets
  await controller.loadTranslations();

  //load translations into exploitable data, kept in memory
  Localization.load(controller.locale, translations: controller.translations, fallbackTranslations: controller.fallbackTranslations);
}

Step 3: now you can actually translate

L.tr(you_string_key);

I am trying to implement your code but getting error Null check operator used on a null value pointing to context.resetLocale() method that I'm calling inside build() method

final Localization L = Localization.instance;

Future<void> loadTranslations() async {...}

Future main() async {
  await loadTranslations();

  // await EasyLocalization.ensureInitialized();
  runApp(
    App(store: store)

    // EasyLocalization(
    //   path: 'assets/translations',
    //   assetLoader: CodegenLoader(),
    //   supportedLocales: [
    //     Locale('en', 'US'),
    //     Locale('hr', 'HR'),
    //   ],
    //   child: App(store: store),
    // ),

   //.... 
  @override
  Widget build(BuildContext context) {
    context.resetLocale(); // error pointing here
Fintasys commented 3 years ago

I had the same issue as @nkulic and I had to change the assetLoader because I use CodeGen.

  import 'package:weddy/gen/codegen_loader.g.dart';
  ...
  assetLoader: const CodegenLoader(),

and I could remove context.resetLocale(); it wasn't necessary anymore.

nkulic commented 3 years ago

I had the same issue as @nkulic and I had to change the assetLoader because I use CodeGen.

  import 'package:weddy/gen/codegen_loader.g.dart';
  ...
  assetLoader: const CodegenLoader(),

and I could remove context.resetLocale(); it wasn't necessary anymore.

I ended up using a very recent and new package fast_i18n

dmrcierhn commented 2 years ago

I finally got it to work. Someone should document the following somewhere, because it definitely is not currently. First off: findSystemLocale() will not work when called from an isolate in the background. Don't ask me why, I just had to debug that to find out it is the case. This is important, because therefore EasyLocalizationController.deviceLocale will not be set. Thus, you have to use the following code from an isolate:

Step 1:

Call context.resetLocale(); somewhere in MyApp.build(), because the currently documented saveLocale will not save anything anywhere. Calling resetLocale will actually save the deviceLocale to SharedPreferences, which we can retrieve even in an isolate.

Step 2: code executed in the isolate

import 'package:easy_localization/src/easy_localization_controller.dart';
import 'package:easy_localization/src/localization.dart';
  /// A manually created instance of Localization to enable translating without context.
  final Localization L = Localization.instance;
/// Method to load translations since context is not available in isolate.
Future<void> loadTranslations() async {

  //this will only set EasyLocalizationController.savedLocale
  await EasyLocalizationController.initEasyLocation();

  final controller = EasyLocalizationController(
    saveLocale: true, //mandatory to use EasyLocalizationController.savedLocale
    fallbackLocale: AppConstants.SUPPORTED_LOCALES[0],
    supportedLocales: AppConstants.SUPPORTED_LOCALES,
    assetLoader: const RootBundleAssetLoader(),
    useOnlyLangCode: false,
    useFallbackTranslations: true,
    path: AppConstants.LOCALIZATION_ASSET_PATH,
    onLoadError: (FlutterError e) {
    },
  );

  //Load translations from assets
  await controller.loadTranslations();

  //load translations into exploitable data, kept in memory
  Localization.load(controller.locale, translations: controller.translations, fallbackTranslations: controller.fallbackTranslations);
}

Step 3: now you can actually translate

L.tr(you_string_key);

i have a similar problem and i tried this solution but im getting an error "MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)".

await EasyLocalizationController.initEasyLocation(); when this line is work i get this error. how can i solve this?

Musta-Pollo commented 2 years ago

Thanks a lot for your help. It worked for me :heart:.

I wanted to get working translations in the background thread when using awesome_notifications—adding this to execute in the background thread when the main thread is dead worked for me.

await EasyLocalization.ensureInitialized();
      final controller = EasyLocalizationController(
        saveLocale: true,
        fallbackLocale: const Locale('en'),
        supportedLocales: const [
          Locale('en'),
          Locale('cs'),
        ],
        assetLoader: const CodegenLoader(),
        useOnlyLangCode: false,
        useFallbackTranslations: true,
        path: '/assets/translations/',
        onLoadError: (FlutterError e) {},
      );

      //Load translations from assets
      await controller.loadTranslations();

      //load translations into exploitable data, kept in memory
      Localization.load(controller.locale,
          translations: controller.translations,
          fallbackTranslations: controller.fallbackTranslations);

And then this worked normally: LocaleKeys.notifications_your_quote.tr()

In context:

static Future<void> onActionReceivedMethod(
      ReceivedAction receivedAction) async {
    SendPort? port = IsolateNameServer.lookupPortByName('main');

    if (port != null) {
      port.send(receivedAction);
    } else {
      if ((UniversalPlatform.isAndroid || UniversalPlatform.isIOS)) {
        var quotesData = data;
        if (await getLocaleIndex() == 1) {
          quotesData = data_cz;
        }
        parseAllQuotes(quotesData).then((value) => quotesProvider = value);
      }
      await Hive.initFlutter();
      Hive.registerAdapter(HabitAdapter());
      Hive.registerAdapter(RewardAdapter());
      Hive.registerAdapter(QuoteAdapter());
      await Hive.openBox<Habit>(habitsBoxName);
      await Hive.openBox(settingsBoxName);
      await Hive.openBox<Reward>(rewardsBoxName);
      await Hive.openBox<Quote>(favouriteQuotesBoxName);
      await EasyLocalization.ensureInitialized();
      final controller = EasyLocalizationController(
        saveLocale: true,
        fallbackLocale: const Locale('en'),
        supportedLocales: const [
          Locale('en'),
          Locale('cs'),
        ],
        assetLoader: const CodegenLoader(),
        useOnlyLangCode: false,
        useFallbackTranslations: true,
        path: '/assets/translations/',
        onLoadError: (FlutterError e) {},
      );

      //Load translations from assets
      await controller.loadTranslations();

      //load translations into exploitable data, kept in memory
      Localization.load(controller.locale,
          translations: controller.translations,
          fallbackTranslations: controller.fallbackTranslations);
      await AwesomeNotifications().initialize(
        'resource://drawable/ic_stat_onesignal_default',
        [
          NotificationChannel(
            channelKey: getChannelNameByType(NotificationSettings.habits),
            channelName: getChannelNameByType(NotificationSettings.habits),
            channelDescription:
                LocaleKeys.notifications_habits_notifications.tr(),
            defaultColor: primaryColor_74,
            ledColor: Colors.blue,
            enableLights: true,
            importance: NotificationImportance.High,
            channelShowBadge: true,
          ),
          NotificationChannel(
            channelKey: getChannelNameByType(NotificationSettings.quotes),
            channelName: getChannelNameByType(NotificationSettings.quotes),
            importance: NotificationImportance.Default,
            channelDescription:
                LocaleKeys.notifications_quotes_notifications.tr(),
            defaultColor: secondaryColorGreen_43,
            ledColor: Colors.green,
          ),
          NotificationChannel(
            channelKey: getChannelNameByType(NotificationSettings.rewards),
            channelName: getChannelNameByType(NotificationSettings.rewards),
            importance: NotificationImportance.Default,
            channelDescription:
                LocaleKeys.notifications_rewards_notifications.tr(),
            defaultColor: Colors.black,
            ledColor: Colors.green,
          ),
        ],
        // debug: true,
      );
      await doneHabit(receivedAction);
    }
  }
bw-flagship commented 1 year ago

Looks like we don't need code here, but just add one of the solutions to the documentation and maybe to the example

armandsLa commented 6 months ago

It's not necessary to always load the Localization when running in isolate. The instance can now be passed as an input parameter. The only situation when it's required to load Localization is when running from another Dart entry point.

Import Localization class: import 'package:easy_localization/src/localization.dart';

Pass the Localization.instance as input parameter: await compute(methodToRun, Localization.instance);

Get the translation: localization.tr(<key>)

@bw-flagship it would be great if Localization could be accessible without the need to import it specifically.

bw-flagship commented 6 months ago

I see, thanks for the explanation! Feel free to open a pr :)

Mistic92 commented 6 months ago

Hi, I'm trying to use EasyLocalization with WorkManager. When I try method with using EasyLocalizationController looks like there is no such class. Is this solution up to date?

bw-flagship commented 6 months ago

@Mistic92 This solution is up-to-date, but I have no experience with work manager and can't tell you if this should work out of the box

Mistic92 commented 6 months ago

@bw-flagship it worked, I had to explicitly import from

import 'package:easy_localization/src/localization.dart';
import 'package:easy_localization/src/easy_localization_controller.dart';

as Android Studio didn't want to import from that location. Even now I'm getting warning but solution works

Import of a library in the 'lib/src' directory of another package. (Documentation)  Try importing a public library that exports this library, or removing the import.
bw-flagship commented 6 months ago

@Mistic92 got it. Then #669 will work for you. However, it seems like it's not worked on at the moment

hasanm08 commented 3 months ago

use this to solve https://github.com/aissat/easy_localization/issues/407#issuecomment-2301633442