Flutter 1.22 Internationalization with variable as key

2.6k Views Asked by At

I implemented the new (official) localization for Flutter (https://pascalw.me/blog/2020/10/02/flutter-1.22-internationalization.html) and everything is working fine, except that I don't know how to get the translation for a variable key.

The translation is in the ARB file, but how can I access it?

Normally I access translations using Translations.of(context).formsBack, but now I would like to get the translation of value["labels"]["label"].

Something like Translations.of(context).(value["labels"]["label"]) does not work of course.

5

There are 5 best solutions below

0
On BEST ANSWER

I don't think this is possible with gen_l10n. The code that is generated by gen_l10n looks like this (somewhat abbreviated):

/// The translations for English (`en`).
class TranslationsEn extends Translations {
  TranslationsEn([String locale = 'en']) : super(locale);

  @override
  String get confirmDialogBtnOk => 'Yes';

  @override
  String get confirmDialogBtnCancel => 'No';
}

As you can see it doesn't generate any code to perform a dynamic lookup.

For most cases code generation like this is a nice advantage since you get auto completion and type safety, but it does mean it's more difficult to accommodate these kinds of dynamic use cases.

The only thing you can do is manually write a lookup table, or choose another i18n solution that does support dynamic lookups.

A lookup table could look something like this. Just make sure you always pass in the current build context, so the l10n code can lookup the current locale.

class DynamicTranslations {
  String get(BuildContext context, String messageId) {
    switch(messageId) {
      case 'confirmDialogBtnOk':
        return Translations.of(context).confirmDialogBtnOk;
      case 'confirmDialogBtnCancel':
        return Translations.of(context).confirmDialogBtnCancel;
      default:
        throw Exception('Unknown message: $messageId');
    }
  }
}
0
On

You can implement your own localization class where translations are looked up by key, so you don't have to rely on the gen-l10n tool. You can add your keys to json files under assets/resources. Make sure you declare your assets in pubspec.yaml:

flutter:
  assets:
    - assets/images/
    - assets/resources/

You custom class could look like this:

import 'dart:convert';
import 'package:flutter/widgets.dart';
import 'package:flutter/services.dart' show rootBundle;

class CustomLocalization {
  final Locale _locale;
  final Map<String, String> _entries;
  static final CustomLocalizationDelegate delegate =
      CustomLocalizationDelegate();

  CustomLocalization(this._locale, this._entries);

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

  String get(String key) {
    return _entries[key] ?? "??:$key";
  }
}

class CustomLocalizationDelegate
    extends LocalizationsDelegate<CustomLocalization> {
  @override
  bool isSupported(Locale locale) =>
      <String>['en', 'es'].contains(locale.languageCode);

  @override
  bool shouldReload(CustomLocalizationDelegate old) => false;

  @override
  Future<CustomLocalization> load(Locale locale) async {
    String language = locale.languageCode;
    // Load translations from assets i18n_en.json or i18n_es.json
    String json = await rootBundle.loadString("resources/i18n_$language.json");
    Map<String, String> entries = Map.castFrom(jsonDecode(json));
    return CustomLocalization(locale, entries);
  }
}

Use CustomLocalization.delegate and CustomLocalization.of() in a similar way as you use AppLocalizations.

0
On

If you try to apply the JSON solution provided but you get this error:

[app_en.arb:languages] ICU Lexing Error: Unexpected character. {"en": "English", "es": "Spanish"}

First add the following to l10n.yaml:

use-escaping: true

Then define your key as follows:

"languages": "'{'\"en\": \"English\", \"es\": \"Spanish\"'}'"
0
On

You can store a key in translation as json string. Then you read it, parse it to Map<string,string> and access dynamically what you need.

Been using this approach with great success

0
On

To provide an example for https://stackoverflow.com/users/5638943/kristi-jorgji 's answer (which works fine):

app_en.arb ->

 {
     "languages": "{\"en\": \"English\", \"ro\": \"Romanian\"}"
 }

localization_controller.dart ->

  String getLocaleName(BuildContext ctx, String languageCode) {
    return jsonDecode(AppLocalizations.of(ctx)!.languages)[languageCode];
  }

getLocaleName(context, 'ro') -> "Romanian"