ilteoood / flutter_i18n

I18n made easy, for Flutter!
MIT License
217 stars 57 forks source link

Use singleton instead of static #153

Closed moseskarunia closed 3 years ago

moseskarunia commented 3 years ago

Hello. first of all, great package. I just wanna ask. Is there any plan to update the function for translation to use singleton instead of static? The reason for this is to make it easier to stub in widget tests.

Proposed usage:

class MyWidget extends StatelessWidget {
  final FlutterI18n flutterI18n;

  MyWidget({@required this.flutterI18n});

  @override
  Widget build(BuildContext context) {
    return Text(
      flutterI18n.translate(context, 'label.myWidget.title');
    );
  }
}
/// Test file

class MockI18n extends Mock implements FlutterI18n {}

void main(){
  FlutterI18n i18n;

  setUp((){
    i18n = MockI18n();
  });

  test('should translate with key label.myWidget.title', () {
    // setup

    verify(i18n.translate(any, 'label.myWidget.title');
  });
}

Thanks!

ilteoood commented 3 years ago

Hi @moseskarunia, we don't have any plan for this kind of conversion. Did you take a look to our test suite? IMO to achieve what you need is sufficient to create a mocked translation loader.

Please, take a look here: https://github.com/ilteoood/flutter_i18n/blob/master/test/test_widget.dart

moseskarunia commented 3 years ago

Hi @ilteoood thanks for the reply. I want to ask a follow up question then. If I make it like this, is there any pitfall I should be wary of? Like new language won't get loaded if it's changed during app run, or something. Thanks!

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:flutter_i18n/utils/simple_translator.dart';

class Translator {
  final FlutterI18n instance;

  Translator(this.instance);

  String translate(final BuildContext context, final String key,
      {final String fallbackKey, final Map<String, String> translationParams}) {
    final SimpleTranslator simpleTranslator = SimpleTranslator(
      instance.decodedMap,
      key,
      instance.keySeparator,
      fallbackKey: fallbackKey,
      translationParams: translationParams,
      missingKeyTranslationHandler: (key) {
        instance.missingTranslationHandler(key, instance.locale);
      },
    );
    return simpleTranslator.translate();
  }
}

class MyWidget extends StatelessWidget {
  final Translator translator;

  const MyWidget({Key key, @required this.translator}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(translator.translate(context, 'label.myWidget.text'));
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyWidget(
        translator: Translator(
          Localizations.of<FlutterI18n>(context, FlutterI18n),
        ),
      ),
    );
  }
}

Basically I just modified your static translate function. (Omitted FlutterI18nDelegate declarations in MaterialApp for brevity)

ilteoood commented 3 years ago

Currently I don't know, but this is not the way that I imagined...

moseskarunia commented 3 years ago

Currently I don't know, but this is not the way that I imagined...

ok. thanks.

In case someone stumbled upon this thread, my solution is to build wrapper around translate, and make the wrapper as a dependency of my widgets.

class Translator {
  const Translator();

  String translate(
    final BuildContext context,
    final String key, {
    final String fallbackKey,
    final Map<String, String> translationParams,
  }) =>
      FlutterI18n.translate(context, key,
          fallbackKey: fallbackKey, translationParams: translationParams);
}

(The Translator class itself is not tested)