Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter localization without context

I am trying to localize my app in flutter. I created the needed string.arb files for the supported languages.

Why does AppLocalizations.of(context) need a context?

I simply want to access the named strings in the files/locales files/classes. At some point in the app I build a List and fill it later via overriding some fields with a separate class.

However, this class has no context but I want to use localized strings in it. Can I write a method which gets me the Localization of whatever String I put in?

like image 478
Salatgurke Avatar asked May 02 '20 16:05

Salatgurke


2 Answers

We can resolve this by using get_it easily, we can use the string anywhere after this setup.

  1. Install this to your vscode Flutter Intl VSCode Extension

  2. setup pubspec.yaml

    dependencies:
    flutter:
      sdk: flutter
    flutter_localizations:                          # Add this line
      sdk: flutter                                  # Add this line
    intl: ^0.17.0                                   # Add this line
    get_it: ^7.2.0                                  # Add this line
    
    
    flutter:
      uses-material-design: true
      generate: true                                # Add this line
    
    
    flutter_intl:                                   # Add this line
      enabled: true                                 # Add this line
      class_name: I10n                              # Add this line
      main_locale: en                               # Add this line
      arb_dir: lib/core/localization/l10n           # Add this line
      output_dir: lib/core/localization/generated   # Add this line
    
  3. Setup main.dart

    import 'package:component_gallery/core/localization/generated/l10n.dart';
    import 'package:component_gallery/locator.dart';
    import 'package:component_gallery/ui/pages/home.dart';
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_localizations/flutter_localizations.dart';
    
    void main() {
      setupLocator();
      runApp(App());
    }
    
    class App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          localizationsDelegates: [
            I10n.delegate,
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
            GlobalCupertinoLocalizations.delegate,
          ],
          supportedLocales: I10n.delegate.supportedLocales,
          localeResolutionCallback: (deviceLocale, supportedLocales) {
            if (supportedLocales
                .map((e) => e.languageCode)
                .contains(deviceLocale?.languageCode)) {
              return deviceLocale;
            } else {
              return const Locale('en', '');
            }
          },
          home: HomePage(),
        );
      }
    }
    
  4. setup locator.dart

    import 'package:component_gallery/core/services/navigation_service.dart';
    import 'package:get_it/get_it.dart';
    
    GetIt locator = GetIt.instance;
    
    void setupLocator() {
      locator.registerLazySingleton(() => I10n());
    }
    
    
  5. Use it with Get_it without context as

    final I10n _i10n = locator<I10n>();
    class MessageComponent extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Text(
          _i10n.sample,
          textAlign: TextAlign.center,
        );
      }
    }
    
    
like image 108
zap Avatar answered Sep 17 '22 13:09

zap


If you would prefer to not use a package then here is a solution that worked for me. Now the most common implementation of AppLocalizations I've seen usually has these two lines:

//.........
static const LocalizationsDelegate<AppLocalizations> delegate =
      _AppLocalizationsDelegate();

static AppLocalizations of(BuildContext context) {
  return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
//.........

The implementation of the delegate would look something like this:

class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
  const _AppLocalizationsDelegate();

  @override
  Future<AppLocalizations> load(Locale locale) async {
    AppLocalizations localizations = new AppLocalizations(locale);
    await localizations.load();

    return localizations;
  }

  //... the rest omitted for brevity
}

Notice the load method on the delegate returns a Future<AppLocalizations>. The load method is usually called once from main and never again so you can take advantage of that by adding a static instance of AppLocalizations to the delegate. So now your delegate would look like this:

class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
  const _AppLocalizationsDelegate();

  static AppLocalizations instance;

  @override
  Future<AppLocalizations> load(Locale locale) async {
    AppLocalizations localizations = new AppLocalizations(locale);
    await localizations.load();

    instance = localizations; // set the static instance here

    return localizations;
  }

  //... the rest omitted for brevity
}

Then on your AppLocalizations class you would now have:

//.........
static const LocalizationsDelegate<AppLocalizations> delegate =
      _AppLocalizationsDelegate();

static AppLocalizations of(BuildContext context) {
  return Localizations.of<AppLocalizations>(context, AppLocalizations);
}

static AppLocalizations get instance => _AppLocalizationsDelegate.instance; // add this
//.........

Now in your translate helper method you could have:

String tr(String key) {
    return AppLocalizations.instance.translate(key);
}

No context needed.

like image 45
chinloyal Avatar answered Sep 17 '22 13:09

chinloyal