-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
I'm trying to add a new language to my app (Nynorsk, language code = "nn").
I've followed the Flutter documentation found here: https://flutter.dev/docs/development/accessibility-and-localization/internationalization#adding-support-for-a-new-language
Since the language I'm adding isn't supported GlobalMaterialLocalizations I've also created a new GlobalMaterialLocalizations delegate like the example provided by Flutter doc (see main.dart below). This is where the problems begin.
Since Nynorsk (nn) isn't supported by Intl the calls to intl.DateFormat in the load function causes a crash.
Steps to Reproduce
- Run
flutter create bug. - Update the main file as follows:
main.dart
import 'dart:async';
import 'package:intl/intl.dart' as intl;
import 'package:intl/date_symbol_data_local.dart' as intl;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
class _NnMaterialLocalizationsDelegate
extends LocalizationsDelegate<MaterialLocalizations> {
const _NnMaterialLocalizationsDelegate();
@override
bool isSupported(Locale locale) => locale.languageCode == 'nn';
@override
Future<MaterialLocalizations> load(Locale locale) async {
final String localeName = intl.Intl.canonicalizedLocale("nn");
await intl.initializeDateFormatting(localeName, null);
return SynchronousFuture<MaterialLocalizations>(
NnMaterialLocalizations(
localeName: localeName,
fullYearFormat: intl.DateFormat('y', localeName),
compactDateFormat: intl.DateFormat('yMd', localeName),
shortDateFormat: intl.DateFormat('yMMMd', localeName),
mediumDateFormat: intl.DateFormat('EEE, MMM d', localeName),
longDateFormat: intl.DateFormat('EEEE, MMMM d, y', localeName),
yearMonthFormat: intl.DateFormat('MMMM y', localeName),
shortMonthDayFormat: intl.DateFormat('MMM d'),
decimalFormat: intl.NumberFormat('#,##0.###', localeName),
twoDigitZeroPaddedFormat: intl.NumberFormat('00', localeName),
),
);
}
@override
bool shouldReload(_NnMaterialLocalizationsDelegate old) => false;
}
class NnMaterialLocalizations extends GlobalMaterialLocalizations {
const NnMaterialLocalizations({
String localeName = 'nn',
@required intl.DateFormat fullYearFormat,
@required intl.DateFormat compactDateFormat,
@required intl.DateFormat shortDateFormat,
@required intl.DateFormat mediumDateFormat,
@required intl.DateFormat longDateFormat,
@required intl.DateFormat yearMonthFormat,
@required intl.DateFormat shortMonthDayFormat,
@required intl.NumberFormat decimalFormat,
@required intl.NumberFormat twoDigitZeroPaddedFormat,
}) : super(
localeName: localeName,
fullYearFormat: fullYearFormat,
compactDateFormat: compactDateFormat,
shortDateFormat: shortDateFormat,
mediumDateFormat: mediumDateFormat,
longDateFormat: longDateFormat,
yearMonthFormat: yearMonthFormat,
shortMonthDayFormat: shortMonthDayFormat,
decimalFormat: decimalFormat,
twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat,
);
@override
String get moreButtonTooltip => r'More';
@override
String get aboutListTileTitleRaw => r'About $applicationName';
@override
String get alertDialogLabel => r'Alert';
@override
String get anteMeridiemAbbreviation => r'AM';
@override
String get backButtonTooltip => r'Back';
@override
String get cancelButtonLabel => r'CANCEL';
@override
String get closeButtonLabel => r'CLOSE';
@override
String get closeButtonTooltip => r'Close';
@override
String get collapsedIconTapHint => r'Expand';
@override
String get continueButtonLabel => r'CONTINUE';
@override
String get copyButtonLabel => r'COPY';
@override
String get cutButtonLabel => r'CUT';
@override
String get deleteButtonTooltip => r'Delete';
@override
String get dialogLabel => r'Dialog';
@override
String get drawerLabel => r'Navigation menu';
@override
String get expandedIconTapHint => r'Collapse';
@override
String get hideAccountsLabel => r'Hide accounts';
@override
String get licensesPageTitle => r'Licenses';
@override
String get modalBarrierDismissLabel => r'Dismiss';
@override
String get nextMonthTooltip => r'Next month';
@override
String get nextPageTooltip => r'Next page';
@override
String get okButtonLabel => r'OK';
@override
String get openAppDrawerTooltip => r'Open navigation menu';
@override
String get pageRowsInfoTitleRaw => r'$firstRow–$lastRow of $rowCount';
@override
String get pageRowsInfoTitleApproximateRaw =>
r'$firstRow–$lastRow of about $rowCount';
@override
String get pasteButtonLabel => r'PASTE';
@override
String get popupMenuLabel => r'Popup menu';
@override
String get postMeridiemAbbreviation => r'PM';
@override
String get previousMonthTooltip => r'Previous month';
@override
String get previousPageTooltip => r'Previous page';
@override
String get refreshIndicatorSemanticLabel => r'Refresh';
@override
String get remainingTextFieldCharacterCountFew => null;
@override
String get remainingTextFieldCharacterCountMany => null;
@override
String get remainingTextFieldCharacterCountOne => r'1 character remaining';
@override
String get remainingTextFieldCharacterCountOther =>
r'$remainingCount characters remaining';
@override
String get remainingTextFieldCharacterCountTwo => null;
@override
String get remainingTextFieldCharacterCountZero => r'No characters remaining';
@override
String get reorderItemDown => r'Move down';
@override
String get reorderItemLeft => r'Move left';
@override
String get reorderItemRight => r'Move right';
@override
String get reorderItemToEnd => r'Move to the end';
@override
String get reorderItemToStart => r'Move to the start';
@override
String get reorderItemUp => r'Move up';
@override
String get rowsPerPageTitle => r'Rows per page:';
@override
ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override
String get searchFieldLabel => r'Search';
@override
String get selectAllButtonLabel => r'SELECT ALL';
@override
String get selectedRowCountTitleFew => null;
@override
String get selectedRowCountTitleMany => null;
@override
String get selectedRowCountTitleOne => r'1 item selected';
@override
String get selectedRowCountTitleOther => r'$selectedRowCount items selected';
@override
String get selectedRowCountTitleTwo => null;
@override
String get selectedRowCountTitleZero => r'No items selected';
@override
String get showAccountsLabel => r'Show accounts';
@override
String get showMenuTooltip => r'Show menu';
@override
String get signedInLabel => r'Signed in';
@override
String get tabLabelRaw => r'Tab $tabIndex of $tabCount';
@override
TimeOfDayFormat get timeOfDayFormatRaw => TimeOfDayFormat.h_colon_mm_space_a;
@override
String get timePickerHourModeAnnouncement => r'Select hours';
@override
String get timePickerMinuteModeAnnouncement => r'Select minutes';
@override
String get viewLicensesButtonLabel => r'VIEW LICENSES';
@override
List<String> get narrowWeekdays =>
const <String>['S', 'M', 'T', 'W', 'T', 'F', 'S'];
@override
int get firstDayOfWeekIndex => 0;
static const LocalizationsDelegate<MaterialLocalizations> delegate =
_NnMaterialLocalizationsDelegate();
@override
String get calendarModeButtonLabel => r'Switch to calendar';
@override
String get dateHelpText => r'mm/dd/yyyy';
@override
String get dateInputLabel => r'Enter Date';
@override
String get dateOutOfRangeLabel => r'Out of range.';
@override
String get datePickerHelpText => r'SELECT DATE';
@override
String get dateRangeEndDateSemanticLabelRaw => r'End date $fullDate';
@override
String get dateRangeEndLabel => r'End Date';
@override
String get dateRangePickerHelpText => 'SELECT RANGE';
@override
String get dateRangeStartDateSemanticLabelRaw => 'Start date \$fullDate';
@override
String get dateRangeStartLabel => 'Start Date';
@override
String get dateSeparator => '/';
@override
String get dialModeButtonLabel => 'Switch to dial picker mode';
@override
String get inputDateModeButtonLabel => 'Switch to input';
@override
String get inputTimeModeButtonLabel => 'Switch to text input mode';
@override
String get invalidDateFormatLabel => 'Invalid format.';
@override
String get invalidDateRangeLabel => 'Invalid range.';
@override
String get invalidTimeLabel => 'Enter a valid time';
@override
String get licensesPackageDetailTextOther => '\$licenseCount licenses';
@override
String get saveButtonLabel => 'SAVE';
@override
String get selectYearSemanticsLabel => 'Select year';
@override
String get timePickerDialHelpText => 'SELECT TIME';
@override
String get timePickerHourLabel => 'Hour';
@override
String get timePickerInputHelpText => 'ENTER TIME';
@override
String get timePickerMinuteLabel => 'Minute';
@override
String get unspecifiedDate => 'Date';
@override
String get unspecifiedDateRange => 'Date Range';
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
onPressed: () {
// This would fail if localizations were not provided for the
// app's locale. Although only a few Material widgets display
// localized strings, many expect to be able to find localized
// values for tooltips and other strings related to accessibility.
showDialog(
context: context,
builder: (BuildContext context) =>
Dialog(child: Center(child: Text('Hello World'))),
);
},
child: const Text('Show Dialog'),
),
),
);
}
}
void main() {
runApp(
MaterialApp(
localizationsDelegates: [
GlobalWidgetsLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
NnMaterialLocalizations.delegate,
],
supportedLocales: [const Locale('nn')],
home: Home(),
),
);
}
Expected results:
Expected closest "related" and supported Locale to be used (nb_NO in this case) if specified locale isn't supported, or just default locale (en_US).
Or an error message with info about how to add the missing DateSymbols and date time patterns for unsupported locale.
Actual results:
Crash. Because Intl.verifiedLocale throws ArgumentError("Invalid locale "nn"")
Logs
[VERBOSE-2:ui_dart_state.cc(166)] Unhandled Exception: Invalid argument(s): Invalid locale "nn"
#0 Intl._throwLocaleError (package:intl/intl.dart:245:5)
#1 Intl.verifiedLocale (package:intl/intl.dart:239:21)
#2 new DateFormat (package:intl/src/intl/date_format.dart:249:20)
#3 _NnMaterialLocalizationsDelegate.load (package:mypackage/resources/nn_material_localization.dart:23:30)
<asynchronous suspension>
#4 _loadAll (package:flutter/src/widgets/localizations.dart:60:49)
#5 _LocalizationsState.load (package:flutter/src/widgets/localizations.dart:515:62)
#6 _LocalizationsState.initState (package:flutter/src/widgets/localizations.dart:480:5)
#7 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4684:58)
#8 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4520:5)
#9 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3490:14)
#10 Element.updateChild (package:flutter/src/widgets/framework.dart:3258:<…>
Flutter analyse output:
No issues found! (ran in 3.9s)
Flutter doctor -v output
[✓] Flutter (Channel stable, 1.20.4, on Mac OS X 10.15.6 19G2021, locale en-NO)
• Flutter version 1.20.4 at /PATH_TO/flutter
• Framework revision fba99f6cf9 (10 days ago), 2020-09-14 15:32:52 -0700
• Engine revision d1bc06f032
• Dart version 2.9.2
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
• Android SDK at /PATH_TO/Library/Android/sdk
• Platform android-30, build-tools 30.0.0
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 11.7)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 11.7, Build version 11E801a
• CocoaPods version 1.9.3
[✓] Android Studio (version 4.0)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin version 49.0.2
• Dart plugin version 193.7547
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
[✓] VS Code (version 1.46.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.12.1
[✓] Connected device (2 available)
• sdk gphone x86 64 (mobile) • emulator-5554 • android-x64 • Android 11 (API 30) (emulator)
• iPhone 8 (mobile) • 77BEAB8F-9C9E-4E72-AE5F-D7784AD94A11 • ios •
com.apple.CoreSimulator.SimRuntime.iOS-13-7 (simulator)
• No issues found!