feat(legacy-settings): DST-aware timezone with phone auto-detect
This commit is contained in:
@@ -18,11 +18,13 @@ import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:timezone/data/latest_all.dart' as tz;
|
||||
|
||||
Future<void> initApp(EnvironmentEnum env) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
await initializeDateFormatting();
|
||||
tz.initializeTimeZones();
|
||||
|
||||
final sharedPreferences = await SharedPreferences.getInstance();
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ dependencies:
|
||||
cupertino_icons: ^1.0.8
|
||||
flutter_svg: ^2.2.2
|
||||
intl: ^0.20.2
|
||||
timezone: ^0.10.1
|
||||
go_router_builder: ^4.1.1
|
||||
build_runner: ^2.7.1
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ part 'timezone_selection_provider.g.dart';
|
||||
@riverpod
|
||||
class TimezoneSelection extends _$TimezoneSelection {
|
||||
@override
|
||||
int? build() => null;
|
||||
String? build() => null;
|
||||
|
||||
void select(int value) => state = value;
|
||||
void select(String iana) => state = iana;
|
||||
|
||||
void clear() => state = null;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ part of 'timezone_selection_provider.dart';
|
||||
const timezoneSelectionProvider = TimezoneSelectionProvider._();
|
||||
|
||||
final class TimezoneSelectionProvider
|
||||
extends $NotifierProvider<TimezoneSelection, int?> {
|
||||
extends $NotifierProvider<TimezoneSelection, String?> {
|
||||
const TimezoneSelectionProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
@@ -33,28 +33,28 @@ final class TimezoneSelectionProvider
|
||||
TimezoneSelection create() => TimezoneSelection();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(int? value) {
|
||||
Override overrideWithValue(String? value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<int?>(value),
|
||||
providerOverride: $SyncValueProvider<String?>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$timezoneSelectionHash() => r'f108a0d1664e0c7a8423b1ec735745ac1781795b';
|
||||
String _$timezoneSelectionHash() => r'97150a64513baae3f18edde7da8396b91f4f1e2f';
|
||||
|
||||
abstract class _$TimezoneSelection extends $Notifier<int?> {
|
||||
int? build();
|
||||
abstract class _$TimezoneSelection extends $Notifier<String?> {
|
||||
String? build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<int?, int?>;
|
||||
final ref = this.ref as $Ref<String?, String?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<int?, int?>,
|
||||
int?,
|
||||
AnyNotifier<String?, String?>,
|
||||
String?,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
|
||||
@@ -21,185 +21,194 @@ class TimezoneScreen extends ConsumerWidget {
|
||||
prev.isLoading &&
|
||||
!next.isLoading &&
|
||||
!next.hasError) {
|
||||
ref.read(timezoneSelectionProvider.notifier).clear();
|
||||
await showSuccessDialog(context, I18n.deviceUpdatedSuccess);
|
||||
}
|
||||
});
|
||||
|
||||
final primaryColor = context.sfColors.legacyPrimary;
|
||||
final device = ref.watch(selectedDeviceProvider).value;
|
||||
final currentTimezone = device?.settings.timezone ?? 0;
|
||||
final selectedTimezone =
|
||||
ref.watch(timezoneSelectionProvider) ?? currentTimezone;
|
||||
final selectedIana = ref.watch(timezoneSelectionProvider);
|
||||
final isSaving = ref.watch(
|
||||
timezoneControllerProvider.select((s) => s.isLoading),
|
||||
);
|
||||
|
||||
final selected = timezoneEntries
|
||||
.where((t) => t.$1 == selectedTimezone)
|
||||
.firstOrNull;
|
||||
final others = timezoneEntries
|
||||
.where((t) => t.$1 != selectedTimezone)
|
||||
.toList();
|
||||
Future<void> onPhoneTap() async {
|
||||
if (device == null) return;
|
||||
if (!await guardDeviceCommand(context, ref)) return;
|
||||
if (!context.mounted) return;
|
||||
ref
|
||||
.read(timezoneControllerProvider.notifier)
|
||||
.save(device: device, newTimezone: phoneOffsetHours());
|
||||
}
|
||||
|
||||
Future<void> onSaveSelection() async {
|
||||
if (device == null || selectedIana == null) return;
|
||||
if (!await guardDeviceCommand(context, ref)) return;
|
||||
if (!context.mounted) return;
|
||||
final entry = timezoneEntries.firstWhere((e) => e.iana == selectedIana);
|
||||
ref.read(timezoneControllerProvider.notifier).save(
|
||||
device: device,
|
||||
newTimezone: entry.currentOffsetHours,
|
||||
);
|
||||
}
|
||||
|
||||
return LegacyPageLayout(
|
||||
title: context.translate(I18n.timezone),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
children: [
|
||||
if (selected != null) ...[
|
||||
_SelectedTimezoneCard(
|
||||
city: selected.$2,
|
||||
continent: selected.$3,
|
||||
label: formatUtcOffset(selected.$1),
|
||||
primaryColor: primaryColor,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4, bottom: 8),
|
||||
child: Text(
|
||||
context.translate(I18n.timezoneOther),
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
_UsePhoneCard(
|
||||
primaryColor: primaryColor,
|
||||
onTap: isSaving ? null : onPhoneTap,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4, bottom: 8),
|
||||
child: Text(
|
||||
context.translate(I18n.timezoneOther),
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
...others.map(
|
||||
),
|
||||
...timezoneEntries.map(
|
||||
(tz) => _TimezoneItem(
|
||||
city: tz.$2,
|
||||
continent: tz.$3,
|
||||
label: formatUtcOffset(tz.$1),
|
||||
entry: tz,
|
||||
selected: tz.iana == selectedIana,
|
||||
primaryColor: primaryColor,
|
||||
onTap: () => ref
|
||||
.read(timezoneSelectionProvider.notifier)
|
||||
.select(tz.$1),
|
||||
.select(tz.iana),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
footer: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () async {
|
||||
if (device == null) return;
|
||||
if (!await guardDeviceCommand(context, ref)) return;
|
||||
if (!context.mounted) return;
|
||||
ref.read(timezoneControllerProvider.notifier).save(
|
||||
device: device,
|
||||
newTimezone: selectedTimezone,
|
||||
);
|
||||
},
|
||||
text: isSaving ? '...' : context.translate(I18n.save),
|
||||
color: primaryColor,
|
||||
footer: selectedIana == null
|
||||
? null
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: isSaving ? null : onSaveSelection,
|
||||
text: isSaving ? '...' : context.translate(I18n.save),
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _UsePhoneCard extends StatelessWidget {
|
||||
final Color primaryColor;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const _UsePhoneCard({required this.primaryColor, required this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final offsetLabel = formatUtcOffset(phoneOffsetHours());
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor.withValues(alpha: 0.08),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: primaryColor.withValues(alpha: 0.3)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 52,
|
||||
height: 52,
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Center(
|
||||
child: Icon(Icons.phone_iphone, color: Colors.white, size: 28),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.translate(I18n.timezoneUsePhone),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
offsetLabel,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: primaryColor.withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(Icons.chevron_right, color: primaryColor, size: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SelectedTimezoneCard extends StatelessWidget {
|
||||
final String city;
|
||||
final String continent;
|
||||
final String label;
|
||||
final Color primaryColor;
|
||||
|
||||
const _SelectedTimezoneCard({
|
||||
required this.city,
|
||||
required this.continent,
|
||||
required this.label,
|
||||
required this.primaryColor,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor.withValues(alpha: 0.08),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: primaryColor.withValues(alpha: 0.3)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 52,
|
||||
height: 52,
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Center(
|
||||
child: Icon(Icons.access_time, color: Colors.white, size: 28),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'$city · $continent',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: primaryColor.withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(Icons.check_circle, color: primaryColor, size: 28),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TimezoneItem extends StatelessWidget {
|
||||
final String city;
|
||||
final String continent;
|
||||
final String label;
|
||||
final TimezoneEntry entry;
|
||||
final bool selected;
|
||||
final Color primaryColor;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _TimezoneItem({
|
||||
required this.city,
|
||||
required this.continent,
|
||||
required this.label,
|
||||
required this.entry,
|
||||
required this.selected,
|
||||
required this.primaryColor,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final offsetLabel = formatUtcOffset(entry.currentOffsetHours);
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
margin: const EdgeInsets.only(bottom: 4),
|
||||
decoration: selected
|
||||
? BoxDecoration(
|
||||
color: primaryColor.withValues(alpha: 0.08),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
)
|
||||
: null,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.outlineVariant,
|
||||
color: selected
|
||||
? primaryColor
|
||||
: Theme.of(context).colorScheme.outlineVariant,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.public,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
color: selected
|
||||
? Colors.white
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
@@ -210,12 +219,16 @@ class _TimezoneItem extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'$label · $city',
|
||||
style: const TextStyle(fontSize: 15),
|
||||
'$offsetLabel · ${entry.city}',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: selected ? FontWeight.w600 : FontWeight.w400,
|
||||
color: selected ? primaryColor : null,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
continent,
|
||||
entry.continent,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
@@ -224,6 +237,8 @@ class _TimezoneItem extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (selected)
|
||||
Icon(Icons.check_circle, color: primaryColor, size: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,32 +1,53 @@
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
class TimezoneEntry {
|
||||
const TimezoneEntry({
|
||||
required this.iana,
|
||||
required this.city,
|
||||
required this.continent,
|
||||
});
|
||||
|
||||
final String iana;
|
||||
final String city;
|
||||
final String continent;
|
||||
|
||||
int get currentOffsetHours {
|
||||
final location = tz.getLocation(iana);
|
||||
return tz.TZDateTime.now(location).timeZoneOffset.inHours;
|
||||
}
|
||||
}
|
||||
|
||||
const timezoneEntries = [
|
||||
(-12, 'Baker Island', 'Pacific'),
|
||||
(-11, 'Pago Pago', 'Pacific'),
|
||||
(-10, 'Honolulu', 'Pacific'),
|
||||
(-9, 'Anchorage', 'America'),
|
||||
(-8, 'Los Angeles', 'America'),
|
||||
(-7, 'Denver', 'America'),
|
||||
(-6, 'Mexico City', 'America'),
|
||||
(-5, 'New York', 'America'),
|
||||
(-4, 'Santiago', 'America'),
|
||||
(-3, 'Buenos Aires', 'America'),
|
||||
(-2, 'South Georgia', 'Atlantic'),
|
||||
(-1, 'Azores', 'Atlantic'),
|
||||
(0, 'London', 'Europe'),
|
||||
(1, 'Madrid', 'Europe'),
|
||||
(2, 'Cairo', 'Africa'),
|
||||
(3, 'Moscow', 'Europe'),
|
||||
(4, 'Dubai', 'Asia'),
|
||||
(5, 'Karachi', 'Asia'),
|
||||
(6, 'Dhaka', 'Asia'),
|
||||
(7, 'Bangkok', 'Asia'),
|
||||
(8, 'Shanghai', 'Asia'),
|
||||
(9, 'Tokyo', 'Asia'),
|
||||
(10, 'Sydney', 'Australia'),
|
||||
(11, 'Noumea', 'Pacific'),
|
||||
(12, 'Auckland', 'Pacific'),
|
||||
TimezoneEntry(iana: 'Pacific/Midway', city: 'Baker Island', continent: 'Pacific'),
|
||||
TimezoneEntry(iana: 'Pacific/Pago_Pago', city: 'Pago Pago', continent: 'Pacific'),
|
||||
TimezoneEntry(iana: 'Pacific/Honolulu', city: 'Honolulu', continent: 'Pacific'),
|
||||
TimezoneEntry(iana: 'America/Anchorage', city: 'Anchorage', continent: 'America'),
|
||||
TimezoneEntry(iana: 'America/Los_Angeles', city: 'Los Angeles', continent: 'America'),
|
||||
TimezoneEntry(iana: 'America/Denver', city: 'Denver', continent: 'America'),
|
||||
TimezoneEntry(iana: 'America/Mexico_City', city: 'Mexico City', continent: 'America'),
|
||||
TimezoneEntry(iana: 'America/New_York', city: 'New York', continent: 'America'),
|
||||
TimezoneEntry(iana: 'America/Santiago', city: 'Santiago', continent: 'America'),
|
||||
TimezoneEntry(iana: 'America/Argentina/Buenos_Aires', city: 'Buenos Aires', continent: 'America'),
|
||||
TimezoneEntry(iana: 'Atlantic/South_Georgia', city: 'South Georgia', continent: 'Atlantic'),
|
||||
TimezoneEntry(iana: 'Atlantic/Azores', city: 'Azores', continent: 'Atlantic'),
|
||||
TimezoneEntry(iana: 'Europe/London', city: 'London', continent: 'Europe'),
|
||||
TimezoneEntry(iana: 'Europe/Madrid', city: 'Madrid', continent: 'Europe'),
|
||||
TimezoneEntry(iana: 'Africa/Cairo', city: 'Cairo', continent: 'Africa'),
|
||||
TimezoneEntry(iana: 'Europe/Moscow', city: 'Moscow', continent: 'Europe'),
|
||||
TimezoneEntry(iana: 'Asia/Dubai', city: 'Dubai', continent: 'Asia'),
|
||||
TimezoneEntry(iana: 'Asia/Karachi', city: 'Karachi', continent: 'Asia'),
|
||||
TimezoneEntry(iana: 'Asia/Dhaka', city: 'Dhaka', continent: 'Asia'),
|
||||
TimezoneEntry(iana: 'Asia/Bangkok', city: 'Bangkok', continent: 'Asia'),
|
||||
TimezoneEntry(iana: 'Asia/Shanghai', city: 'Shanghai', continent: 'Asia'),
|
||||
TimezoneEntry(iana: 'Asia/Tokyo', city: 'Tokyo', continent: 'Asia'),
|
||||
TimezoneEntry(iana: 'Australia/Sydney', city: 'Sydney', continent: 'Australia'),
|
||||
TimezoneEntry(iana: 'Pacific/Noumea', city: 'Noumea', continent: 'Pacific'),
|
||||
TimezoneEntry(iana: 'Pacific/Auckland', city: 'Auckland', continent: 'Pacific'),
|
||||
];
|
||||
|
||||
String formatUtcOffset(int offset) {
|
||||
final sign = offset >= 0 ? '+' : '';
|
||||
return 'UTC$sign$offset';
|
||||
}
|
||||
|
||||
int phoneOffsetHours() => DateTime.now().timeZoneOffset.inHours;
|
||||
|
||||
@@ -63,6 +63,7 @@ dependencies:
|
||||
json_serializable: ^6.11.2
|
||||
url_launcher: ^6.3.2
|
||||
flutter_contacts: ^1.1.9+2
|
||||
timezone: ^0.10.1
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
|
||||
@@ -766,6 +766,7 @@
|
||||
"timezoneUpdated": "Zeitzone aktualisiert",
|
||||
"errorTimezone": "Die Zeitzone konnte nicht aktualisiert werden",
|
||||
"timezoneOther": "Andere Zeitzonen",
|
||||
"timezoneUsePhone": "Zeitzone dieses Telefons verwenden",
|
||||
"takingPhoto": "Foto wird aufgenommen...",
|
||||
"errorTakePicture": "Fehler beim Fotografieren",
|
||||
"errorFetchPhotos": "Fehler beim Abrufen der Fotos",
|
||||
|
||||
@@ -934,6 +934,7 @@
|
||||
"timezoneUpdated": "Timezone updated",
|
||||
"errorTimezone": "Could not update timezone",
|
||||
"timezoneOther": "Other timezones",
|
||||
"timezoneUsePhone": "Use this phone's timezone",
|
||||
"takingPhoto": "Taking photo...",
|
||||
"errorTakePicture": "Error taking photo",
|
||||
"errorFetchPhotos": "Error fetching photos",
|
||||
|
||||
@@ -935,6 +935,7 @@
|
||||
"timezoneUpdated": "Zona horaria actualizada",
|
||||
"errorTimezone": "No se pudo actualizar la zona horaria",
|
||||
"timezoneOther": "Otras zonas horarias",
|
||||
"timezoneUsePhone": "Usar la zona horaria del móvil",
|
||||
"takingPhoto": "Tomando foto...",
|
||||
"errorTakePicture": "Error al tomar la foto",
|
||||
"errorFetchPhotos": "Error al obtener las fotos",
|
||||
|
||||
@@ -766,6 +766,7 @@
|
||||
"timezoneUpdated": "Fuseau horaire mis à jour",
|
||||
"errorTimezone": "Impossible de mettre à jour le fuseau horaire",
|
||||
"timezoneOther": "Autres fuseaux horaires",
|
||||
"timezoneUsePhone": "Utiliser le fuseau horaire du téléphone",
|
||||
"takingPhoto": "Prise de photo...",
|
||||
"errorTakePicture": "Erreur lors de la prise de photo",
|
||||
"errorFetchPhotos": "Erreur lors de la récupération des photos",
|
||||
|
||||
@@ -766,6 +766,7 @@
|
||||
"timezoneUpdated": "Fuso orario aggiornato",
|
||||
"errorTimezone": "Impossibile aggiornare il fuso orario",
|
||||
"timezoneOther": "Altri fusi orari",
|
||||
"timezoneUsePhone": "Usa il fuso orario del telefono",
|
||||
"takingPhoto": "Scattando foto...",
|
||||
"errorTakePicture": "Errore durante lo scatto della foto",
|
||||
"errorFetchPhotos": "Errore durante il recupero delle foto",
|
||||
|
||||
@@ -766,6 +766,7 @@
|
||||
"timezoneUpdated": "Fuso horário atualizado",
|
||||
"errorTimezone": "Não foi possível atualizar o fuso horário",
|
||||
"timezoneOther": "Outros fusos horários",
|
||||
"timezoneUsePhone": "Usar o fuso horário do telemóvel",
|
||||
"takingPhoto": "Tirando foto...",
|
||||
"errorTakePicture": "Erro ao tirar foto",
|
||||
"errorFetchPhotos": "Erro ao obter fotos",
|
||||
|
||||
@@ -887,6 +887,7 @@ class I18n {
|
||||
static const String timezoneOther = 'timezoneOther';
|
||||
static const String timezoneSearch = 'timezoneSearch';
|
||||
static const String timezoneUpdated = 'timezoneUpdated';
|
||||
static const String timezoneUsePhone = 'timezoneUsePhone';
|
||||
static const String today = 'today';
|
||||
static const String topApps = 'topApps';
|
||||
static const String totalSteps = 'totalSteps';
|
||||
|
||||
Reference in New Issue
Block a user