Reputation: 659
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.
Upvotes: 5
Views: 3338
Reputation: 470
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.
Upvotes: 0
Reputation: 470
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\"'}'"
Upvotes: 0
Reputation: 731
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"
Upvotes: 2
Reputation: 1731
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
Upvotes: 0
Reputation: 76
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');
}
}
}
Upvotes: 6