How can you handle language selection from anywhere in the application?

Question

I followed your article link. It works for the main page but does not work for any other page.


Answer

The article I posted on April 2018 (link) suggests a global solution to handle multi-lingual applications.

Some of you experienced issues in implementing the initial solution, as a consequence I posted an updated solution in October 2018.

Meanwhile, I found another alternative which works in all circumstances, based on the combination of the solution #2 from the article and a BLoC.

For further information on BLoC, please refer to the articles on this topic:

Explanation

First the explanation, then the code…

Singleton - GlobalTranslations class

The solution is based on a Singleton class, I called “GlobalTranslations” which exposes its instance via a global variable “allTranslations”.

This class is responsible for:

  • loading any language preference made by the user
  • saving the language preference of a user
  • loading the different translations from assets files
  • setting the current working language
  • delivering the translation in the current language

BLoC - TranslationsBloc

This BLoC is responsible for handling the selection of a language. When a new language selection is made:

  • it requests to save the language as a preference
  • it invokes the GlobalTranslations instance to request it to load the new translations from the corresponding assets
  • it notifies whomever might be interested in knowing that a language has been selected

Singleton - Preferences class

This class is only meant to deal with Shared Preferences.

This class is optional and the management of the SharedPreferences could be done at the level of the GlobalTranslations class, however, as I personally prefer to split the responsabilities, I put all routines related to the SharedPreferences in a single place.

The Code

The “main.dart” simply initializes the translations and then launches the application.

The “application.dart”.

As you can see, the Application is a StatefulWidget as I want it to dispose the TranslationBloc when the application will be discarded (see dispose method).

We wrap the MaterialApp inside a BlocProvider and StreamBuilder so that each time a new language selection is made, the MaterialApp is rebuilt.

The “global_translations.dart” is the Singleton that handles everything related to the language selection and provisioning of a translation.

The “preferences.dart” is also a Singleton responsible for handling all SharedPreferences. (for additional information on Shared Preferences, refer to the following article)

The “translations_bloc.dart” is the BLoC that handles both the actions related to a new language selection and the refresh once a new language is selected.


How to use it?

The implementation is quite straightforward. The following code illustrates the following:

  • how to get a translation in the current language
  • how to change the language.

Please note that both actions can now be performed from anywhere in the application !


Assets and dependencies

The following packages are used:

  • intl
  • rxdart
  • shared_preferences

These dependencies need to be mentioned in the “pubspec.yaml” as follows:

dependencies:
  flutter:
    sdk: flutter

  flutter_localizations:
    sdk: flutter

  intl: ^0.15.5
  rxdart: ^0.21.0
  shared_preferences: ^0.4.2

The assets (one json file per language) need to be stored in a folder. I personally use the “/assets/locale/” folder to store these files. Their name have to stick to the following pattern: “locale_en.json” or “locale_fr.json” and need to be mentioned in the pubspec.yaml file too as follows:

flutter:

  uses-material-design: true

  assets:
    - assets/locale/locale_fr.json
    - assets/locale/locale_en.json

Example of asset content: “/assets/locale/locale_en.json“:

{
    "page": {
        "title": "My Page Title",
        "changeLanguage": "Tap this button to select 'French'"
    }
}

BlocProvider

For convenience, here is the BlocProvider


Conclusion

Here is a global solution that I personally use in all my projects.

I really hope you will find this useful.