diff --git a/packages/flutter/lib/src/cupertino/date_picker.dart b/packages/flutter/lib/src/cupertino/date_picker.dart index 693b4069a3..afc938624e 100644 --- a/packages/flutter/lib/src/cupertino/date_picker.dart +++ b/packages/flutter/lib/src/cupertino/date_picker.dart @@ -438,8 +438,9 @@ class CupertinoDatePicker extends StatefulWidget { _PickerColumnType columnType, CupertinoLocalizations localizations, BuildContext context, - bool showDayOfWeek - ) { + bool showDayOfWeek, { + bool standaloneMonth = false, + }) { String longestText = ''; switch (columnType) { @@ -491,8 +492,10 @@ class CupertinoDatePicker extends StatefulWidget { } } case _PickerColumnType.month: - for (int i = 1; i <=12; i++) { - final String month = localizations.datePickerMonth(i); + for (int i = 1; i <= 12; i++) { + final String month = standaloneMonth + ? localizations.datePickerStandaloneMonth(i) + : localizations.datePickerMonth(i); if (longestText.length < month.length) { longestText = month; } @@ -1280,11 +1283,12 @@ class _CupertinoDatePickerDateState extends State { final int month = index + 1; final bool isInvalidMonth = (widget.minimumDate?.year == selectedYear && widget.minimumDate!.month > month) || (widget.maximumDate?.year == selectedYear && widget.maximumDate!.month < month); + final String monthName = (widget.mode == CupertinoDatePickerMode.monthYear) ? localizations.datePickerStandaloneMonth(month) : localizations.datePickerMonth(month); return itemPositioningBuilder( context, Text( - localizations.datePickerMonth(month), + monthName, style: _themeTextStyle(context, isValid: !isInvalidMonth), ), ); @@ -1577,7 +1581,8 @@ class _CupertinoDatePickerMonthYearState extends State { } void _refreshEstimatedColumnWidths() { - estimatedColumnWidths[_PickerColumnType.month.index] = CupertinoDatePicker._getColumnWidth(_PickerColumnType.month, localizations, context, false); + estimatedColumnWidths[_PickerColumnType.month.index] = + CupertinoDatePicker._getColumnWidth(_PickerColumnType.month, localizations, context, false, standaloneMonth: widget.mode == CupertinoDatePickerMode.monthYear); estimatedColumnWidths[_PickerColumnType.year.index] = CupertinoDatePicker._getColumnWidth(_PickerColumnType.year, localizations, context, false); } @@ -1613,11 +1618,12 @@ class _CupertinoDatePickerMonthYearState extends State { final int month = index + 1; final bool isInvalidMonth = (widget.minimumDate?.year == selectedYear && widget.minimumDate!.month > month) || (widget.maximumDate?.year == selectedYear && widget.maximumDate!.month < month); + final String monthName = (widget.mode == CupertinoDatePickerMode.monthYear) ? localizations.datePickerStandaloneMonth(month) : localizations.datePickerMonth(month); return itemPositioningBuilder( context, Text( - localizations.datePickerMonth(month), + monthName, style: _themeTextStyle(context, isValid: !isInvalidMonth), ), ); diff --git a/packages/flutter/lib/src/cupertino/localizations.dart b/packages/flutter/lib/src/cupertino/localizations.dart index 17bccd35b5..02cb25970f 100644 --- a/packages/flutter/lib/src/cupertino/localizations.dart +++ b/packages/flutter/lib/src/cupertino/localizations.dart @@ -76,9 +76,25 @@ abstract class CupertinoLocalizations { /// /// - US English: January /// - Korean: 1월 + /// - Russian: января // The global version uses date symbols data from the intl package. String datePickerMonth(int monthIndex); + /// Month that is shown in [CupertinoDatePicker] spinner corresponding to + /// the given month index in [CupertinoDatePickerMode.monthYear] mode. + /// + /// This is distinct from [datePickerMonth] because in some languages, like Russian, + /// the name of a month takes a different form depending + /// on whether it is preceded by a day or whether it stands alone. + /// + /// Examples: datePickerMonth(1) in: + /// + /// - US English: January + /// - Korean: 1월 + /// - Russian: Январь + // The global version uses date symbols data from the intl package. + String datePickerStandaloneMonth(int monthIndex); + /// Day of month that is shown in [CupertinoDatePicker] spinner corresponding /// to the given day index. /// @@ -367,6 +383,9 @@ class DefaultCupertinoLocalizations implements CupertinoLocalizations { @override String datePickerMonth(int monthIndex) => _months[monthIndex - 1]; + @override + String datePickerStandaloneMonth(int monthIndex) => _months[monthIndex - 1]; + @override String datePickerDayOfMonth(int dayIndex, [int? weekDay]) { if (weekDay != null) { diff --git a/packages/flutter_localizations/lib/src/cupertino_localizations.dart b/packages/flutter_localizations/lib/src/cupertino_localizations.dart index 68a5c0bc8b..ec9b821207 100644 --- a/packages/flutter_localizations/lib/src/cupertino_localizations.dart +++ b/packages/flutter_localizations/lib/src/cupertino_localizations.dart @@ -100,6 +100,18 @@ abstract class GlobalCupertinoLocalizations implements CupertinoLocalizations { return _fullYearFormat.dateSymbols.MONTHS[monthIndex - 1]; } + @override + String datePickerStandaloneMonth(int monthIndex) { + // It doesn't actually have anything to do with _fullYearFormat. It's just + // taking advantage of the fact that _fullYearFormat loaded the needed + // locale's symbols. + // + // Because this will be used without specifying any day of month, + // in most cases it should be capitalized (according to rules in specific language). + return intl.toBeginningOfSentenceCase(_fullYearFormat.dateSymbols.STANDALONEMONTHS[monthIndex - 1]) ?? + _fullYearFormat.dateSymbols.STANDALONEMONTHS[monthIndex - 1]; + } + @override String datePickerDayOfMonth(int dayIndex, [int? weekDay]) { if (weekDay != null) { diff --git a/packages/flutter_localizations/test/cupertino/date_picker_test.dart b/packages/flutter_localizations/test/cupertino/date_picker_test.dart new file mode 100644 index 0000000000..d94b5343a9 --- /dev/null +++ b/packages/flutter_localizations/test/cupertino/date_picker_test.dart @@ -0,0 +1,47 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/cupertino.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Test correct month form for CupertinoDatePicker in monthYear mode', (WidgetTester tester) async { + await tester.pumpWidget( + CupertinoApp( + home: CupertinoPageScaffold( + child: Center( + child: CupertinoDatePicker( + initialDateTime: DateTime(2023, 5), + onDateTimeChanged: (_) {}, + mode: CupertinoDatePickerMode.monthYear, + )), + ), + supportedLocales: const [Locale('ru', 'RU')], + localizationsDelegates: GlobalCupertinoLocalizations.delegates, + ), + ); + + expect(find.text('Май'), findsWidgets); + }); + + testWidgets('Test correct month form for CupertinoDatePicker in date mode', (WidgetTester tester) async { + await tester.pumpWidget( + CupertinoApp( + home: CupertinoPageScaffold( + child: Center( + child: CupertinoDatePicker( + initialDateTime: DateTime(2023, 5), + onDateTimeChanged: (_) {}, + mode: CupertinoDatePickerMode.date, + )), + ), + supportedLocales: const [Locale('ru', 'RU')], + localizationsDelegates: GlobalCupertinoLocalizations.delegates, + ), + ); + + expect(find.text('мая'), findsWidgets); + }); +} diff --git a/packages/flutter_localizations/test/cupertino/translations_test.dart b/packages/flutter_localizations/test/cupertino/translations_test.dart index 736e5ddb25..a827e0d498 100644 --- a/packages/flutter_localizations/test/cupertino/translations_test.dart +++ b/packages/flutter_localizations/test/cupertino/translations_test.dart @@ -34,6 +34,11 @@ void main() { expect(localizations.datePickerMonth(11), isNotNull); expect(localizations.datePickerMonth(12), isNotNull); + expect(localizations.datePickerStandaloneMonth(1), isNotNull); + expect(localizations.datePickerStandaloneMonth(2), isNotNull); + expect(localizations.datePickerStandaloneMonth(11), isNotNull); + expect(localizations.datePickerStandaloneMonth(12), isNotNull); + expect(localizations.datePickerDayOfMonth(0), isNotNull); expect(localizations.datePickerDayOfMonth(1), isNotNull); expect(localizations.datePickerDayOfMonth(2), isNotNull);