#960 l10n: use dates with western arabic numerals for maghreb arabic locales

This commit is contained in:
Thibault Deckers 2024-03-27 21:15:32 +01:00
parent 5db31476fe
commit 007c917dca
16 changed files with 153 additions and 33 deletions

View file

@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file.
### Changed ### Changed
- opening app from launcher always show home page - opening app from launcher always show home page
- use dates with western arabic numerals for maghreb arabic locales
- upgraded Flutter to stable v3.19.4 - upgraded Flutter to stable v3.19.4
### Fixed ### Fixed

View file

@ -7,6 +7,7 @@ import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/catalog.dart'; import 'package:aves/model/entry/extensions/catalog.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/metadata/date_modifier.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/ref/metadata/exif.dart'; import 'package:aves/ref/metadata/exif.dart';
import 'package:aves/ref/metadata/iptc.dart'; import 'package:aves/ref/metadata/iptc.dart';
import 'package:aves/ref/metadata/xmp.dart'; import 'package:aves/ref/metadata/xmp.dart';
@ -121,7 +122,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
if (latLng != null && latLng != removalLocation) { if (latLng != null && latLng != removalLocation) {
final latitude = latLng.latitude; final latitude = latLng.latitude;
final longitude = latLng.longitude; final longitude = latLng.longitude;
const locale = 'en_US'; const locale = asciiLocale;
final isoLat = '${latitude >= 0 ? '+' : '-'}${NumberFormat('00.0000', locale).format(latitude.abs())}'; final isoLat = '${latitude >= 0 ? '+' : '-'}${NumberFormat('00.0000', locale).format(latitude.abs())}';
final isoLon = '${longitude >= 0 ? '+' : '-'}${NumberFormat('000.0000', locale).format(longitude.abs())}'; final isoLon = '${longitude >= 0 ? '+' : '-'}${NumberFormat('000.0000', locale).format(longitude.abs())}';
iso6709String = '$isoLat$isoLon/'; iso6709String = '$isoLat$isoLon/';

View file

@ -20,6 +20,7 @@ class NamingPattern {
factory NamingPattern.from({ factory NamingPattern.from({
required String userPattern, required String userPattern,
required int entryCount, required int entryCount,
required String locale,
}) { }) {
final processors = <NamingProcessor>[]; final processors = <NamingProcessor>[];
@ -40,7 +41,7 @@ class NamingPattern {
switch (processorKey) { switch (processorKey) {
case DateNamingProcessor.key: case DateNamingProcessor.key:
if (processorOptions != null) { if (processorOptions != null) {
processors.add(DateNamingProcessor(processorOptions.trim())); processors.add(DateNamingProcessor(processorOptions.trim(), locale));
} }
case TagsNamingProcessor.key: case TagsNamingProcessor.key:
processors.add(TagsNamingProcessor(processorOptions?.trim() ?? '')); processors.add(TagsNamingProcessor(processorOptions?.trim() ?? ''));
@ -156,7 +157,7 @@ class DateNamingProcessor extends NamingProcessor {
@override @override
List<Object?> get props => [format.pattern]; List<Object?> get props => [format.pattern];
DateNamingProcessor(String pattern) : format = DateFormat(pattern); DateNamingProcessor(String pattern, String locale) : format = DateFormat(pattern, locale);
@override @override
String? process(AvesEntry entry, int index, Map<String, dynamic> fieldValues) { String? process(AvesEntry entry, int index, Map<String, dynamic> fieldValues) {

View file

@ -50,7 +50,7 @@ mixin AppSettings on SettingsAccess {
].join(localeSeparator); ].join(localeSeparator);
} }
set(SettingKeys.localeKey, tag); set(SettingKeys.localeKey, tag);
_appliedLocale = null; resetAppliedLocale();
} }
List<Locale> _systemLocalesFallback = []; List<Locale> _systemLocalesFallback = [];

View file

@ -8,6 +8,7 @@ import 'package:aves/model/video/profiles/aac.dart';
import 'package:aves/model/video/profiles/h264.dart'; import 'package:aves/model/video/profiles/h264.dart';
import 'package:aves/model/video/profiles/hevc.dart'; import 'package:aves/model/video/profiles/hevc.dart';
import 'package:aves/ref/languages.dart'; import 'package:aves/ref/languages.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';
import 'package:aves/ref/mp4.dart'; import 'package:aves/ref/mp4.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
@ -453,7 +454,7 @@ class VideoMetadataFormatter {
static String _formatFilesize(dynamic value) { static String _formatFilesize(dynamic value) {
final size = value is int ? value : int.tryParse(value); final size = value is int ? value : int.tryParse(value);
return size != null ? formatFileSize('en_US', size) : value; return size != null ? formatFileSize(asciiLocale, size) : value;
} }
static String _formatLanguage(String value) { static String _formatLanguage(String value) {

47
lib/ref/locales.dart Normal file
View file

@ -0,0 +1,47 @@
import 'dart:ui';
const String asciiLocale = 'en_US';
// cf https://en.wikipedia.org/wiki/Eastern_Arabic_numerals
bool shouldUseNativeDigits(Locale? countrifiedLocale) {
switch (countrifiedLocale?.toString()) {
// Maghreb
case 'ar_DZ': // Algeria
case 'ar_EH': // Western Sahara
case 'ar_LY': // Libya
case 'ar_MA': // Morocco
case 'ar_MR': // Mauritania
case 'ar_TN': // Tunisia
return false;
// Mashriq
case 'ar_AE': // United Arab Emirates
case 'ar_BH': // Bahrain
case 'ar_EG': // Egypt
case 'ar_IQ': // Iraq
case 'ar_JO': // Jordan
case 'ar_KW': // Kuwait
case 'ar_LB': // Lebanon
case 'ar_OM': // Oman
case 'ar_PS': // Palestinian Territories
case 'ar_QA': // Qatar
case 'ar_SA': // Saudi Arabia
case 'ar_SD': // Sudan
case 'ar_SS': // South Sudan
case 'ar_SY': // Syria
case 'ar_YE': // Yemen
return true;
// Horn of Africa
case 'ar_DJ': // Djibouti
case 'ar_ER': // Eritrea
case 'ar_KM': // Comoros
case 'ar_SO': // Somalia
return true;
// others
case 'ar_IL': // Israel
case 'ar_TD': // Chad
return true;
case null:
default:
return true;
}
}

View file

@ -1,3 +1,4 @@
import 'package:aves/ref/locales.dart';
import 'package:aves/ref/metadata/xmp.dart'; import 'package:aves/ref/metadata/xmp.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:xml/xml.dart'; import 'package:xml/xml.dart';
@ -60,7 +61,7 @@ class XMP {
return '${offsetMinutes.isNegative ? '-' : '+'}${h.toString().padLeft(2, '0')}:${m.toString().padLeft(2, '0')}'; return '${offsetMinutes.isNegative ? '-' : '+'}${h.toString().padLeft(2, '0')}:${m.toString().padLeft(2, '0')}';
} }
static String toXmpDate(DateTime date) => '${DateFormat('yyyy-MM-ddTHH:mm:ss').format(date)}${_xmpTimeZoneDesignator(date)}'; static String toXmpDate(DateTime date) => '${DateFormat('yyyy-MM-ddTHH:mm:ss', asciiLocale).format(date)}${_xmpTimeZoneDesignator(date)}';
static String? getString( static String? getString(
List<XmlNode> nodes, List<XmlNode> nodes,

View file

@ -6,6 +6,7 @@ import 'package:aves/app_flavor.dart';
import 'package:aves/flutter_version.dart'; import 'package:aves/flutter_version.dart';
import 'package:aves/model/device.dart'; import 'package:aves/model/device.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
@ -176,7 +177,7 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
final result = await Process.run('logcat', ['-d']); final result = await Process.run('logcat', ['-d']);
final logs = result.stdout; final logs = result.stdout;
final success = await storageService.createFile( final success = await storageService.createFile(
'aves-logs-${DateFormat('yyyyMMdd_HHmmss').format(DateTime.now())}.txt', 'aves-logs-${DateFormat('yyyyMMdd_HHmmss', asciiLocale).format(DateTime.now())}.txt',
MimeTypes.plainText, MimeTypes.plainText,
Uint8List.fromList(utf8.encode(logs)), Uint8List.fromList(utf8.encode(logs)),
); );

View file

@ -16,6 +16,7 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/media_store_source.dart'; import 'package:aves/model/source/media_store_source.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/services/accessibility_service.dart'; import 'package:aves/services/accessibility_service.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
@ -37,12 +38,14 @@ import 'package:aves/widgets/navigation/tv_rail.dart';
import 'package:aves/widgets/welcome_page.dart'; import 'package:aves/widgets/welcome_page.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:aves_utils/aves_utils.dart'; import 'package:aves_utils/aves_utils.dart';
import 'package:collection/collection.dart';
import 'package:dynamic_color/dynamic_color.dart'; import 'package:dynamic_color/dynamic_color.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_localization_nn/flutter_localization_nn.dart'; import 'package:flutter_localization_nn/flutter_localization_nn.dart';
import 'package:intl/intl.dart';
import 'package:overlay_support/overlay_support.dart'; import 'package:overlay_support/overlay_support.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:screen_brightness/screen_brightness.dart'; import 'package:screen_brightness/screen_brightness.dart';
@ -158,6 +161,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
final ValueNotifier<PageTransitionsBuilder> _pageTransitionsBuilderNotifier = ValueNotifier(defaultPageTransitionsBuilder); final ValueNotifier<PageTransitionsBuilder> _pageTransitionsBuilderNotifier = ValueNotifier(defaultPageTransitionsBuilder);
final ValueNotifier<TvMediaQueryModifier?> _tvMediaQueryModifierNotifier = ValueNotifier(null); final ValueNotifier<TvMediaQueryModifier?> _tvMediaQueryModifierNotifier = ValueNotifier(null);
final ValueNotifier<AppMode> _appModeNotifier = ValueNotifier(AppMode.main); final ValueNotifier<AppMode> _appModeNotifier = ValueNotifier(AppMode.main);
final ValueNotifier<LocaleOverrides> _localeOverridesNotifier = ValueNotifier(LocaleOverrides.none);
// observers are not registered when using the same list object with different items // observers are not registered when using the same list object with different items
// the list itself needs to be reassigned // the list itself needs to be reassigned
@ -217,6 +221,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
Provider<TvRailController>.value(value: _tvRailController), Provider<TvRailController>.value(value: _tvRailController),
DurationsProvider(), DurationsProvider(),
HighlightInfoProvider(), HighlightInfoProvider(),
ListenableProvider<ValueNotifier<LocaleOverrides>>.value(value: _localeOverridesNotifier),
], ],
child: OverlaySupport( child: OverlaySupport(
child: FutureBuilder<void>( child: FutureBuilder<void>(
@ -239,9 +244,6 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
), ),
builder: (context, s, child) { builder: (context, s, child) {
final (settingsLocale, themeBrightness, enableDynamicColor) = s; final (settingsLocale, themeBrightness, enableDynamicColor) = s;
AStyles.updateStylesForLocale(settings.appliedLocale);
return DynamicColorBuilder( return DynamicColorBuilder(
builder: (lightScheme, darkScheme) { builder: (lightScheme, darkScheme) {
const defaultAccent = AvesColorsData.defaultAccent; const defaultAccent = AvesColorsData.defaultAccent;
@ -411,6 +413,33 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
} }
} }
@override
void didChangeLocales(List<Locale>? locales) {
_applyLocale();
}
void _applyLocale() {
settings.resetAppliedLocale();
final appliedLocale = settings.appliedLocale;
AStyles.updateStylesForLocale(appliedLocale);
Locale? countrifiedLocale;
if (appliedLocale.countryCode == null) {
final languageCode = appliedLocale.languageCode;
countrifiedLocale = WidgetsBinding.instance.platformDispatcher.locales.firstWhereOrNull((v) => v.languageCode == languageCode);
}
if (appliedLocale.languageCode == 'ar') {
final useNativeDigits = shouldUseNativeDigits(countrifiedLocale);
DateFormat.useNativeDigitsByDefaultFor(appliedLocale.toString(), useNativeDigits);
DateFormat.useNativeDigitsByDefaultFor(countrifiedLocale.toString(), useNativeDigits);
}
_localeOverridesNotifier.value = LocaleOverrides(
countrifiedLocale: countrifiedLocale,
);
}
Widget _getFirstPage({Map? intentData}) => settings.hasAcceptedTerms ? HomePage(intentData: intentData) : const WelcomePage(); Widget _getFirstPage({Map? intentData}) => settings.hasAcceptedTerms ? HomePage(intentData: intentData) : const WelcomePage();
Size? _getScreenSize(BuildContext context) { Size? _getScreenSize(BuildContext context) {
@ -504,7 +533,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
} }
void _monitorSettings() { void _monitorSettings() {
void applyIsInstalledAppAccessAllowed() { void _applyIsInstalledAppAccessAllowed() {
if (settings.isInstalledAppAccessAllowed) { if (settings.isInstalledAppAccessAllowed) {
appInventory.initAppNames(); appInventory.initAppNames();
} else { } else {
@ -512,9 +541,9 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
} }
} }
void applyDisplayRefreshRateMode() => settings.displayRefreshRateMode.apply(); void _applyDisplayRefreshRateMode() => settings.displayRefreshRateMode.apply();
void applyMaxBrightness() { void _applyMaxBrightness() {
switch (settings.maxBrightness) { switch (settings.maxBrightness) {
case MaxBrightness.never: case MaxBrightness.never:
case MaxBrightness.viewerOnly: case MaxBrightness.viewerOnly:
@ -524,9 +553,9 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
} }
} }
void applyKeepScreenOn() => settings.keepScreenOn.apply(); void _applyKeepScreenOn() => settings.keepScreenOn.apply();
void applyIsRotationLocked() { void _applyIsRotationLocked() {
if (!settings.isRotationLocked && !settings.useTvLayout) { if (!settings.isRotationLocked && !settings.useTvLayout) {
windowService.requestOrientation(); windowService.requestOrientation();
} }
@ -545,20 +574,22 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
final settingStream = settings.updateStream; final settingStream = settings.updateStream;
// app // app
settingStream.where((event) => event.key == SettingKeys.isInstalledAppAccessAllowedKey).listen((_) => applyIsInstalledAppAccessAllowed()); settingStream.where((event) => event.key == SettingKeys.isInstalledAppAccessAllowedKey).listen((_) => _applyIsInstalledAppAccessAllowed());
settingStream.where((event) => event.key == SettingKeys.localeKey).listen((_) => _applyLocale());
// display // display
settingStream.where((event) => event.key == SettingKeys.displayRefreshRateModeKey).listen((_) => applyDisplayRefreshRateMode()); settingStream.where((event) => event.key == SettingKeys.displayRefreshRateModeKey).listen((_) => _applyDisplayRefreshRateMode());
settingStream.where((event) => event.key == SettingKeys.maxBrightnessKey).listen((_) => applyMaxBrightness()); settingStream.where((event) => event.key == SettingKeys.maxBrightnessKey).listen((_) => _applyMaxBrightness());
settingStream.where((event) => event.key == SettingKeys.forceTvLayoutKey).listen((_) => applyForceTvLayout()); settingStream.where((event) => event.key == SettingKeys.forceTvLayoutKey).listen((_) => applyForceTvLayout());
// navigation // navigation
settingStream.where((event) => event.key == SettingKeys.keepScreenOnKey).listen((_) => applyKeepScreenOn()); settingStream.where((event) => event.key == SettingKeys.keepScreenOnKey).listen((_) => _applyKeepScreenOn());
// platform settings // platform settings
settingStream.where((event) => event.key == SettingKeys.platformAccelerometerRotationKey).listen((_) => applyIsRotationLocked()); settingStream.where((event) => event.key == SettingKeys.platformAccelerometerRotationKey).listen((_) => _applyIsRotationLocked());
applyDisplayRefreshRateMode(); _applyLocale();
applyMaxBrightness(); _applyDisplayRefreshRateMode();
applyKeepScreenOn(); _applyMaxBrightness();
applyIsRotationLocked(); _applyKeepScreenOn();
_applyIsRotationLocked();
} }
Future<void> _setupErrorReporting() async { Future<void> _setupErrorReporting() async {
@ -632,3 +663,18 @@ class AvesScrollBehavior extends MaterialScrollBehavior {
} }
typedef TvMediaQueryModifier = MediaQueryData Function(MediaQueryData); typedef TvMediaQueryModifier = MediaQueryData Function(MediaQueryData);
class LocaleOverrides extends Equatable {
final Locale? countrifiedLocale;
@override
List<Object?> get props => [countrifiedLocale];
const LocaleOverrides({
required this.countrifiedLocale,
});
static const LocaleOverrides none = LocaleOverrides(
countrifiedLocale: null,
);
}

View file

@ -672,8 +672,8 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge
final newest = firstKey.date; final newest = firstKey.date;
final oldest = lastKey.date; final oldest = lastKey.date;
if (newest != null && oldest != null) { if (newest != null && oldest != null) {
final localeName = context.l10n.localeName; final locale = context.l10n.localeName;
final dateFormat = (newest.difference(oldest).inDays).abs() > 365 ? DateFormat.y(localeName) : DateFormat.MMM(localeName); final dateFormat = (newest.difference(oldest).inDays).abs() > 365 ? DateFormat.y(locale) : DateFormat.MMM(locale);
String? lastLabel; String? lastLabel;
sectionLayouts.forEach((section) { sectionLayouts.forEach((section) {
final date = (section.sectionKey as EntryDateSectionKey).date; final date = (section.sectionKey as EntryDateSectionKey).date;

View file

@ -1,3 +1,4 @@
import 'package:aves/ref/locales.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/file_utils.dart'; import 'package:aves/utils/file_utils.dart';
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
@ -15,6 +16,8 @@ class _DebugCacheSectionState extends State<DebugCacheSection> with AutomaticKee
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
final currentSizeBytes = formatFileSize(asciiLocale, imageCache.currentSizeBytes);
final maxSizeBytes = formatFileSize(asciiLocale, imageCache.maximumSizeBytes);
return AvesExpansionTile( return AvesExpansionTile(
title: 'Cache', title: 'Cache',
children: [ children: [
@ -25,7 +28,7 @@ class _DebugCacheSectionState extends State<DebugCacheSection> with AutomaticKee
Row( Row(
children: [ children: [
Expanded( Expanded(
child: Text('Image cache:\n\t${imageCache.currentSize}/${imageCache.maximumSize} items\n\t${formatFileSize('en_US', imageCache.currentSizeBytes)}/${formatFileSize('en_US', imageCache.maximumSizeBytes)}'), child: Text('Image cache:\n\t${imageCache.currentSize}/${imageCache.maximumSize} items\n\t$currentSizeBytes/$maxSizeBytes'),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
ElevatedButton( ElevatedButton(

View file

@ -7,6 +7,7 @@ import 'package:aves/model/metadata/trash.dart';
import 'package:aves/model/vaults/details.dart'; import 'package:aves/model/vaults/details.dart';
import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/model/video_playback.dart'; import 'package:aves/model/video_playback.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/file_utils.dart'; import 'package:aves/utils/file_utils.dart';
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
@ -65,7 +66,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
return Row( return Row(
children: [ children: [
Expanded( Expanded(
child: Text('DB file size: ${formatFileSize('en_US', snapshot.data!)}'), child: Text('DB file size: ${formatFileSize(asciiLocale, snapshot.data!)}'),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
ElevatedButton( ElevatedButton(

View file

@ -1,3 +1,4 @@
import 'package:aves/ref/locales.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/file_utils.dart'; import 'package:aves/utils/file_utils.dart';
@ -47,7 +48,7 @@ class _DebugStorageSectionState extends State<DebugStorageSection> with Automati
'isPrimary': '${v.isPrimary}', 'isPrimary': '${v.isPrimary}',
'isRemovable': '${v.isRemovable}', 'isRemovable': '${v.isRemovable}',
'state': v.state, 'state': v.state,
if (freeSpace != null) 'freeSpace': formatFileSize('en_US', freeSpace), if (freeSpace != null) 'freeSpace': formatFileSize(asciiLocale, freeSpace),
}, },
), ),
), ),

View file

@ -38,6 +38,7 @@ class RenameEntrySetPage extends StatefulWidget {
class _RenameEntrySetPageState extends State<RenameEntrySetPage> { class _RenameEntrySetPageState extends State<RenameEntrySetPage> {
final TextEditingController _patternTextController = TextEditingController(); final TextEditingController _patternTextController = TextEditingController();
final ValueNotifier<NamingPattern> _namingPatternNotifier = ValueNotifier<NamingPattern>(const NamingPattern([])); final ValueNotifier<NamingPattern> _namingPatternNotifier = ValueNotifier<NamingPattern>(const NamingPattern([]));
late final String locale;
static const int previewMax = 10; static const int previewMax = 10;
static const double thumbnailExtent = 48; static const double thumbnailExtent = 48;
@ -51,7 +52,11 @@ class _RenameEntrySetPageState extends State<RenameEntrySetPage> {
super.initState(); super.initState();
_patternTextController.text = settings.entryRenamingPattern; _patternTextController.text = settings.entryRenamingPattern;
_patternTextController.addListener(_onUserPatternChanged); _patternTextController.addListener(_onUserPatternChanged);
WidgetsBinding.instance.addPostFrameCallback((_) {
locale = context.l10n.localeName;
_onUserPatternChanged(); _onUserPatternChanged();
});
} }
@override @override
@ -229,6 +234,7 @@ class _RenameEntrySetPageState extends State<RenameEntrySetPage> {
_namingPatternNotifier.value = NamingPattern.from( _namingPatternNotifier.value = NamingPattern.from(
userPattern: _patternTextController.text, userPattern: _patternTextController.text,
entryCount: entryCount, entryCount: entryCount,
locale: locale,
); );
} }

View file

@ -4,6 +4,7 @@ import 'dart:typed_data';
import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
@ -117,7 +118,7 @@ class _SettingsMobilePageState extends State<SettingsMobilePage> with FeedbackMi
final allJsonString = jsonEncode(allMap); final allJsonString = jsonEncode(allMap);
final success = await storageService.createFile( final success = await storageService.createFile(
'aves-settings-${DateFormat('yyyyMMdd_HHmmss').format(DateTime.now())}.json', 'aves-settings-${DateFormat('yyyyMMdd_HHmmss', asciiLocale).format(DateTime.now())}.json',
MimeTypes.json, MimeTypes.json,
Uint8List.fromList(utf8.encode(allJsonString)), Uint8List.fromList(utf8.encode(allJsonString)),
); );

View file

@ -1,13 +1,20 @@
import 'package:aves/model/naming_pattern.dart'; import 'package:aves/model/naming_pattern.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
setUpAll(() async {
await initializeDateFormatting();
});
test('mixed processors', () { test('mixed processors', () {
const entryCount = 42; const entryCount = 42;
const locale = 'en';
expect( expect(
NamingPattern.from( NamingPattern.from(
userPattern: 'pure literal', userPattern: 'pure literal',
entryCount: entryCount, entryCount: entryCount,
locale: locale,
).processors, ).processors,
[ [
const LiteralNamingProcessor('pure literal'), const LiteralNamingProcessor('pure literal'),
@ -17,10 +24,11 @@ void main() {
NamingPattern.from( NamingPattern.from(
userPattern: 'prefix<date,yyyy-MM-ddTHH:mm:ss>suffix', userPattern: 'prefix<date,yyyy-MM-ddTHH:mm:ss>suffix',
entryCount: entryCount, entryCount: entryCount,
locale: locale,
).processors, ).processors,
[ [
const LiteralNamingProcessor('prefix'), const LiteralNamingProcessor('prefix'),
DateNamingProcessor('yyyy-MM-ddTHH:mm:ss'), DateNamingProcessor('yyyy-MM-ddTHH:mm:ss', locale),
const LiteralNamingProcessor('suffix'), const LiteralNamingProcessor('suffix'),
], ],
); );
@ -28,9 +36,10 @@ void main() {
NamingPattern.from( NamingPattern.from(
userPattern: '<date,yyyy-MM-ddTHH:mm:ss> <name>', userPattern: '<date,yyyy-MM-ddTHH:mm:ss> <name>',
entryCount: entryCount, entryCount: entryCount,
locale: locale,
).processors, ).processors,
[ [
DateNamingProcessor('yyyy-MM-ddTHH:mm:ss'), DateNamingProcessor('yyyy-MM-ddTHH:mm:ss', locale),
const LiteralNamingProcessor(' '), const LiteralNamingProcessor(' '),
const NameNamingProcessor(), const NameNamingProcessor(),
], ],