I've been having this error for a while and I think I need some second eyes of an expert to solve it and I'm really new in this language ^^.
I added localizable to my project in Flutter, added all the files.arb in different languages and tries to import it following Google's tutorial and other just different work around but keep getting the same error:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building StyleguideScreen(dirty):
The getter 'welcomeGeneralInfoTitle' was called on null.
Receiver: null
Tried calling: welcomeGeneralInfoTitle
This is my AppLocalizations.dart class I'm using for the localicationDelegates
class AppLocalizations {
static const AppLocalizationsDelegate delegate = AppLocalizationsDelegate();
static Future<AppLocalizations> load(Locale locale) {
final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName;
return AppLocalizations();
});
}
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
String get welcomeGeneralInfoTitle {
return Intl.message('Bet Master', name: 'title', desc: 'App Title');
}
}
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
const AppLocalizationsDelegate();
List<Locale> get supportedLocales {
return const <Locale>[
Locale('en', ''),
Locale('de', ''),
Locale('es', ''),
Locale('es', 'ES'),
]; //Still need to add 18 languages, is there a better way to add them?
}
#override
bool isSupported(Locale locale) => _isSupported(locale);
#override
Future<AppLocalizations> load(Locale locale) => AppLocalizations.load(locale);
#override
bool shouldReload(AppLocalizationsDelegate old) => false;
bool _isSupported(Locale locale) {
if (locale != null) {
for (Locale supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode) {
return true;
}
}
}
return false;
}
}
and here is where I added to the root of the project
return MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: AppLocalizations.delegate.supportedLocales,
title: 'AMP',
theme: Theme.darkTheme,
home: StyleguideScreen(),
);
And here is how I try to implement it and it crashes
class StyleguideScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final welcome = AppStrings.current.welcomeGeneralInfoTitle;
return Scaffold(...)
}
}
The app generates correctly all the generated files per language that it needs to import and I think it looks pretty straight forward, when I debug it, it is getting the locale correctly. Has anyone any idea why could this be happening? Thanks in advance :pray:
FIX: I just needed to add into the localizationsDelegates the auto-generated AppStrings.delegate, from the file import 'generated/l10n.dart'; instead of creating a new AppLocalizations.delegate.
like this:
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
AppStrings.delegate,
],
and remove completely the AppLocationzations class I did and it works smooth! :)
PD: I add this new library flutter_localized_locales
I use Android Studio Flutter with Intl plugin and the problem was the same
In main.dart add import import 'generated/l10n.dart';
Also add S.delegate in list of localizationsDelegates
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
S.delegate,
],
Related
I'm trying to write my simple flutter app for adding localisation for new language name "Hmong" with language code "hmn".
I've written some file of code there as described in docs. But, not working for me,
main.dart file code,
localizationsDelegates: [
Translations.delegate,
TranslationsHMNDelegate (),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
locale: value.appLocale,
supportedLocales: [Locale('en'), Locale('hmn')],
Delegate file code,
class TranslationsHMNDelegate extends LocalizationsDelegate<Translations> {
#override
bool isSupported(Locale locale) {
return locale.languageCode == "hmn";
}
#override
Future<Translations> load(Locale locale) async {
return HmnTranslations ();
}
#override
bool shouldReload(dynamic old) {
return false;
}
}
class HmnTranslations extends Translations{
// added hmn translations with 137 overridden methods here
}
Translations file code,
class Translations {
const Translations();
static Translations? current;
static Translations? of(BuildContext context) {
return Localizations.of<Translations>(context, Translations);
}
static const LocalizationsDelegate<Translations> delegate =
_TranslationsDelegate();
// Other english translations added here.
}
class _TranslationsDelegate extends LocalizationsDelegate<Translations> {
const _TranslationsDelegate();
#override
bool isSupported(Locale locale) {
return ['en', 'hmn'].contains(locale.languageCode);
}
#override
Future<Translations> load(Locale locale) async {
if (locale.languageCode.toLowerCase() == 'hmn') {
Translations.current = HmnTranslations();
return SynchronousFuture<Translations>(
Translations.current ?? const Translations());
}
Translations.current = const Translations();
return SynchronousFuture<Translations>(
Translations.current ?? const Translations());
}
#override
bool shouldReload(_TranslationsDelegate old) => false;
}
But, this is not working for me. I'm getting errors like following,
======== Exception caught by widgets ===============================================================
The following message was thrown:
This application's locale, hmn, is not supported by all of its localization delegates.
• A MaterialLocalizations delegate that supports the hmn locale was not found.
• A CupertinoLocalizations delegate that supports the hmn locale was not found.
The declared supported locales for this app are: en, hmn
So can anyone please help me with this? Thank you.
I'm trying to implement internationalization for my Flutter app but it won't let me use my preferred language (Indonesian).
From the docs, it told me to add localizationsDelegates to my MaterialApp. Since I am using GetX, the MaterialApp here are wrapped inside the GetMaterialApp.
But when I did that, it throws me an error:
Exception has occurred.
UnsupportedError (Unsupported operation: Cannot modify unmodifiable map)
I tried to remove the localizationsDelegates, it throws me yet another error:
Exception has occurred.
FlutterError (No MaterialLocalizations found.
TabBar widgets require MaterialLocalizations to be provided by a Localizations widget ancestor.
The material library uses Localizations to generate messages, labels, and abbreviations.
To introduce a MaterialLocalizations, either use a MaterialApp at the root of your application to include them automatically, or add a Localization widget with a MaterialLocalizations delegate.
The specific widget that could not find a MaterialLocalizations ancestor was:
TabBar
But when I hard code the locale property of my GetMaterialApp to Locale('en', 'US') and commented the localizationsDelegates, it works.
Do you guys know why and how to fix this?
Anyways, here is how my main.dart (and some related files) look like.
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Get.putAsync(() => EnvService().init());
await Get.putAsync(() => IntlService().init());
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: "MyApp",
initialRoute: AppPages.INITIAL,
getPages: AppPages.routes,
themeMode: ThemeMode.dark,
theme: MyAppTheme.light,
darkTheme: MyAppTheme.dark,
debugShowCheckedModeBanner: false,
// translations: AppTranslations(),
locale: const Locale('en', 'US'),
fallbackLocale: const Locale('id', 'ID'),
supportedLocales: const [
Locale('en', 'US'),
Locale('id', 'ID'),
],
//
//
// UNCOMMENT THIS LINE, YOUR PHONE WILL EXPLODE!
// localizationsDelegates: GlobalMaterialLocalizations.delegates,
);
}
}
env_service.dart
class EnvService extends GetxService {
static EnvService get instance => Get.find();
Future<EnvService> init() async {
await dotenv.load();
return this;
}
Locale get defaultLocale {
final locale = dotenv.get('DEFAULT_LOCALE', fallback: 'id');
if (locale != 'id') {
return const Locale('en', 'US');
}
return Locale(locale, locale.toUpperCase());
}
}
intl_service.dart
class IntlService extends GetxService {
static IntlService get instance => Get.find();
Locale get _locale {
return Get.locale ?? EnvService.instance.defaultLocale;
}
Future<IntlService> init() async {
await initializeDateFormatting(_locale.countryCode);
return this;
}
String formatCurrency(double number) {
final formatter = NumberFormat.simpleCurrency(decimalDigits: 0);
return formatter.format(number);
}
String formatDate(DateTime? dateTime, [String? format]) {
if (dateTime == null) {
return '';
}
return DateFormat(format).format(dateTime);
}
}
try add this in GetMaterialApp:
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
It should work if you remove initializeDateFormatting(). And be aware that Hot Reload might not work if you make this change.
This works because initializeDateFormatting() calls initializeDatePatterns() with a const Map setting dateTimePatterns which is immutable after that. But GlobalMaterialLocalizations.delegate calls initializeDateFormattingCustom() which tries to write its map into dateTimePatterns at the end. That results in an "Unsupported operation: Cannot modify unmodifiable map". So if we do not call initializeDateFormatting(), this error does not happen because dateTimePatterns is not immutable when initializeDateFormattingCustom() is called. But the date formatting is initialized anyway because that is what initializeDateFormattingCustom() does with the formatting from GlobalMaterialLocalizations.
This depends on intl ^0.17.0.
I've created some lib in my project. There are a lot of words and phrases, so I must translate everything.
I set the app's language using bloc but I don't know how to use it in my custom library.
I think I should use something related to context. I don't know at all.
Help me, please.
It's my class for localizations.
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'constants/constants.dart';
class AppLocalizations {
Locale locale;
AppLocalizations(this.locale);
/// Helper method to keep the code in the widgets concise
/// Localizations are accessed using an InheritedWidget "of" syntax
static AppLocalizations of(BuildContext context) {
return Localizations.of(context, AppLocalizations);
}
/// Static member to have a simple access to the delegate from the MaterialApp
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
late Map<String, String> _localizedStrings;
Future<bool> load() async {
String jsonString =
await rootBundle.loadString('assets/langs/${locale.languageCode}.json');
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedStrings = jsonMap.map((key, value) {
return MapEntry(key, value.toString());
});
return true;
}
String? translate(String key) {
return _localizedStrings[key];
}
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
// add all languages code here
#override
bool isSupported(Locale locale) {
return LanguageKeys.LANGUAGE_KEYS_LIST.contains(locale.languageCode);
}
// load all localization files
#override
Future<AppLocalizations> load(Locale locale) async {
AppLocalizations localizations = new AppLocalizations(locale);
await localizations.load();
return localizations;
}
#override
bool shouldReload(LocalizationsDelegate<AppLocalizations> old) => false;
}
I don't know much about bloc pattern since I have always used provider.
But the basic step would be:
(1) Inject the AppLocalizations class localization into material app
MaterialApp(
supportedLocales: [
Locale('en'),
Locale('es'),
],
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode &&
supportedLocale.countryCode == locale.countryCode) {
return supportedLocale;
}
}
return supportedLocales.first;
},
locale: Locale('en'), // translate to this language
.......
);
(2) For any translation pass the key to Applocalization
AppLocalizations.of(context).translate("understood")
You can change translate method in AppLocalizations class
(3) If you want to change app locale/lang add your stateful variable to locale key in Material App and put the Consumer above MaterialApp itself.
ChangeNotifierProvider<LangSettingsProvider>(
create: (_) => LangSettingsProvider(),
child: Consumer<LangSettingsProvider>(
builder: (context, lang, _) {
return MaterialApp(
locale : lang.appLocal
....
(4) Include assets inside assets folder and specify in the pubspec yml file
assets:
- assets/icon/
- assets/lang/en.json
- assets/lang/es.json
(5) Sample of the json key value pair
//en.json
"understood":"Understood",
//es.json
"understood":"Entendido",
Hello I am trying add BottomNavigationBar in flutter app but when i run project error occures :
A MaterialLocalizations delegate that supports the ka_GE locale was not found
This is my app delegates:
supportedLocales: [
const Locale('en', 'US'),
const Locale('ka', 'GE'),
const Locale('ru', 'RU'),
],
localizationsDelegates: [
const InfosLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
locale: Locale('ka')
This is Custom LocalizationsDelegate:
class CLocalizationsDelegate
extends LocalizationsDelegate<CLocalizations> {
const CLocalizationsDelegate();
#override
bool isSupported(Locale locale) =>
['en', 'ka', 'ru'].contains(locale.languageCode);
#override
Future<CLocalizations> load(Locale locale) async {
CLocalizations localizations = new CLocalizations(locale);
await localizations.load();
print("Load ${locale.languageCode}");
return localizations;
}
#override
bool shouldReload(CLocalizationsDelegate old) => false;
}
Yeah I know that the problem is 'ka' because MaterialLocalizations doesn't not supports it but I have to solve that problem, so guys can you help me out?
Addig GlobalCupertinoLocalizations too to the localizationsDelegates also saves the problem.
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
You can implement your custom MaterialLocalizations delegate
class MaterialLocalizationKaDelegate extends LocalizationsDelegate<MaterialLocalizations> {
#override
bool isSupported(Locale locale) {
return locale.countryCode == "GE" && locale.languageCode == "ka";
}
#override
Future<MaterialLocalizations> load(Locale locale) async {
return MaterialLocalizationKa();
}
#override
bool shouldReload(Foo old) {
return false;
}
}
class MaterialLocalizationKa extends MaterialLocalizations {
// TODO: implement KA localization yourself
}
I followed the explanations given in the official Flutter pages (see here) to make my application work in different languages.
According to the documentation, it retrieves the user's locale and this works fine.
Let's now suppose that my application supports different languages (such as EN, FR, ES, ...) and that the user could select one of these languages to use the application (the selected language would then be different than the one defined in the phone's settings), how can I achieve this?
How may I force the application Locale and dynamically "reload" all the translations?
The Flutter page does not explain this and I haven't seen anything that help me in the documentation...
Here is the current implementation:
class Translations {
Translations(this.locale);
final Locale locale;
static Translations of(BuildContext context){
return Localizations.of<Translations>(context, Translations);
}
static Map<String, Map<String, String>> _localizedValues = {
'en': {
'title': 'Hello',
},
'fr': {
'title': 'Bonjour',
},
'es': {
'title': 'Hola',
}
};
String text(String key){
return _localizedValues[locale.languageCode][key] ?? '** ${key} not found';
}
}
class TranslationsDelegate extends LocalizationsDelegate<Translations> {
const TranslationsDelegate();
#override
bool isSupported(Locale locale) => ['en', 'fr','es'].contains(locale.languageCode);
#override
Future<Translations> load(Locale locale) {
return new SynchronousFuture<Translations>(new Translations(locale));
}
#override
bool shouldReload(TranslationsDelegate old) => false;
}
In the main.dart:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: Translations.of(context).text('title'),
theme: new ThemeData(
primarySwatch: Colors.blue,
),
localizationsDelegates: [
const TranslationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''), // English
const Locale('fr', ''), // French
const Locale('fr', ''), // French
],
home: new LandingPage(),
);
}
}
Many thanks for your help.
This can be accomplished by
creating a new LocalizationsDelegate that either translates to a
single locale or defers completely depending on a parameter
converting the base app (MyApp) to a stateful widget and inserting the new delegate above into the localizationsDelegates list
managing the base app (MyApp) state with a new delegate targeting a specific locale based on some event
A simple implementation for 1) might be:
class SpecifiedLocalizationDelegate
extends LocalizationsDelegate<Translations> {
final Locale overriddenLocale;
const SpecifiedLocalizationDelegate(this.overriddenLocale);
#override
bool isSupported(Locale locale) => overriddenLocale != null;
#override
Future<Translations> load(Locale locale) =>
Translations.load(overriddenLocale);
#override
bool shouldReload(SpecifiedLocalizationDelegate old) => true;
}
Next for 2) and 3), convert the MyApp to stateful and include the new delegate (initially just deferring everything), plus some event handlers to change the state with a new delegate that specifies a new Locale.
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
SpecifiedLocalizationDelegate _localeOverrideDelegate;
#override
void initState() {
super.initState();
_localeOverrideDelegate = new SpecifiedLocalizationDelegate(null);
}
onLocaleChange(Locale l) {
setState(() {
_localeOverrideDelegate = new SpecifiedLocalizationDelegate(l);
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
localizationsDelegates: [
_localeOverrideDelegate,
const TranslationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''), // English
const Locale('fr', ''), // French
],
home: new LandingPage(onLocaleSwitch: onLocaleChange),
);
}
}
With these changes, in children widgets you could now use Translations.of(context).myLocalizedString to retrieve the translations.
More complete gist: https://gist.github.com/ilikerobots/474b414138f3f99150dbb3d0cc4cc721
To control the locale of the app, you can use the locale property of the MaterialApp:
return MaterialApp(
...
locale: _myLocal,
...
);
This, combined with #ilikerobots StatefulWidget approach shall provide you with what you need.
using one of the Providers should do the job, I am not really familiar with providers but this got me working easily
wrap your material app using ChangeNotifierProvider
return ChangeNotifierProvider(
create: (_) => new LocaleModel(),
child: Consumer<LocaleModel>(
builder: (context, provider, child) => MaterialApp(
title: 'myapp',
locale: Provider.of<LocaleModel>(context).locale
...
...
...
create A model class with getters and setters to get & set the locale as\
import 'package:iborganic/const/page_exports.dart';
class LocaleModel with ChangeNotifier {
Locale locale = Locale('en');
Locale get getlocale => locale;
void changelocale(Locale l) {
locale = l;
notifyListeners();
}
}
Change the locale on some event (button click) as
Provider.of<LocaleModel>(context).changelocale(Locale("kn"));
The benefit of wrapping the material app within Provider is you can have access to the locale value from any part of your app
The easiest way, which weirdly enough is not mentioned in the internationalization tutorial, is using the locale property. This property of the MaterialApp class allows us to immediately specify what locale we want our app to use
return MaterialApp(
locale: Locale('ar', ''),
localizationsDelegates: [
MyLocalizationsDelegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''), // English
const Locale('ar', ''), // Arabic
],
home: HomeScreen()
);
This tutorial explained it better
It also explained how to load the locale preference from sharedPreferences