Husam Alhwadi
Husam Alhwadi

Reputation: 531

passing dynamic strings to AppLocalizations.of(context)! in Flutter

right now Im working in App using Flutter and I have 4 different languages, I use json (arb files) for localization (translation)

I need to pass different string values which app fetch them using API's as shown in example below

AppLocalizations.of(context)!snapshot.data![index].state_pickup[0]

however "AppLocalizations.of(context)!" doesn't fetch the return data from snapshot.data![index].state_pickup[0] and instead it looks for it as string and tries to search for match string name in AppLocalization.dart class?

Any idea how I can pass dynamic string arguments to AppLocalizations.of(context)!?

Upvotes: 15

Views: 11683

Answers (5)

Mergen Taganow
Mergen Taganow

Reputation: 1

I also had that problem and solved it like this:

class DynamicLocalization {
  static String? arbContent;
  static Map<String, dynamic>? localizedMap = {};

  static init(Locale? locale) async {
    var filePath = 'lib/l10n/app_${locale?.languageCode ?? 'en'}.arb';
    arbContent = await rootBundle.loadString(filePath);
    localizedMap = jsonDecode(arbContent!) as Map<String, dynamic>;
  }

  static String translate(String key) {
    return localizedMap?[key] ?? key;
  }
}

and using it like

Text(DynamicLocalization.translate("dynamicKey"))

but before using need to do this steps

1st: init DynamicLocalization when you give initial locale to your material app:

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  static Locale? appLocale = const Locale('tr');

  @override
  void initState() {
    DynamicLocalization.init(appLocale); //here when app initializing
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: appLocale,
    );
  }
}

2nd: init DynamicLocalization when you change your language:

setLocale(BuildContext context, Locale newLocale) {
    DynamicLocalization.init(newLocale);
    ...your locale changing logic
    }

3rd: add your localization files to assets inside pubspec.yaml file:

assets:
    - lib/l10n/app_en.arb
    - lib/l10n/app_ru.arb
    - lib/l10n/app_tr.arb

explanation: init function will load the content of your .arb file as Map. translate function will try to return your key's value from that map. The cause why I made a init function is if you trying get content of selected .arb file inside translate function then it will return Future<String> which will be difficult to use inside Text widgets.

hope will help

Upvotes: 0

Christian Onwe
Christian Onwe

Reputation: 41

I faced similar situation requiring dynamic translation keys parsed to AppLocalizations and created l10n_mapper_generator package to address this.

As flutter does not support reflection (but dart does) the solution tries to emulated this by generating localization part file using specified setup configuration options (which you can find in the pub documentation). This supports parsing dynamic translation keys and defines other helpful extension methods.

Example setup

  • Install l10n_mapper_annotation and l10n_mapper_generator packages (in pubspec.yaml)

  • Install l10n_mapper_generator as global dependency dart pub global activate l10n_mapper_generator (required to run in terminal)

  • Setup configuration options (find example in pub documentation)

  • Run the below scrips in succession

    • generate localization-related files run: flutter gen-l10n

    • annotate app_localizations to generate app_localizations.g.dart file run: dart pub run l10n_mapper_generator --gen-mapper

Example usage As the generated part file consists of key-value pair returning translation keys values, below illustrates usage

final applicationName = context.l10nParser('application_name'); // Localization mapper
final depositTimeFrame = context.l10nParser('deposit_timeframe'); // Instant

// parsing placeholder parameters
final convertBeforeWithdraw = context.l10nParser('convert_before_withdraw', arguments: ['CAD', 'EUR']); // * For withdrawing your CAD you first need to convert it back to EUR

Upvotes: 1

pardus
pardus

Reputation: 1

Using the Flutter easy_localization package, you can use curly braces '{}' in your JSON translation files to handle dynamic string expressions. Below is the format for the Flutter code and the JSON file:

en_US.json:
{"before_notification": "Notifications will be set to {} minutes ago."}

 Flutter:
Text(tr('before_notification', args:[_selectedMinutes.round()]),), 

In this setup, you define your translation key in the JSON file with curly braces '{}' as a placeholder for dynamic values. Then, in your Flutter code, you use the tr() function to access the translation, and you pass the dynamic value to the placeholder using the args parameter.

By following this structure, the easy_localization package will replace the placeholder {} in your translation with the corresponding dynamic value at runtime. This way, you can easily display translated strings with dynamic content based on the selected language.

Upvotes: -1

SoftWyer
SoftWyer

Reputation: 2197

An alternative is to use (abuse?) the ICU Select option.

This is the equivalent of a switch case statement in the translation file itself, rather than coding it as described by @pdurasie.

As described in the Localizely documentation (here and here)

Select statements take the form that matches passed variable or defaults to other form, which is required.

{
  "selectExample": "Today is {gender, select, male {his} female {her} other {their} } birthday"
  "@selectExample": {
    "placeholders": {
      "gender": {}
    }
  }
}

If we pass gender variable with the value male to the example, it will print "Today is his birthday".

The Flutter code for the poster would look like:

AppLocalizations.of(context).selectExample(snapshot.data![index].state_pickup[0])

It's unclear if there are any limits on the number of select options. (ICU ref docs)

Upvotes: 5

pdurasie
pdurasie

Reputation: 101

What you are trying to do, invoking a method by its name at runtime, is called reflection, and this is not supported by Flutter natively (though there are packages that try to emulate this, but I have no experience with them).

What will work for you, even though it might be tedious, is manually mapping your value from the API to the corresponding method from AppLocalizations.of(context).

String localizedString = getLocalizedString(snapshot.data![index].state_pickup[0], context);

String getLocalizedString(String key, BuildContext context) {
  switch (key) {
    case "possible_api_value_1":
      return AppLocalizations.of(context)!.possibleApiValue1;
    case "possible_api_value_2":
       return AppLocalizations.of(context)!.possibleApiValue2;
      ...
     }

Upvotes: 9

Related Questions