diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index afd97abab..3732de0ff 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -6,6 +6,8 @@ on: paths: - 'lib/l10n/intl_en.arb' branches: [ main ] + schedule: + - cron: '0 */12 * * *' # Every 12 hours - https://crontab.guru/#0_*/12_*_*_* jobs: synchronize-with-crowdin: diff --git a/.gitignore b/.gitignore index 2ff5fe7c7..465f249bd 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ #Visual Studio Code related .vscode/launch.json +.vscode/settings.json # Flutter/Dart/Pub related **/doc/api/ @@ -28,7 +29,6 @@ .pub/ /build/ -lib/generated/* # Web related lib/generated_plugin_registrant.dart diff --git a/README.md b/README.md index 571d732c7..ebcd1164a 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,9 @@ You can alternatively install the build from PlayStore or F-Droid. 3. Pull in all submodules with `git submodule update --init --recursive` 4. Enable repo git hooks `git config core.hooksPath hooks` 5. Setup TensorFlowLite by executing `setup.sh` -6. For Android, [setup your keystore](https://docs.flutter.dev/deployment/android#create-an-upload-keystore) and run `flutter build apk --release --flavor independent` -7. For iOS, run `flutter build ios` +6. If using Visual Studio Code, add the [Flutter Intl](https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl) extension +7. For Android, [setup your keystore](https://docs.flutter.dev/deployment/android#create-an-upload-keystore) and run `flutter build apk --release --flavor independent` +8. For iOS, run `flutter build ios`
## 🙋 Help diff --git a/lib/app.dart b/lib/app.dart index 7d8409770..feba2c151 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -21,29 +21,44 @@ class EnteApp extends StatefulWidget { final Future Function(String) runBackgroundTask; final Future Function(String) killBackgroundTask; final AdaptiveThemeMode? savedThemeMode; + final Locale locale; const EnteApp( this.runBackgroundTask, this.killBackgroundTask, + this.locale, this.savedThemeMode, { Key? key, }) : super(key: key); + static void setLocale(BuildContext context, Locale newLocale) { + final state = context.findAncestorStateOfType<_EnteAppState>()!; + state.setLocale(newLocale); + } + @override State createState() => _EnteAppState(); } class _EnteAppState extends State with WidgetsBindingObserver { final _logger = Logger("EnteAppState"); + late Locale locale; @override void initState() { _logger.info('init App'); super.initState(); + locale = widget.locale; setupIntentAction(); WidgetsBinding.instance.addObserver(this); } + setLocale(Locale newLocale) { + setState(() { + locale = newLocale; + }); + } + void setupIntentAction() async { final mediaExtentionAction = Platform.isAndroid ? await initIntentAction() @@ -72,6 +87,7 @@ class _EnteAppState extends State with WidgetsBindingObserver { : const HomeWidget(), debugShowCheckedModeBanner: false, builder: EasyLoading.init(), + locale: locale, supportedLocales: appSupportedLocales, localeListResolutionCallback: localResolutionCallBack, localizationsDelegates: const [ @@ -89,6 +105,7 @@ class _EnteAppState extends State with WidgetsBindingObserver { home: const HomeWidget(), debugShowCheckedModeBanner: false, builder: EasyLoading.init(), + locale: locale, supportedLocales: appSupportedLocales, localeListResolutionCallback: localResolutionCallBack, localizationsDelegates: const [ diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 236419b4d..63f62ed39 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -57,3 +57,11 @@ const double restrictedMaxWidth = 430; const double mobileSmallThreshold = 336; const publicLinkDeviceLimits = [50, 25, 10, 5, 2, 1]; + +const kilometersPerDegree = 111.16; + +const radiusValues = [2, 10, 20, 40, 80, 200, 400, 1200]; + +const defaultRadiusValueIndex = 4; + +const galleryGridSpacing = 2.0; diff --git a/lib/db/entities_db.dart b/lib/db/entities_db.dart new file mode 100644 index 000000000..b8b48fbe4 --- /dev/null +++ b/lib/db/entities_db.dart @@ -0,0 +1,65 @@ +import 'package:flutter/foundation.dart'; +import 'package:photos/db/files_db.dart'; +import "package:photos/models/api/entity/type.dart"; +import "package:photos/models/local_entity_data.dart"; +import 'package:sqflite/sqlite_api.dart'; + +extension EntitiesDB on FilesDB { + Future upsertEntities( + List data, { + ConflictAlgorithm conflictAlgorithm = ConflictAlgorithm.replace, + }) async { + debugPrint("Inserting missing PathIDToLocalIDMapping"); + final db = await database; + var batch = db.batch(); + int batchCounter = 0; + for (LocalEntityData e in data) { + if (batchCounter == 400) { + await batch.commit(noResult: true); + batch = db.batch(); + batchCounter = 0; + } + batch.insert( + "entities", + e.toJson(), + conflictAlgorithm: conflictAlgorithm, + ); + batchCounter++; + } + await batch.commit(noResult: true); + } + + Future deleteEntities( + List ids, + ) async { + final db = await database; + var batch = db.batch(); + int batchCounter = 0; + for (String id in ids) { + if (batchCounter == 400) { + await batch.commit(noResult: true); + batch = db.batch(); + batchCounter = 0; + } + batch.delete( + "entities", + where: "id = ?", + whereArgs: [id], + ); + batchCounter++; + } + await batch.commit(noResult: true); + } + + Future> getEntities(EntityType type) async { + final db = await database; + final List> maps = await db.query( + "entities", + where: "type = ?", + whereArgs: [type.typeToString()], + ); + return List.generate(maps.length, (i) { + return LocalEntityData.fromJson(maps[i]); + }); + } +} diff --git a/lib/db/files_db.dart b/lib/db/files_db.dart index 67f09df9e..af8cd6057 100644 --- a/lib/db/files_db.dart +++ b/lib/db/files_db.dart @@ -1,3 +1,4 @@ +import 'dart:developer' as dev; import 'dart:io' as io; import 'package:flutter/foundation.dart'; @@ -8,7 +9,7 @@ import 'package:photos/models/backup_status.dart'; import 'package:photos/models/file.dart'; import 'package:photos/models/file_load_result.dart'; import 'package:photos/models/file_type.dart'; -import 'package:photos/models/location.dart'; +import 'package:photos/models/location/location.dart'; import 'package:photos/models/magic_metadata.dart'; import 'package:photos/utils/file_uploader_util.dart'; import 'package:sqflite/sqflite.dart'; @@ -79,6 +80,7 @@ class FilesDB { ...createOnDeviceFilesAndPathCollection(), ...addFileSizeColumn(), ...updateIndexes(), + ...createEntityDataTable(), ]; final dbConfig = MigrationConfig( @@ -331,6 +333,20 @@ class FilesDB { ]; } + static List createEntityDataTable() { + return [ + ''' + CREATE TABLE IF NOT EXISTS entities ( + id TEXT PRIMARY KEY NOT NULL, + type TEXT NOT NULL, + ownerID INTEGER NOT NULL, + data TEXT NOT NULL DEFAULT '{}', + updatedAt INTEGER NOT NULL + ); + ''' + ]; + } + static List addFileSizeColumn() { return [ ''' @@ -486,6 +502,8 @@ class FilesDB { int visibility = visibilityVisible, Set? ignoredCollectionIDs, }) async { + final stopWatch = Stopwatch()..start(); + final db = await instance.database; final order = (asc ?? false ? 'ASC' : 'DESC'); final results = await db.query( @@ -501,6 +519,9 @@ class FilesDB { final files = convertToFiles(results); final List deduplicatedFiles = _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs); + dev.log( + "getAllPendingOrUploadedFiles time taken: ${stopWatch.elapsedMilliseconds} ms"); + stopWatch.stop(); return FileLoadResult(deduplicatedFiles, files.length == limit); } @@ -1376,6 +1397,33 @@ class FilesDB { return filesCount; } + Future fetchAllUploadedAndSharedFilesWithLocation( + int startTime, + int endTime, { + int? limit, + bool? asc, + Set? ignoredCollectionIDs, + }) async { + final db = await instance.database; + final order = (asc ?? false ? 'ASC' : 'DESC'); + final results = await db.query( + filesTable, + where: + '$columnLatitude IS NOT NULL AND $columnLongitude IS NOT NULL AND ($columnLatitude IS NOT 0 OR $columnLongitude IS NOT 0)' + ' AND $columnCreationTime >= ? AND $columnCreationTime <= ? AND ' + '($columnMMdVisibility IS NULL OR $columnMMdVisibility = ?)' + ' AND ($columnLocalID IS NOT NULL OR ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1))', + whereArgs: [startTime, endTime, visibilityVisible], + orderBy: + '$columnCreationTime ' + order + ', $columnModificationTime ' + order, + limit: limit, + ); + final files = convertToFiles(results); + final List deduplicatedFiles = + _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs); + return FileLoadResult(deduplicatedFiles, files.length == limit); + } + Map _getRowForFile(File file) { final row = {}; if (file.generatedID != null) { @@ -1387,6 +1435,10 @@ class FilesDB { row[columnCollectionID] = file.collectionID ?? -1; row[columnTitle] = file.title; row[columnDeviceFolder] = file.deviceFolder; + // if (file.location == null || + // (file.location!.latitude == null && file.location!.longitude == null)) { + // file.location = Location.randomLocation(); + // } if (file.location != null) { row[columnLatitude] = file.location!.latitude; row[columnLongitude] = file.location!.longitude; @@ -1471,7 +1523,10 @@ class FilesDB { file.title = row[columnTitle]; file.deviceFolder = row[columnDeviceFolder]; if (row[columnLatitude] != null && row[columnLongitude] != null) { - file.location = Location(row[columnLatitude], row[columnLongitude]); + file.location = Location( + latitude: row[columnLatitude], + longitude: row[columnLongitude], + ); } file.fileType = getFileType(row[columnFileType]); file.creationTime = row[columnCreationTime]; diff --git a/lib/events/location_tag_updated_event.dart b/lib/events/location_tag_updated_event.dart new file mode 100644 index 000000000..ae8db761c --- /dev/null +++ b/lib/events/location_tag_updated_event.dart @@ -0,0 +1,16 @@ +import 'package:photos/events/event.dart'; +import "package:photos/models/local_entity_data.dart"; +import "package:photos/models/location_tag/location_tag.dart"; + +class LocationTagUpdatedEvent extends Event { + final List>? updatedLocTagEntities; + final LocTagEventType type; + + LocationTagUpdatedEvent(this.type, {this.updatedLocTagEntities}); +} + +enum LocTagEventType { + add, + update, + delete, +} diff --git a/lib/gateways/entity_gw.dart b/lib/gateways/entity_gw.dart new file mode 100644 index 000000000..a88d31eeb --- /dev/null +++ b/lib/gateways/entity_gw.dart @@ -0,0 +1,114 @@ +import "package:dio/dio.dart"; +import "package:photos/models/api/entity/data.dart"; +import "package:photos/models/api/entity/key.dart"; +import "package:photos/models/api/entity/type.dart"; + +class EntityGateway { + final Dio _enteDio; + + EntityGateway(this._enteDio); + + Future createKey( + EntityType entityType, + String encKey, + String header, + ) async { + await _enteDio.post( + "/user-entity/key", + data: { + "type": entityType.typeToString(), + "encryptedKey": encKey, + "header": header, + }, + ); + } + + Future getKey(EntityType type) async { + try { + final response = await _enteDio.get( + "/user-entity/key", + queryParameters: { + "type": type.typeToString(), + }, + ); + return EntityKey.fromMap(response.data); + } on DioError catch (e) { + if (e.response != null && (e.response!.statusCode ?? 0) == 404) { + throw EntityKeyNotFound(); + } else { + rethrow; + } + } catch (e) { + rethrow; + } + } + + Future createEntity( + EntityType type, + String encryptedData, + String header, + ) async { + final response = await _enteDio.post( + "/user-entity/entity", + data: { + "encryptedData": encryptedData, + "header": header, + "type": type.typeToString(), + }, + ); + return EntityData.fromMap(response.data); + } + + Future updateEntity( + EntityType type, + String id, + String encryptedData, + String header, + ) async { + final response = await _enteDio.put( + "/user-entity/entity", + data: { + "id": id, + "encryptedData": encryptedData, + "header": header, + "type": type.typeToString(), + }, + ); + return EntityData.fromMap(response.data); + } + + Future deleteEntity( + String id, + ) async { + await _enteDio.delete( + "/user-entity/entity", + queryParameters: { + "id": id, + }, + ); + } + + Future> getDiff( + EntityType type, + int sinceTime, { + int limit = 500, + }) async { + final response = await _enteDio.get( + "/user-entity/entity/diff", + queryParameters: { + "sinceTime": sinceTime, + "limit": limit, + "type": type.typeToString(), + }, + ); + final List authEntities = []; + final diff = response.data["diff"] as List; + for (var entry in diff) { + final EntityData entity = EntityData.fromMap(entry); + authEntities.add(entity); + } + return authEntities; + } +} + +class EntityKeyNotFound extends Error {} diff --git a/lib/generated/intl/messages_all.dart b/lib/generated/intl/messages_all.dart new file mode 100644 index 000000000..0bb6ce189 --- /dev/null +++ b/lib/generated/intl/messages_all.dart @@ -0,0 +1,99 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that looks up messages for specific locales by +// delegating to the appropriate library. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:implementation_imports, file_names, unnecessary_new +// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering +// ignore_for_file:argument_type_not_assignable, invalid_assignment +// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases +// ignore_for_file:comment_references + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; +import 'package:intl/src/intl_helpers.dart'; + +import 'messages_cs.dart' as messages_cs; +import 'messages_de.dart' as messages_de; +import 'messages_en.dart' as messages_en; +import 'messages_es.dart' as messages_es; +import 'messages_fr.dart' as messages_fr; +import 'messages_it.dart' as messages_it; +import 'messages_ko.dart' as messages_ko; +import 'messages_nl.dart' as messages_nl; +import 'messages_pl.dart' as messages_pl; +import 'messages_pt.dart' as messages_pt; + +typedef Future LibraryLoader(); +Map _deferredLibraries = { + 'cs': () => new SynchronousFuture(null), + 'de': () => new SynchronousFuture(null), + 'en': () => new SynchronousFuture(null), + 'es': () => new SynchronousFuture(null), + 'fr': () => new SynchronousFuture(null), + 'it': () => new SynchronousFuture(null), + 'ko': () => new SynchronousFuture(null), + 'nl': () => new SynchronousFuture(null), + 'pl': () => new SynchronousFuture(null), + 'pt': () => new SynchronousFuture(null), +}; + +MessageLookupByLibrary? _findExact(String localeName) { + switch (localeName) { + case 'cs': + return messages_cs.messages; + case 'de': + return messages_de.messages; + case 'en': + return messages_en.messages; + case 'es': + return messages_es.messages; + case 'fr': + return messages_fr.messages; + case 'it': + return messages_it.messages; + case 'ko': + return messages_ko.messages; + case 'nl': + return messages_nl.messages; + case 'pl': + return messages_pl.messages; + case 'pt': + return messages_pt.messages; + default: + return null; + } +} + +/// User programs should call this before using [localeName] for messages. +Future initializeMessages(String localeName) { + var availableLocale = Intl.verifiedLocale( + localeName, (locale) => _deferredLibraries[locale] != null, + onFailure: (_) => null); + if (availableLocale == null) { + return new SynchronousFuture(false); + } + var lib = _deferredLibraries[availableLocale]; + lib == null ? new SynchronousFuture(false) : lib(); + initializeInternalMessageLookup(() => new CompositeMessageLookup()); + messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); + return new SynchronousFuture(true); +} + +bool _messagesExistFor(String locale) { + try { + return _findExact(locale) != null; + } catch (e) { + return false; + } +} + +MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { + var actualLocale = + Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); + if (actualLocale == null) return null; + return _findExact(actualLocale); +} diff --git a/lib/generated/intl/messages_cs.dart b/lib/generated/intl/messages_cs.dart new file mode 100644 index 000000000..2bfb5800f --- /dev/null +++ b/lib/generated/intl/messages_cs.dart @@ -0,0 +1,25 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a cs locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'cs'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => {}; +} diff --git a/lib/generated/intl/messages_de.dart b/lib/generated/intl/messages_de.dart new file mode 100644 index 000000000..22274dd89 --- /dev/null +++ b/lib/generated/intl/messages_de.dart @@ -0,0 +1,41 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a de locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'de'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "accountWelcomeBack": + MessageLookupByLibrary.simpleMessage("Willkommen zurück!"), + "activeSessions": + MessageLookupByLibrary.simpleMessage("Aktive Sitzungen"), + "cancel": MessageLookupByLibrary.simpleMessage("Abbrechen"), + "createNewAccount": + MessageLookupByLibrary.simpleMessage("Neues Konto erstellen"), + "deleteAccount": MessageLookupByLibrary.simpleMessage("Konto löschen"), + "email": MessageLookupByLibrary.simpleMessage("E-Mail"), + "enterYourEmailAddress": MessageLookupByLibrary.simpleMessage( + "Geben Sie Ihre E-Mail-Adresse ein"), + "feedback": MessageLookupByLibrary.simpleMessage("Rückmeldung"), + "insecureDevice": + MessageLookupByLibrary.simpleMessage("Unsicheres Gerät"), + "password": MessageLookupByLibrary.simpleMessage("Passwort") + }; +} diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart new file mode 100644 index 000000000..667cb3dfb --- /dev/null +++ b/lib/generated/intl/messages_en.dart @@ -0,0 +1,1240 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a en locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'en'; + + static String m0(count) => + "${Intl.plural(count, one: 'Add item', other: 'Add items')}"; + + static String m1(emailOrName) => "Added by ${emailOrName}"; + + static String m2(albumName) => "Added successfully to ${albumName}"; + + static String m3(paymentProvider) => + "Please cancel your existing subscription from ${paymentProvider} first"; + + static String m4(user) => + "${user} will not be able to add more photos to this album\n\nThey will still be able to remove existing photos added by them"; + + static String m5(isFamilyMember, storageAmountInGb) => + "${Intl.select(isFamilyMember, { + 'true': 'Your family has claimed ${storageAmountInGb} Gb so far', + 'false': 'You have claimed ${storageAmountInGb} Gb so far', + 'other': 'You have claimed ${storageAmountInGb} Gb so far!', + })}"; + + static String m6(albumName) => "Collaborative link created for ${albumName}"; + + static String m7(familyAdminEmail) => + "Please contact ${familyAdminEmail} to manage your subscription"; + + static String m8(provider) => + "Please contact us at support@ente.io to manage your ${provider} subscription."; + + static String m9(currentlyDeleting, totalCount) => + "Deleting ${currentlyDeleting} / ${totalCount}"; + + static String m10(albumName) => + "This will remove the public link for accessing \"${albumName}\"."; + + static String m11(supportEmail) => + "Please drop an email to ${supportEmail} from your registered email address"; + + static String m12(count, storageSaved) => + "Your have cleaned up ${Intl.plural(count, one: '${count} duplicate file', other: '${count} duplicate files')}, saving (${storageSaved}!)"; + + static String m13(newEmail) => "Email changed to ${newEmail}"; + + static String m14(email) => + "${email} does not have an ente account.\n\nSend them an invite to share photos."; + + static String m15(count, formattedNumber) => + "${Intl.plural(count, one: '1 file', other: '${formattedNumber} files')} on this device have been backed up safely"; + + static String m16(count, formattedNumber) => + "${Intl.plural(count, one: '1 file', other: '${formattedNumber} files')} in this album has been backed up safely"; + + static String m17(storageAmountInGB) => + "${storageAmountInGB} GB each time someone signs up for a paid plan and applies your code"; + + static String m18(endDate) => "Free trial valid till ${endDate}"; + + static String m19(count) => + "You can still access ${Intl.plural(count, one: 'it', other: 'them')} on ente as long as you have an active subscription"; + + static String m20(sizeInMBorGB) => "Free up ${sizeInMBorGB}"; + + static String m21(count, formattedSize) => + "${Intl.plural(count, one: 'It can be deleted from the device to free up ${formattedSize}', other: 'They can be deleted from the device to free up ${formattedSize}')}"; + + static String m22(count) => + "${Intl.plural(count, one: '${count} item', other: '${count} items')}"; + + static String m23(count) => "${count} selected"; + + static String m24(expiryTime) => "Link will expire on ${expiryTime}"; + + static String m25(maxValue) => + "When set to the maximum (${maxValue}), the device limit will be relaxed to allow for temporary spikes of large number of viewers."; + + static String m26(count) => + "${Intl.plural(count, zero: 'no memories', one: '${count} memory', other: '${count} memories')}"; + + static String m27(count) => + "${Intl.plural(count, one: 'Move item', other: 'Move items')}"; + + static String m28(albumName) => "Moved successfully to ${albumName}"; + + static String m29(passwordStrengthValue) => + "Password strength: ${passwordStrengthValue}"; + + static String m30(providerName) => + "Please talk to ${providerName} support if you were charged"; + + static String m31(reason) => + "Unfortunately your payment failed due to ${reason}"; + + static String m32(toEmail) => "Please email us at ${toEmail}"; + + static String m33(toEmail) => "Please send the logs to \n${toEmail}"; + + static String m34(storeName) => "Rate us on ${storeName}"; + + static String m35(storageInGB) => + "3. Both of you get ${storageInGB} GB* free"; + + static String m36(userEmail) => + "${userEmail} will be removed from this shared album\n\nAny photos added by them will also be removed from the album"; + + static String m37(endDate) => "Renews on ${endDate}"; + + static String m38(count) => "${count} selected"; + + static String m39(count, yourCount) => + "${count} selected (${yourCount} yours)"; + + static String m40(verificationID) => + "Here\'s my verification ID: ${verificationID} for ente.io."; + + static String m41(verificationID) => + "Hey, can you confirm that this is your ente.io verification ID: ${verificationID}"; + + static String m42(referralCode, referralStorageInGB) => + "ente referral code: ${referralCode} \n\nApply it in Settings → General → Referrals to get ${referralStorageInGB} GB free after you signup for a paid plan\n\nhttps://ente.io"; + + static String m43(numberOfPeople) => + "${Intl.plural(numberOfPeople, zero: 'Share with specific people', one: 'Shared with 1 person', other: 'Shared with ${numberOfPeople} people')}"; + + static String m44(emailIDs) => "Shared with ${emailIDs}"; + + static String m45(fileType) => + "This ${fileType} will be deleted from your device."; + + static String m46(fileType) => + "This ${fileType} is in both ente and your device."; + + static String m47(fileType) => "This ${fileType} will be deleted from ente."; + + static String m48(storageAmountInGB) => "${storageAmountInGB} GB"; + + static String m49(id) => + "Your ${id} is already linked to another ente account.\nIf you would like to use your ${id} with this account, please contact our support\'\'"; + + static String m50(endDate) => + "Your subscription will be cancelled on ${endDate}"; + + static String m51(completed, total) => + "${completed}/${total} memories preserved"; + + static String m52(storageAmountInGB) => + "They also get ${storageAmountInGB} GB"; + + static String m53(email) => "This is ${email}\'s Verification ID"; + + static String m54(email) => "Verify ${email}"; + + static String m55(count) => + "${Intl.plural(count, one: '${count} year ago', other: '${count} years ago')}"; + + static String m56(storageSaved) => + "You have successfully freed up ${storageSaved}!"; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "aNewVersionOfEnteIsAvailable": MessageLookupByLibrary.simpleMessage( + "A new version of ente is available."), + "about": MessageLookupByLibrary.simpleMessage("About"), + "account": MessageLookupByLibrary.simpleMessage("Account"), + "accountWelcomeBack": + MessageLookupByLibrary.simpleMessage("Welcome back!"), + "ackPasswordLostWarning": MessageLookupByLibrary.simpleMessage( + "I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted."), + "activeSessions": + MessageLookupByLibrary.simpleMessage("Active sessions"), + "addANewEmail": MessageLookupByLibrary.simpleMessage("Add a new email"), + "addCollaborator": + MessageLookupByLibrary.simpleMessage("Add collaborator"), + "addItem": m0, + "addLocation": MessageLookupByLibrary.simpleMessage("Add location"), + "addLocationButton": MessageLookupByLibrary.simpleMessage("Add"), + "addMore": MessageLookupByLibrary.simpleMessage("Add more"), + "addToAlbum": MessageLookupByLibrary.simpleMessage("Add to album"), + "addToEnte": MessageLookupByLibrary.simpleMessage("Add to ente"), + "addViewer": MessageLookupByLibrary.simpleMessage("Add viewer"), + "addedAs": MessageLookupByLibrary.simpleMessage("Added as"), + "addedBy": m1, + "addedSuccessfullyTo": m2, + "addingToFavorites": + MessageLookupByLibrary.simpleMessage("Adding to favorites..."), + "advanced": MessageLookupByLibrary.simpleMessage("Advanced"), + "advancedSettings": MessageLookupByLibrary.simpleMessage("Advanced"), + "after1Day": MessageLookupByLibrary.simpleMessage("After 1 day"), + "after1Hour": MessageLookupByLibrary.simpleMessage("After 1 hour"), + "after1Month": MessageLookupByLibrary.simpleMessage("After 1 month"), + "after1Week": MessageLookupByLibrary.simpleMessage("After 1 week"), + "after1Year": MessageLookupByLibrary.simpleMessage("After 1 year"), + "albumOwner": MessageLookupByLibrary.simpleMessage("Owner"), + "albumTitle": MessageLookupByLibrary.simpleMessage("Album title"), + "albumUpdated": MessageLookupByLibrary.simpleMessage("Album updated"), + "albums": MessageLookupByLibrary.simpleMessage("Albums"), + "allClear": MessageLookupByLibrary.simpleMessage("✨ All clear"), + "allMemoriesPreserved": + MessageLookupByLibrary.simpleMessage("All memories preserved"), + "allowAddPhotosDescription": MessageLookupByLibrary.simpleMessage( + "Allow people with the link to also add photos to the shared album."), + "allowAddingPhotos": + MessageLookupByLibrary.simpleMessage("Allow adding photos"), + "allowDownloads": + MessageLookupByLibrary.simpleMessage("Allow downloads"), + "allowPeopleToAddPhotos": + MessageLookupByLibrary.simpleMessage("Allow people to add photos"), + "androidIosWebDesktop": + MessageLookupByLibrary.simpleMessage("Android, iOS, Web, Desktop"), + "appleId": MessageLookupByLibrary.simpleMessage("Apple ID"), + "apply": MessageLookupByLibrary.simpleMessage("Apply"), + "applyCodeTitle": MessageLookupByLibrary.simpleMessage("Apply code"), + "appstoreSubscription": + MessageLookupByLibrary.simpleMessage("AppStore subscription"), + "archive": MessageLookupByLibrary.simpleMessage("Archive"), + "archiveAlbum": MessageLookupByLibrary.simpleMessage("Archive album"), + "archiving": MessageLookupByLibrary.simpleMessage("Archiving..."), + "areYouSureThatYouWantToLeaveTheFamily": + MessageLookupByLibrary.simpleMessage( + "Are you sure that you want to leave the family plan?"), + "areYouSureYouWantToCancel": MessageLookupByLibrary.simpleMessage( + "Are you sure you want to cancel?"), + "areYouSureYouWantToChangeYourPlan": + MessageLookupByLibrary.simpleMessage( + "Are you sure you want to change your plan?"), + "areYouSureYouWantToExit": MessageLookupByLibrary.simpleMessage( + "Are you sure you want to exit?"), + "areYouSureYouWantToLogout": MessageLookupByLibrary.simpleMessage( + "Are you sure you want to logout?"), + "areYouSureYouWantToRenew": MessageLookupByLibrary.simpleMessage( + "Are you sure you want to renew?"), + "askCancelReason": MessageLookupByLibrary.simpleMessage( + "Your subscription was cancelled. Would you like to share the reason?"), + "askDeleteReason": MessageLookupByLibrary.simpleMessage( + "What is the main reason you are deleting your account?"), + "askYourLovedOnesToShare": MessageLookupByLibrary.simpleMessage( + "Ask your loved ones to share"), + "atAFalloutShelter": + MessageLookupByLibrary.simpleMessage("at a fallout shelter"), + "authToChangeLockscreenSetting": MessageLookupByLibrary.simpleMessage( + "Please authenticate to change lockscreen setting"), + "authToChangeYourEmail": MessageLookupByLibrary.simpleMessage( + "Please authenticate to change your email"), + "authToChangeYourPassword": MessageLookupByLibrary.simpleMessage( + "Please authenticate to change your password"), + "authToConfigureTwofactorAuthentication": + MessageLookupByLibrary.simpleMessage( + "Please authenticate to configure two-factor authentication"), + "authToInitiateAccountDeletion": MessageLookupByLibrary.simpleMessage( + "Please authenticate to initiate account deletion"), + "authToViewYourActiveSessions": MessageLookupByLibrary.simpleMessage( + "Please authenticate to view your active sessions"), + "authToViewYourHiddenFiles": MessageLookupByLibrary.simpleMessage( + "Please authenticate to view your hidden files"), + "authToViewYourMemories": MessageLookupByLibrary.simpleMessage( + "Please authenticate to view your memories"), + "authToViewYourRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Please authenticate to view your recovery key"), + "authenticating": + MessageLookupByLibrary.simpleMessage("Authenticating..."), + "authenticationFailedPleaseTryAgain": + MessageLookupByLibrary.simpleMessage( + "Authentication failed, please try again"), + "authenticationSuccessful": + MessageLookupByLibrary.simpleMessage("Authentication successful!"), + "available": MessageLookupByLibrary.simpleMessage("Available"), + "backedUpFolders": + MessageLookupByLibrary.simpleMessage("Backed up folders"), + "backup": MessageLookupByLibrary.simpleMessage("Backup"), + "backupFailed": MessageLookupByLibrary.simpleMessage("Backup failed"), + "backupOverMobileData": + MessageLookupByLibrary.simpleMessage("Backup over mobile data"), + "backupSettings": + MessageLookupByLibrary.simpleMessage("Backup settings"), + "backupVideos": MessageLookupByLibrary.simpleMessage("Backup videos"), + "blog": MessageLookupByLibrary.simpleMessage("Blog"), + "cachedData": MessageLookupByLibrary.simpleMessage("Cached data"), + "calculating": MessageLookupByLibrary.simpleMessage("Calculating..."), + "canNotUploadToAlbumsOwnedByOthers": + MessageLookupByLibrary.simpleMessage( + "Can not upload to albums owned by others"), + "canOnlyCreateLinkForFilesOwnedByYou": + MessageLookupByLibrary.simpleMessage( + "Can only create link for files owned by you"), + "canOnlyRemoveFilesOwnedByYou": MessageLookupByLibrary.simpleMessage( + "Can only remove files owned by you"), + "cancel": MessageLookupByLibrary.simpleMessage("Cancel"), + "cancelOtherSubscription": m3, + "cancelSubscription": + MessageLookupByLibrary.simpleMessage("Cancel subscription"), + "cannotAddMorePhotosAfterBecomingViewer": m4, + "centerPoint": MessageLookupByLibrary.simpleMessage("Center point"), + "changeEmail": MessageLookupByLibrary.simpleMessage("Change email"), + "changePassword": + MessageLookupByLibrary.simpleMessage("Change password"), + "changePasswordTitle": + MessageLookupByLibrary.simpleMessage("Change password"), + "changePermissions": + MessageLookupByLibrary.simpleMessage("Change permissions?"), + "checkForUpdates": + MessageLookupByLibrary.simpleMessage("Check for updates"), + "checkInboxAndSpamFolder": MessageLookupByLibrary.simpleMessage( + "Please check your inbox (and spam) to complete verification"), + "checking": MessageLookupByLibrary.simpleMessage("Checking..."), + "claimFreeStorage": + MessageLookupByLibrary.simpleMessage("Claim free storage"), + "claimMore": MessageLookupByLibrary.simpleMessage("Claim more!"), + "claimed": MessageLookupByLibrary.simpleMessage("Claimed"), + "claimedStorageSoFar": m5, + "clearCaches": MessageLookupByLibrary.simpleMessage("Clear caches"), + "click": MessageLookupByLibrary.simpleMessage("• Click"), + "clickOnTheOverflowMenu": MessageLookupByLibrary.simpleMessage( + "• Click on the overflow menu"), + "close": MessageLookupByLibrary.simpleMessage("Close"), + "clubByCaptureTime": + MessageLookupByLibrary.simpleMessage("Club by capture time"), + "codeAppliedPageTitle": + MessageLookupByLibrary.simpleMessage("Code applied"), + "codeCopiedToClipboard": + MessageLookupByLibrary.simpleMessage("Code copied to clipboard"), + "codeUsedByYou": + MessageLookupByLibrary.simpleMessage("Code used by you"), + "collabLinkSectionDescription": MessageLookupByLibrary.simpleMessage( + "Create a link to allow people to add and view photos in your shared album without needing an ente app or account. Great for collecting event photos."), + "collaborativeLink": + MessageLookupByLibrary.simpleMessage("Collaborative link"), + "collaborativeLinkCreatedFor": m6, + "collaborator": MessageLookupByLibrary.simpleMessage("Collaborator"), + "collaboratorsCanAddPhotosAndVideosToTheSharedAlbum": + MessageLookupByLibrary.simpleMessage( + "Collaborators can add photos and videos to the shared album."), + "collectEventPhotos": + MessageLookupByLibrary.simpleMessage("Collect event photos"), + "collectPhotos": MessageLookupByLibrary.simpleMessage("Collect photos"), + "confirm": MessageLookupByLibrary.simpleMessage("Confirm"), + "confirm2FADisable": MessageLookupByLibrary.simpleMessage( + "Are you sure you want to disable two-factor authentication?"), + "confirmAccountDeletion": + MessageLookupByLibrary.simpleMessage("Confirm Account Deletion"), + "confirmDeletePrompt": MessageLookupByLibrary.simpleMessage( + "Yes, I want to permanently delete this account and all its data."), + "confirmPassword": + MessageLookupByLibrary.simpleMessage("Confirm password"), + "confirmPlanChange": + MessageLookupByLibrary.simpleMessage("Confirm plan change"), + "confirmRecoveryKey": + MessageLookupByLibrary.simpleMessage("Confirm recovery key"), + "confirmYourRecoveryKey": + MessageLookupByLibrary.simpleMessage("Confirm your recovery key"), + "contactFamilyAdmin": m7, + "contactSupport": + MessageLookupByLibrary.simpleMessage("Contact support"), + "contactToManageSubscription": m8, + "continueLabel": MessageLookupByLibrary.simpleMessage("Continue"), + "continueOnFreeTrial": + MessageLookupByLibrary.simpleMessage("Continue on free trial"), + "copyEmailAddress": + MessageLookupByLibrary.simpleMessage("Copy email address"), + "copyLink": MessageLookupByLibrary.simpleMessage("Copy link"), + "copypasteThisCodentoYourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Copy-paste this code\nto your authenticator app"), + "couldNotBackUpTryLater": MessageLookupByLibrary.simpleMessage( + "We could not backup your data.\nWe will retry later."), + "couldNotFreeUpSpace": + MessageLookupByLibrary.simpleMessage("Could not free up space"), + "couldNotUpdateSubscription": MessageLookupByLibrary.simpleMessage( + "Could not update subscription"), + "count": MessageLookupByLibrary.simpleMessage("Count"), + "createAccount": MessageLookupByLibrary.simpleMessage("Create account"), + "createAlbumActionHint": MessageLookupByLibrary.simpleMessage( + "Long press to select photos and click + to create an album"), + "createNewAccount": + MessageLookupByLibrary.simpleMessage("Create new account"), + "createOrSelectAlbum": + MessageLookupByLibrary.simpleMessage("Create or select album"), + "createPublicLink": + MessageLookupByLibrary.simpleMessage("Create public link"), + "creatingLink": + MessageLookupByLibrary.simpleMessage("Creating link..."), + "criticalUpdateAvailable": + MessageLookupByLibrary.simpleMessage("Critical update available"), + "currentUsageIs": + MessageLookupByLibrary.simpleMessage("Current usage is "), + "custom": MessageLookupByLibrary.simpleMessage("Custom"), + "darkTheme": MessageLookupByLibrary.simpleMessage("Dark"), + "decrypting": MessageLookupByLibrary.simpleMessage("Decrypting..."), + "decryptingVideo": + MessageLookupByLibrary.simpleMessage("Decrypting video..."), + "deduplicateFiles": + MessageLookupByLibrary.simpleMessage("Deduplicate Files"), + "delete": MessageLookupByLibrary.simpleMessage("Delete"), + "deleteAccount": MessageLookupByLibrary.simpleMessage("Delete account"), + "deleteAccountFeedbackPrompt": MessageLookupByLibrary.simpleMessage( + "We are sorry to see you go. Please share your feedback to help us improve."), + "deleteAccountPermanentlyButton": + MessageLookupByLibrary.simpleMessage("Delete Account Permanently"), + "deleteAlbum": MessageLookupByLibrary.simpleMessage("Delete album"), + "deleteAlbumDialog": MessageLookupByLibrary.simpleMessage( + "Also delete the photos (and videos) present in this album from all other albums they are part of?"), + "deleteAlbumsDialogBody": MessageLookupByLibrary.simpleMessage( + "This will delete all empty albums. This is useful when you want to reduce the clutter in your album list."), + "deleteAll": MessageLookupByLibrary.simpleMessage("Delete All"), + "deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage( + "You are about to permanently delete your account and all its data.\nThis action is irreversible."), + "deleteEmailRequest": MessageLookupByLibrary.simpleMessage( + "Please send an email to account-deletion@ente.io from your registered email address."), + "deleteEmptyAlbums": + MessageLookupByLibrary.simpleMessage("Delete empty albums"), + "deleteEmptyAlbumsWithQuestionMark": + MessageLookupByLibrary.simpleMessage("Delete empty albums?"), + "deleteFromBoth": + MessageLookupByLibrary.simpleMessage("Delete from both"), + "deleteFromDevice": + MessageLookupByLibrary.simpleMessage("Delete from device"), + "deleteFromEnte": + MessageLookupByLibrary.simpleMessage("Delete from ente"), + "deleteLocation": + MessageLookupByLibrary.simpleMessage("Delete location"), + "deletePhotos": MessageLookupByLibrary.simpleMessage("Delete photos"), + "deleteProgress": m9, + "deleteReason1": MessageLookupByLibrary.simpleMessage( + "It’s missing a key feature that I need"), + "deleteReason2": MessageLookupByLibrary.simpleMessage( + "The app or a certain feature does not \nbehave as I think it should"), + "deleteReason3": MessageLookupByLibrary.simpleMessage( + "I found another service that I like better"), + "deleteReason4": + MessageLookupByLibrary.simpleMessage("My reason isn’t listed"), + "deleteRequestSLAText": MessageLookupByLibrary.simpleMessage( + "Your request will be processed within 72 hours."), + "deleteSharedAlbum": + MessageLookupByLibrary.simpleMessage("Delete shared album?"), + "deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage( + "The album will be deleted for everyone\n\nYou will lose access to shared photos in this album that are owned by others"), + "deselectAll": MessageLookupByLibrary.simpleMessage("Deselect all"), + "designedToOutlive": + MessageLookupByLibrary.simpleMessage("Designed to outlive"), + "details": MessageLookupByLibrary.simpleMessage("Details"), + "devAccountChanged": MessageLookupByLibrary.simpleMessage( + "The developer account we use to publish ente on App Store has changed. Because of this, you will need to login again.\n\nOur apologies for the inconvenience, but this was unavoidable."), + "deviceFilesAutoUploading": MessageLookupByLibrary.simpleMessage( + "Files added to this device album will automatically get uploaded to ente."), + "deviceLockExplanation": MessageLookupByLibrary.simpleMessage( + "Disable the device screen lock when ente is in the foreground and there is a backup in progress. This is normally not needed, but may help big uploads and initial imports of large libraries complete faster."), + "didYouKnow": MessageLookupByLibrary.simpleMessage("Did you know?"), + "disableAutoLock": + MessageLookupByLibrary.simpleMessage("Disable auto lock"), + "disableDownloadWarningBody": MessageLookupByLibrary.simpleMessage( + "Viewers can still take screenshots or save a copy of your photos using external tools"), + "disableDownloadWarningTitle": + MessageLookupByLibrary.simpleMessage("Please note"), + "disableLinkMessage": m10, + "disableTwofactor": + MessageLookupByLibrary.simpleMessage("Disable two-factor"), + "disablingTwofactorAuthentication": + MessageLookupByLibrary.simpleMessage( + "Disabling two-factor authentication..."), + "discord": MessageLookupByLibrary.simpleMessage("Discord"), + "dismiss": MessageLookupByLibrary.simpleMessage("Dismiss"), + "doThisLater": MessageLookupByLibrary.simpleMessage("Do this later"), + "done": MessageLookupByLibrary.simpleMessage("Done"), + "doubleYourStorage": + MessageLookupByLibrary.simpleMessage("Double your storage"), + "download": MessageLookupByLibrary.simpleMessage("Download"), + "downloadFailed": + MessageLookupByLibrary.simpleMessage("Download failed"), + "downloading": MessageLookupByLibrary.simpleMessage("Downloading..."), + "dropSupportEmail": m11, + "duplicateFileCountWithStorageSaved": m12, + "edit": MessageLookupByLibrary.simpleMessage("Edit"), + "eligible": MessageLookupByLibrary.simpleMessage("eligible"), + "email": MessageLookupByLibrary.simpleMessage("Email"), + "emailChangedTo": m13, + "emailNoEnteAccount": m14, + "emailYourLogs": + MessageLookupByLibrary.simpleMessage("Email your logs"), + "empty": MessageLookupByLibrary.simpleMessage("Empty"), + "emptyTrash": MessageLookupByLibrary.simpleMessage("Empty trash?"), + "encryptingBackup": + MessageLookupByLibrary.simpleMessage("Encrypting backup..."), + "encryption": MessageLookupByLibrary.simpleMessage("Encryption"), + "encryptionKeys": + MessageLookupByLibrary.simpleMessage("Encryption keys"), + "endtoendEncryptedByDefault": MessageLookupByLibrary.simpleMessage( + "End-to-end encrypted by default"), + "enteCanEncryptAndPreserveFilesOnlyIfYouGrant": + MessageLookupByLibrary.simpleMessage( + "ente can encrypt and preserve files only if you grant access to them"), + "enteSubscriptionPitch": MessageLookupByLibrary.simpleMessage( + "ente preserves your memories, so they\'re always available to you, even if you lose your device."), + "enteSubscriptionShareWithFamily": MessageLookupByLibrary.simpleMessage( + "Your family can be added to your plan as well."), + "enterAlbumName": + MessageLookupByLibrary.simpleMessage("Enter album name"), + "enterCode": MessageLookupByLibrary.simpleMessage("Enter code"), + "enterCodeDescription": MessageLookupByLibrary.simpleMessage( + "Enter the code provided by your friend to claim free storage for both of you"), + "enterEmail": MessageLookupByLibrary.simpleMessage("Enter email"), + "enterFileName": + MessageLookupByLibrary.simpleMessage("Enter file name"), + "enterNewPasswordToEncrypt": MessageLookupByLibrary.simpleMessage( + "Enter a new password we can use to encrypt your data"), + "enterPassword": MessageLookupByLibrary.simpleMessage("Enter password"), + "enterPasswordToEncrypt": MessageLookupByLibrary.simpleMessage( + "Enter a password we can use to encrypt your data"), + "enterReferralCode": + MessageLookupByLibrary.simpleMessage("Enter referral code"), + "enterThe6digitCodeFromnyourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Enter the 6-digit code from\nyour authenticator app"), + "enterValidEmail": MessageLookupByLibrary.simpleMessage( + "Please enter a valid email address."), + "enterYourEmailAddress": + MessageLookupByLibrary.simpleMessage("Enter your email address"), + "enterYourPassword": + MessageLookupByLibrary.simpleMessage("Enter your password"), + "enterYourRecoveryKey": + MessageLookupByLibrary.simpleMessage("Enter your recovery key"), + "error": MessageLookupByLibrary.simpleMessage("Error"), + "everywhere": MessageLookupByLibrary.simpleMessage("everywhere"), + "exif": MessageLookupByLibrary.simpleMessage("EXIF"), + "existingUser": MessageLookupByLibrary.simpleMessage("Existing user"), + "expiredLinkInfo": MessageLookupByLibrary.simpleMessage( + "This link has expired. Please select a new expiry time or disable link expiry."), + "exportLogs": MessageLookupByLibrary.simpleMessage("Export logs"), + "exportYourData": + MessageLookupByLibrary.simpleMessage("Export your data"), + "failedToApplyCode": + MessageLookupByLibrary.simpleMessage("Failed to apply code"), + "failedToCancel": + MessageLookupByLibrary.simpleMessage("Failed to cancel"), + "failedToFetchOriginalForEdit": MessageLookupByLibrary.simpleMessage( + "Failed to fetch original for edit"), + "failedToFetchReferralDetails": MessageLookupByLibrary.simpleMessage( + "Unable to fetch referral details. Please try again later."), + "failedToLoadAlbums": + MessageLookupByLibrary.simpleMessage("Failed to load albums"), + "failedToRenew": + MessageLookupByLibrary.simpleMessage("Failed to renew"), + "failedToVerifyPaymentStatus": MessageLookupByLibrary.simpleMessage( + "Failed to verify payment status"), + "familyPlans": MessageLookupByLibrary.simpleMessage("Family plans"), + "faq": MessageLookupByLibrary.simpleMessage("FAQ"), + "faqs": MessageLookupByLibrary.simpleMessage("FAQs"), + "favorite": MessageLookupByLibrary.simpleMessage("Favorite"), + "feedback": MessageLookupByLibrary.simpleMessage("Feedback"), + "fileSavedToGallery": + MessageLookupByLibrary.simpleMessage("File saved to gallery"), + "filesBackedUpFromDevice": m15, + "filesBackedUpInAlbum": m16, + "filesDeleted": MessageLookupByLibrary.simpleMessage("Files deleted"), + "forYourMemories": + MessageLookupByLibrary.simpleMessage("for your memories"), + "forgotPassword": + MessageLookupByLibrary.simpleMessage("Forgot password"), + "freeStorageClaimed": + MessageLookupByLibrary.simpleMessage("Free storage claimed"), + "freeStorageOnReferralSuccess": m17, + "freeStorageUsable": + MessageLookupByLibrary.simpleMessage("Free storage usable"), + "freeTrial": MessageLookupByLibrary.simpleMessage("Free trial"), + "freeTrialValidTill": m18, + "freeUpAccessPostDelete": m19, + "freeUpAmount": m20, + "freeUpDeviceSpace": + MessageLookupByLibrary.simpleMessage("Free up device space"), + "freeUpSpace": MessageLookupByLibrary.simpleMessage("Free up space"), + "freeUpSpaceSaving": m21, + "galleryMemoryLimitInfo": MessageLookupByLibrary.simpleMessage( + "Up to 1000 memories shown in gallery"), + "general": MessageLookupByLibrary.simpleMessage("General"), + "generatingEncryptionKeys": MessageLookupByLibrary.simpleMessage( + "Generating encryption keys..."), + "googlePlayId": MessageLookupByLibrary.simpleMessage("Google Play ID"), + "grantPermission": + MessageLookupByLibrary.simpleMessage("Grant permission"), + "groupNearbyPhotos": + MessageLookupByLibrary.simpleMessage("Group nearby photos"), + "hidden": MessageLookupByLibrary.simpleMessage("Hidden"), + "hide": MessageLookupByLibrary.simpleMessage("Hide"), + "howItWorks": MessageLookupByLibrary.simpleMessage("How it works"), + "howToViewShareeVerificationID": MessageLookupByLibrary.simpleMessage( + "Please ask them to long-press their email address on the settings screen, and verify that the IDs on both devices match."), + "ignoredFolderUploadReason": MessageLookupByLibrary.simpleMessage( + "Some files in this album are ignored from upload because they had previously been deleted from ente."), + "importing": MessageLookupByLibrary.simpleMessage("Importing...."), + "incorrectCode": MessageLookupByLibrary.simpleMessage("Incorrect code"), + "incorrectPasswordTitle": + MessageLookupByLibrary.simpleMessage("Incorrect password"), + "incorrectRecoveryKey": + MessageLookupByLibrary.simpleMessage("Incorrect recovery key"), + "incorrectRecoveryKeyBody": MessageLookupByLibrary.simpleMessage( + "The recovery key you entered is incorrect"), + "incorrectRecoveryKeyTitle": + MessageLookupByLibrary.simpleMessage("Incorrect recovery key"), + "insecureDevice": + MessageLookupByLibrary.simpleMessage("Insecure device"), + "installManually": + MessageLookupByLibrary.simpleMessage("Install manually"), + "invalidEmailAddress": + MessageLookupByLibrary.simpleMessage("Invalid email address"), + "invalidKey": MessageLookupByLibrary.simpleMessage("Invalid key"), + "invalidRecoveryKey": MessageLookupByLibrary.simpleMessage( + "The recovery key you entered is not valid. Please make sure it contains 24 words, and check the spelling of each.\n\nIf you entered an older recovery code, make sure it is 64 characters long, and check each of them."), + "invite": MessageLookupByLibrary.simpleMessage("Invite"), + "inviteToEnte": MessageLookupByLibrary.simpleMessage("Invite to ente"), + "inviteYourFriends": + MessageLookupByLibrary.simpleMessage("Invite your friends"), + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": + MessageLookupByLibrary.simpleMessage( + "It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team."), + "itemCount": m22, + "itemSelectedCount": m23, + "itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion": + MessageLookupByLibrary.simpleMessage( + "Items show the number of days remaining before permanent deletion"), + "itemsWillBeRemovedFromAlbum": MessageLookupByLibrary.simpleMessage( + "Selected items will be removed from this album"), + "keepPhotos": MessageLookupByLibrary.simpleMessage("Keep Photos"), + "kiloMeterUnit": MessageLookupByLibrary.simpleMessage("km"), + "kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage( + "Kindly help us with this information"), + "language": MessageLookupByLibrary.simpleMessage("Language"), + "lastUpdated": MessageLookupByLibrary.simpleMessage("Last updated"), + "leave": MessageLookupByLibrary.simpleMessage("Leave"), + "leaveAlbum": MessageLookupByLibrary.simpleMessage("Leave album"), + "leaveFamily": MessageLookupByLibrary.simpleMessage("Leave family"), + "leaveSharedAlbum": + MessageLookupByLibrary.simpleMessage("Leave shared album?"), + "lightTheme": MessageLookupByLibrary.simpleMessage("Light"), + "linkCopiedToClipboard": + MessageLookupByLibrary.simpleMessage("Link copied to clipboard"), + "linkDeviceLimit": MessageLookupByLibrary.simpleMessage("Device limit"), + "linkEnabled": MessageLookupByLibrary.simpleMessage("Enabled"), + "linkExpired": MessageLookupByLibrary.simpleMessage("Expired"), + "linkExpiresOn": m24, + "linkExpiry": MessageLookupByLibrary.simpleMessage("Link expiry"), + "linkHasExpired": + MessageLookupByLibrary.simpleMessage("Link has expired"), + "linkNeverExpires": MessageLookupByLibrary.simpleMessage("Never"), + "loadMessage1": MessageLookupByLibrary.simpleMessage( + "You can share your subscription with your family"), + "loadMessage2": MessageLookupByLibrary.simpleMessage( + "We have preserved over 10 million memories so far"), + "loadMessage3": MessageLookupByLibrary.simpleMessage( + "We keep 3 copies of your data, one in an underground fallout shelter"), + "loadMessage4": MessageLookupByLibrary.simpleMessage( + "All our apps are open source"), + "loadMessage5": MessageLookupByLibrary.simpleMessage( + "Our source code and cryptography have been externally audited"), + "loadMessage6": MessageLookupByLibrary.simpleMessage( + "You can share links to your albums with your loved ones"), + "loadMessage7": MessageLookupByLibrary.simpleMessage( + "Our mobile apps run in the background to encrypt and backup any new photos you click"), + "loadMessage8": MessageLookupByLibrary.simpleMessage( + "web.ente.io has a slick uploader"), + "loadMessage9": MessageLookupByLibrary.simpleMessage( + "We use Xchacha20Poly1305 to safely encrypt your data"), + "loadingExifData": + MessageLookupByLibrary.simpleMessage("Loading EXIF data..."), + "loadingGallery": + MessageLookupByLibrary.simpleMessage("Loading gallery..."), + "loadingMessage": + MessageLookupByLibrary.simpleMessage("Loading your photos..."), + "localGallery": MessageLookupByLibrary.simpleMessage("Local gallery"), + "location": MessageLookupByLibrary.simpleMessage("Location"), + "locationName": MessageLookupByLibrary.simpleMessage("Location name"), + "locationTagFeatureDescription": MessageLookupByLibrary.simpleMessage( + "A location tag groups all photos that were taken within some radius of a photo"), + "lockButtonLabel": MessageLookupByLibrary.simpleMessage("Lock"), + "lockScreenEnablePreSteps": MessageLookupByLibrary.simpleMessage( + "To enable lockscreen, please setup device passcode or screen lock in your system settings."), + "lockscreen": MessageLookupByLibrary.simpleMessage("Lockscreen"), + "logInLabel": MessageLookupByLibrary.simpleMessage("Log in"), + "loggingOut": MessageLookupByLibrary.simpleMessage("Logging out..."), + "loginTerms": MessageLookupByLibrary.simpleMessage( + "By clicking log in, I agree to the terms of service and privacy policy"), + "logout": MessageLookupByLibrary.simpleMessage("Logout"), + "logsDialogBody": MessageLookupByLibrary.simpleMessage( + "This will send across logs to help us debug your issue. Please note that file names will be included to help track issues with specific files."), + "longpressOnAnItemToViewInFullscreen": + MessageLookupByLibrary.simpleMessage( + "Long-press on an item to view in full-screen"), + "lostDevice": MessageLookupByLibrary.simpleMessage("Lost device?"), + "manage": MessageLookupByLibrary.simpleMessage("Manage"), + "manageDeviceStorage": + MessageLookupByLibrary.simpleMessage("Manage device storage"), + "manageFamily": MessageLookupByLibrary.simpleMessage("Manage Family"), + "manageLink": MessageLookupByLibrary.simpleMessage("Manage link"), + "manageParticipants": MessageLookupByLibrary.simpleMessage("Manage"), + "manageSubscription": + MessageLookupByLibrary.simpleMessage("Manage subscription"), + "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), + "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), + "maxDeviceLimitSpikeHandling": m25, + "memoryCount": m26, + "merchandise": MessageLookupByLibrary.simpleMessage("Merchandise"), + "mobileWebDesktop": + MessageLookupByLibrary.simpleMessage("Mobile, Web, Desktop"), + "moderateStrength": MessageLookupByLibrary.simpleMessage("Moderate"), + "monthly": MessageLookupByLibrary.simpleMessage("Monthly"), + "moveItem": m27, + "moveToAlbum": MessageLookupByLibrary.simpleMessage("Move to album"), + "movedSuccessfullyTo": m28, + "movedToTrash": MessageLookupByLibrary.simpleMessage("Moved to trash"), + "movingFilesToAlbum": + MessageLookupByLibrary.simpleMessage("Moving files to album..."), + "name": MessageLookupByLibrary.simpleMessage("Name"), + "never": MessageLookupByLibrary.simpleMessage("Never"), + "newAlbum": MessageLookupByLibrary.simpleMessage("New album"), + "newToEnte": MessageLookupByLibrary.simpleMessage("New to ente"), + "newest": MessageLookupByLibrary.simpleMessage("Newest"), + "no": MessageLookupByLibrary.simpleMessage("No"), + "noDeviceThatCanBeDeleted": MessageLookupByLibrary.simpleMessage( + "You\'ve no files on this device that can be deleted"), + "noDuplicates": MessageLookupByLibrary.simpleMessage("✨ No duplicates"), + "noExifData": MessageLookupByLibrary.simpleMessage("No EXIF data"), + "noHiddenPhotosOrVideos": + MessageLookupByLibrary.simpleMessage("No hidden photos or videos"), + "noPhotosAreBeingBackedUpRightNow": + MessageLookupByLibrary.simpleMessage( + "No photos are being backed up right now"), + "noRecoveryKey": + MessageLookupByLibrary.simpleMessage("No recovery key?"), + "noRecoveryKeyNoDecryption": MessageLookupByLibrary.simpleMessage( + "Due to the nature of our end-to-end encryption protocol, your data cannot be decrypted without your password or recovery key"), + "noResults": MessageLookupByLibrary.simpleMessage("No results"), + "noResultsFound": + MessageLookupByLibrary.simpleMessage("No results found"), + "nothingToSeeHere": + MessageLookupByLibrary.simpleMessage("Nothing to see here! 👀"), + "ok": MessageLookupByLibrary.simpleMessage("Ok"), + "oops": MessageLookupByLibrary.simpleMessage("Oops"), + "oopsSomethingWentWrong": + MessageLookupByLibrary.simpleMessage("Oops, something went wrong"), + "openTheItem": MessageLookupByLibrary.simpleMessage("• Open the item"), + "optionalAsShortAsYouLike": MessageLookupByLibrary.simpleMessage( + "Optional, as short as you like..."), + "orPickAnExistingOne": + MessageLookupByLibrary.simpleMessage("Or pick an existing one"), + "password": MessageLookupByLibrary.simpleMessage("Password"), + "passwordChangedSuccessfully": MessageLookupByLibrary.simpleMessage( + "Password changed successfully"), + "passwordLock": MessageLookupByLibrary.simpleMessage("Password lock"), + "passwordStrength": m29, + "passwordWarning": MessageLookupByLibrary.simpleMessage( + "We don\'t store this password, so if you forget, we cannot decrypt your data"), + "paymentDetails": + MessageLookupByLibrary.simpleMessage("Payment details"), + "paymentFailed": MessageLookupByLibrary.simpleMessage("Payment failed"), + "paymentFailedTalkToProvider": m30, + "paymentFailedWithReason": m31, + "pendingSync": MessageLookupByLibrary.simpleMessage("Pending sync"), + "peopleUsingYourCode": + MessageLookupByLibrary.simpleMessage("People using your code"), + "permDeleteWarning": MessageLookupByLibrary.simpleMessage( + "All items in trash will be permanently deleted\n\nThis action cannot be undone"), + "permanentlyDelete": + MessageLookupByLibrary.simpleMessage("Permanently delete"), + "permanentlyDeleteFromDevice": MessageLookupByLibrary.simpleMessage( + "Permanently delete from device?"), + "photoGridSize": + MessageLookupByLibrary.simpleMessage("Photo grid size"), + "photoSmallCase": MessageLookupByLibrary.simpleMessage("photo"), + "photosAddedByYouWillBeRemovedFromTheAlbum": + MessageLookupByLibrary.simpleMessage( + "Photos added by you will be removed from the album"), + "pickCenterPoint": + MessageLookupByLibrary.simpleMessage("Pick center point"), + "playstoreSubscription": + MessageLookupByLibrary.simpleMessage("PlayStore subscription"), + "pleaseContactSupportAndWeWillBeHappyToHelp": + MessageLookupByLibrary.simpleMessage( + "Please contact support@ente.io and we will be happy to help!"), + "pleaseContactSupportIfTheProblemPersists": + MessageLookupByLibrary.simpleMessage( + "Please contact support if the problem persists"), + "pleaseEmailUsAt": m32, + "pleaseGrantPermissions": + MessageLookupByLibrary.simpleMessage("Please grant permissions"), + "pleaseLoginAgain": + MessageLookupByLibrary.simpleMessage("Please login again"), + "pleaseSendTheLogsTo": m33, + "pleaseTryAgain": + MessageLookupByLibrary.simpleMessage("Please try again"), + "pleaseVerifyTheCodeYouHaveEntered": + MessageLookupByLibrary.simpleMessage( + "Please verify the code you have entered"), + "pleaseWait": MessageLookupByLibrary.simpleMessage("Please wait..."), + "pleaseWaitDeletingAlbum": + MessageLookupByLibrary.simpleMessage("Please wait, deleting album"), + "pleaseWaitForSometimeBeforeRetrying": + MessageLookupByLibrary.simpleMessage( + "Please wait for sometime before retrying"), + "preparingLogs": + MessageLookupByLibrary.simpleMessage("Preparing logs..."), + "preserveMore": MessageLookupByLibrary.simpleMessage("Preserve more"), + "pressAndHoldToPlayVideo": MessageLookupByLibrary.simpleMessage( + "Press and hold to play video"), + "privacy": MessageLookupByLibrary.simpleMessage("Privacy"), + "privacyPolicyTitle": + MessageLookupByLibrary.simpleMessage("Privacy Policy"), + "privateBackups": + MessageLookupByLibrary.simpleMessage("Private backups"), + "privateSharing": + MessageLookupByLibrary.simpleMessage("Private sharing"), + "publicLinkCreated": + MessageLookupByLibrary.simpleMessage("Public link created"), + "publicLinkEnabled": + MessageLookupByLibrary.simpleMessage("Public link enabled"), + "radius": MessageLookupByLibrary.simpleMessage("Radius"), + "raiseTicket": MessageLookupByLibrary.simpleMessage("Raise ticket"), + "rateTheApp": MessageLookupByLibrary.simpleMessage("Rate the app"), + "rateUs": MessageLookupByLibrary.simpleMessage("Rate us"), + "rateUsOnStore": m34, + "recover": MessageLookupByLibrary.simpleMessage("Recover"), + "recoverAccount": + MessageLookupByLibrary.simpleMessage("Recover account"), + "recoverButton": MessageLookupByLibrary.simpleMessage("Recover"), + "recoveryKey": MessageLookupByLibrary.simpleMessage("Recovery key"), + "recoveryKeyCopiedToClipboard": MessageLookupByLibrary.simpleMessage( + "Recovery key copied to clipboard"), + "recoveryKeyOnForgotPassword": MessageLookupByLibrary.simpleMessage( + "If you forget your password, the only way you can recover your data is with this key."), + "recoveryKeySaveDescription": MessageLookupByLibrary.simpleMessage( + "We don\'t store this key, please save this 24 word key in a safe place."), + "recoveryKeySuccessBody": MessageLookupByLibrary.simpleMessage( + "Great! Your recovery key is valid. Thank you for verifying.\n\nPlease remember to keep your recovery key safely backed up."), + "recoveryKeyVerified": + MessageLookupByLibrary.simpleMessage("Recovery key verified"), + "recoveryKeyVerifyReason": MessageLookupByLibrary.simpleMessage( + "Your recovery key is the only way to recover your photos if you forget your password. You can find your recovery key in Settings > Account.\n\nPlease enter your recovery key here to verify that you have saved it correctly."), + "recoverySuccessful": + MessageLookupByLibrary.simpleMessage("Recovery successful!"), + "recreatePasswordBody": MessageLookupByLibrary.simpleMessage( + "The current device is not powerful enough to verify your password, but we can regenerate in a way that works with all devices.\n\nPlease login using your recovery key and regenerate your password (you can use the same one again if you wish)."), + "recreatePasswordTitle": + MessageLookupByLibrary.simpleMessage("Recreate password"), + "reddit": MessageLookupByLibrary.simpleMessage("Reddit"), + "referFriendsAnd2xYourPlan": MessageLookupByLibrary.simpleMessage( + "Refer friends and 2x your plan"), + "referralStep1": MessageLookupByLibrary.simpleMessage( + "1. Give this code to your friends"), + "referralStep2": MessageLookupByLibrary.simpleMessage( + "2. They sign up for a paid plan"), + "referralStep3": m35, + "referrals": MessageLookupByLibrary.simpleMessage("Referrals"), + "referralsAreCurrentlyPaused": MessageLookupByLibrary.simpleMessage( + "Referrals are currently paused"), + "remindToEmptyDeviceTrash": MessageLookupByLibrary.simpleMessage( + "Also empty \"Recently Deleted\" from \"Settings\" -> \"Storage\" to claim the freed space"), + "remindToEmptyEnteTrash": MessageLookupByLibrary.simpleMessage( + "Also empty your \"Trash\" to claim the freed up space"), + "remoteImages": MessageLookupByLibrary.simpleMessage("Remote images"), + "remoteThumbnails": + MessageLookupByLibrary.simpleMessage("Remote thumbnails"), + "remoteVideos": MessageLookupByLibrary.simpleMessage("Remote videos"), + "remove": MessageLookupByLibrary.simpleMessage("Remove"), + "removeDuplicates": + MessageLookupByLibrary.simpleMessage("Remove duplicates"), + "removeFromAlbum": + MessageLookupByLibrary.simpleMessage("Remove from album"), + "removeFromAlbumTitle": + MessageLookupByLibrary.simpleMessage("Remove from album?"), + "removeFromFavorite": + MessageLookupByLibrary.simpleMessage("Remove from favorite"), + "removeLink": MessageLookupByLibrary.simpleMessage("Remove link"), + "removeParticipant": + MessageLookupByLibrary.simpleMessage("Remove participant"), + "removeParticipantBody": m36, + "removePublicLink": + MessageLookupByLibrary.simpleMessage("Remove public link"), + "removeShareItemsWarning": MessageLookupByLibrary.simpleMessage( + "Some of the items you are removing were added by other people, and you will lose access to them"), + "removeWithQuestionMark": + MessageLookupByLibrary.simpleMessage("Remove?"), + "removingFromFavorites": + MessageLookupByLibrary.simpleMessage("Removing from favorites..."), + "rename": MessageLookupByLibrary.simpleMessage("Rename"), + "renameAlbum": MessageLookupByLibrary.simpleMessage("Rename album"), + "renameFile": MessageLookupByLibrary.simpleMessage("Rename file"), + "renewSubscription": + MessageLookupByLibrary.simpleMessage("Renew subscription"), + "renewsOn": m37, + "reportABug": MessageLookupByLibrary.simpleMessage("Report a bug"), + "reportBug": MessageLookupByLibrary.simpleMessage("Report bug"), + "resendEmail": MessageLookupByLibrary.simpleMessage("Resend email"), + "resetIgnoredFiles": + MessageLookupByLibrary.simpleMessage("Reset ignored files"), + "resetPasswordTitle": + MessageLookupByLibrary.simpleMessage("Reset password"), + "restore": MessageLookupByLibrary.simpleMessage("Restore"), + "restoreToAlbum": + MessageLookupByLibrary.simpleMessage("Restore to album"), + "restoringFiles": + MessageLookupByLibrary.simpleMessage("Restoring files..."), + "retry": MessageLookupByLibrary.simpleMessage("Retry"), + "reviewDeduplicateItems": MessageLookupByLibrary.simpleMessage( + "Please review and delete the items you believe are duplicates."), + "safelyStored": MessageLookupByLibrary.simpleMessage("Safely stored"), + "save": MessageLookupByLibrary.simpleMessage("Save"), + "saveKey": MessageLookupByLibrary.simpleMessage("Save key"), + "saveYourRecoveryKeyIfYouHaventAlready": + MessageLookupByLibrary.simpleMessage( + "Save your recovery key if you haven\'t already"), + "scanCode": MessageLookupByLibrary.simpleMessage("Scan code"), + "scanThisBarcodeWithnyourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Scan this barcode with\nyour authenticator app"), + "searchByAlbumNameHint": + MessageLookupByLibrary.simpleMessage("Album name"), + "searchByExamples": MessageLookupByLibrary.simpleMessage( + "• Album names (e.g. \"Camera\")\n• Types of files (e.g. \"Videos\", \".gif\")\n• Years and months (e.g. \"2022\", \"January\")\n• Holidays (e.g. \"Christmas\")\n• Photo descriptions (e.g. “#fun”)"), + "searchHintText": MessageLookupByLibrary.simpleMessage( + "Albums, months, days, years, ..."), + "security": MessageLookupByLibrary.simpleMessage("Security"), + "selectAlbum": MessageLookupByLibrary.simpleMessage("Select album"), + "selectAll": MessageLookupByLibrary.simpleMessage("Select all"), + "selectFoldersForBackup": + MessageLookupByLibrary.simpleMessage("Select folders for backup"), + "selectLanguage": + MessageLookupByLibrary.simpleMessage("Select Language"), + "selectReason": MessageLookupByLibrary.simpleMessage("Select reason"), + "selectYourPlan": + MessageLookupByLibrary.simpleMessage("Select your plan"), + "selectedFilesAreNotOnEnte": MessageLookupByLibrary.simpleMessage( + "Selected files are not on ente"), + "selectedFoldersWillBeEncryptedAndBackedUp": + MessageLookupByLibrary.simpleMessage( + "Selected folders will be encrypted and backed up"), + "selectedItemsWillBeDeletedFromAllAlbumsAndMoved": + MessageLookupByLibrary.simpleMessage( + "Selected items will be deleted from all albums and moved to trash."), + "selectedPhotos": m38, + "selectedPhotosWithYours": m39, + "send": MessageLookupByLibrary.simpleMessage("Send"), + "sendEmail": MessageLookupByLibrary.simpleMessage("Send email"), + "sendInvite": MessageLookupByLibrary.simpleMessage("Send invite"), + "sendLink": MessageLookupByLibrary.simpleMessage("Send link"), + "sessionExpired": + MessageLookupByLibrary.simpleMessage("Session expired"), + "setAPassword": MessageLookupByLibrary.simpleMessage("Set a password"), + "setAs": MessageLookupByLibrary.simpleMessage("Set as"), + "setPasswordTitle": + MessageLookupByLibrary.simpleMessage("Set password"), + "setupComplete": MessageLookupByLibrary.simpleMessage("Setup complete"), + "share": MessageLookupByLibrary.simpleMessage("Share"), + "shareALink": MessageLookupByLibrary.simpleMessage("Share a link"), + "shareAlbumHint": MessageLookupByLibrary.simpleMessage( + "Open an album and tap the share button on the top right to share."), + "shareAnAlbumNow": + MessageLookupByLibrary.simpleMessage("Share an album now"), + "shareLink": MessageLookupByLibrary.simpleMessage("Share link"), + "shareMyVerificationID": m40, + "shareOnlyWithThePeopleYouWant": MessageLookupByLibrary.simpleMessage( + "Share only with the people you want"), + "shareTextConfirmOthersVerificationID": m41, + "shareTextRecommendUsingEnte": MessageLookupByLibrary.simpleMessage( + "Download ente so we can easily share original quality photos and videos\n\nhttps://ente.io/#download"), + "shareTextReferralCode": m42, + "shareWithNonenteUsers": + MessageLookupByLibrary.simpleMessage("Share with non-ente users"), + "shareWithPeopleSectionTitle": m43, + "shareYourFirstAlbum": + MessageLookupByLibrary.simpleMessage("Share your first album"), + "sharedAlbumSectionDescription": MessageLookupByLibrary.simpleMessage( + "Create shared and collaborative albums with other ente users, including users on free plans."), + "sharedByMe": MessageLookupByLibrary.simpleMessage("Shared by me"), + "sharedWith": m44, + "sharedWithMe": MessageLookupByLibrary.simpleMessage("Shared with me"), + "sharing": MessageLookupByLibrary.simpleMessage("Sharing..."), + "signUpTerms": MessageLookupByLibrary.simpleMessage( + "I agree to the terms of service and privacy policy"), + "singleFileDeleteFromDevice": m45, + "singleFileDeleteHighlight": MessageLookupByLibrary.simpleMessage( + "It will be deleted from all albums."), + "singleFileInBothLocalAndRemote": m46, + "singleFileInRemoteOnly": m47, + "skip": MessageLookupByLibrary.simpleMessage("Skip"), + "social": MessageLookupByLibrary.simpleMessage("Social"), + "someItemsAreInBothEnteAndYourDevice": + MessageLookupByLibrary.simpleMessage( + "Some items are in both ente and your device."), + "someOfTheFilesYouAreTryingToDeleteAre": + MessageLookupByLibrary.simpleMessage( + "Some of the files you are trying to delete are only available on your device and cannot be recovered if deleted"), + "someoneSharingAlbumsWithYouShouldSeeTheSameId": + MessageLookupByLibrary.simpleMessage( + "Someone sharing albums with you should see the same ID on their device."), + "somethingWentWrong": + MessageLookupByLibrary.simpleMessage("Something went wrong"), + "somethingWentWrongPleaseTryAgain": + MessageLookupByLibrary.simpleMessage( + "Something went wrong, please try again"), + "sorry": MessageLookupByLibrary.simpleMessage("Sorry"), + "sorryCouldNotAddToFavorites": MessageLookupByLibrary.simpleMessage( + "Sorry, could not add to favorites!"), + "sorryCouldNotRemoveFromFavorites": + MessageLookupByLibrary.simpleMessage( + "Sorry, could not remove from favorites!"), + "sorryTheCodeYouveEnteredIsIncorrect": + MessageLookupByLibrary.simpleMessage( + "Sorry, the code you\'ve entered is incorrect"), + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": + MessageLookupByLibrary.simpleMessage( + "Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device."), + "sparkleSuccess": MessageLookupByLibrary.simpleMessage("✨ Success"), + "startBackup": MessageLookupByLibrary.simpleMessage("Start backup"), + "storageInGB": m48, + "storageLimitExceeded": + MessageLookupByLibrary.simpleMessage("Storage limit exceeded"), + "strongStrength": MessageLookupByLibrary.simpleMessage("Strong"), + "subAlreadyLinkedErrMessage": m49, + "subWillBeCancelledOn": m50, + "subscribe": MessageLookupByLibrary.simpleMessage("Subscribe"), + "subscribeToEnableSharing": MessageLookupByLibrary.simpleMessage( + "Looks like your subscription has expired. Please subscribe to enable sharing."), + "subscription": MessageLookupByLibrary.simpleMessage("Subscription"), + "success": MessageLookupByLibrary.simpleMessage("Success"), + "successfullyArchived": + MessageLookupByLibrary.simpleMessage("Successfully archived"), + "successfullyUnarchived": + MessageLookupByLibrary.simpleMessage("Successfully unarchived"), + "suggestFeatures": + MessageLookupByLibrary.simpleMessage("Suggest features"), + "support": MessageLookupByLibrary.simpleMessage("Support"), + "syncProgress": m51, + "syncStopped": MessageLookupByLibrary.simpleMessage("Sync stopped"), + "syncing": MessageLookupByLibrary.simpleMessage("Syncing..."), + "systemTheme": MessageLookupByLibrary.simpleMessage("System"), + "tapToCopy": MessageLookupByLibrary.simpleMessage("tap to copy"), + "tapToEnterCode": + MessageLookupByLibrary.simpleMessage("Tap to enter code"), + "tempErrorContactSupportIfPersists": MessageLookupByLibrary.simpleMessage( + "It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team."), + "terminate": MessageLookupByLibrary.simpleMessage("Terminate"), + "terminateSession": + MessageLookupByLibrary.simpleMessage("Terminate session?"), + "terms": MessageLookupByLibrary.simpleMessage("Terms"), + "termsOfServicesTitle": MessageLookupByLibrary.simpleMessage("Terms"), + "thankYou": MessageLookupByLibrary.simpleMessage("Thank you"), + "thankYouForSubscribing": + MessageLookupByLibrary.simpleMessage("Thank you for subscribing!"), + "theDownloadCouldNotBeCompleted": MessageLookupByLibrary.simpleMessage( + "The download could not be completed"), + "theRecoveryKeyYouEnteredIsIncorrect": + MessageLookupByLibrary.simpleMessage( + "The recovery key you entered is incorrect"), + "theme": MessageLookupByLibrary.simpleMessage("Theme"), + "theseItemsWillBeDeletedFromYourDevice": + MessageLookupByLibrary.simpleMessage( + "These items will be deleted from your device."), + "theyAlsoGetXGb": m52, + "theyWillBeDeletedFromAllAlbums": MessageLookupByLibrary.simpleMessage( + "They will be deleted from all albums."), + "thisActionCannotBeUndone": MessageLookupByLibrary.simpleMessage( + "This action cannot be undone"), + "thisAlbumAlreadyHDACollaborativeLink": + MessageLookupByLibrary.simpleMessage( + "This album already has a collaborative link"), + "thisCanBeUsedToRecoverYourAccountIfYou": + MessageLookupByLibrary.simpleMessage( + "This can be used to recover your account if you lose your second factor"), + "thisDevice": MessageLookupByLibrary.simpleMessage("This device"), + "thisEmailIsAlreadyInUse": MessageLookupByLibrary.simpleMessage( + "This email is already in use"), + "thisImageHasNoExifData": + MessageLookupByLibrary.simpleMessage("This image has no exif data"), + "thisIsPersonVerificationId": m53, + "thisIsYourVerificationId": MessageLookupByLibrary.simpleMessage( + "This is your Verification ID"), + "thisWillLogYouOutOfTheFollowingDevice": + MessageLookupByLibrary.simpleMessage( + "This will log you out of the following device:"), + "thisWillLogYouOutOfThisDevice": MessageLookupByLibrary.simpleMessage( + "This will log you out of this device!"), + "time": MessageLookupByLibrary.simpleMessage("Time"), + "toHideAPhotoOrVideo": + MessageLookupByLibrary.simpleMessage("To hide a photo or video"), + "todaysLogs": MessageLookupByLibrary.simpleMessage("Today\'s logs"), + "total": MessageLookupByLibrary.simpleMessage("total"), + "totalSize": MessageLookupByLibrary.simpleMessage("Total size"), + "trash": MessageLookupByLibrary.simpleMessage("Trash"), + "tryAgain": MessageLookupByLibrary.simpleMessage("Try again"), + "turnOnBackupForAutoUpload": MessageLookupByLibrary.simpleMessage( + "Turn on backup to automatically upload files added to this device folder to ente."), + "twitter": MessageLookupByLibrary.simpleMessage("Twitter"), + "twoMonthsFreeOnYearlyPlans": MessageLookupByLibrary.simpleMessage( + "2 months free on yearly plans"), + "twofactor": MessageLookupByLibrary.simpleMessage("Two-factor"), + "twofactorAuthenticationHasBeenDisabled": + MessageLookupByLibrary.simpleMessage( + "Two-factor authentication has been disabled"), + "twofactorAuthenticationPageTitle": + MessageLookupByLibrary.simpleMessage("Two-factor authentication"), + "twofactorAuthenticationSuccessfullyReset": + MessageLookupByLibrary.simpleMessage( + "Two-factor authentication successfully reset"), + "twofactorSetup": + MessageLookupByLibrary.simpleMessage("Two-factor setup"), + "unarchive": MessageLookupByLibrary.simpleMessage("Unarchive"), + "unarchiveAlbum": + MessageLookupByLibrary.simpleMessage("Unarchive album"), + "unarchiving": MessageLookupByLibrary.simpleMessage("Unarchiving..."), + "uncategorized": MessageLookupByLibrary.simpleMessage("Uncategorized"), + "unhide": MessageLookupByLibrary.simpleMessage("Unhide"), + "unhideToAlbum": + MessageLookupByLibrary.simpleMessage("Unhide to album"), + "unhidingFilesToAlbum": + MessageLookupByLibrary.simpleMessage("Unhiding files to album"), + "unlock": MessageLookupByLibrary.simpleMessage("Unlock"), + "unselectAll": MessageLookupByLibrary.simpleMessage("Unselect all"), + "update": MessageLookupByLibrary.simpleMessage("Update"), + "updateAvailable": + MessageLookupByLibrary.simpleMessage("Update available"), + "updatingFolderSelection": MessageLookupByLibrary.simpleMessage( + "Updating folder selection..."), + "upgrade": MessageLookupByLibrary.simpleMessage("Upgrade"), + "uploadingFilesToAlbum": + MessageLookupByLibrary.simpleMessage("Uploading files to album..."), + "usableReferralStorageInfo": MessageLookupByLibrary.simpleMessage( + "Usable storage is limited by your current plan. Excess claimed storage will automatically become usable when you upgrade your plan."), + "usePublicLinksForPeopleNotOnEnte": + MessageLookupByLibrary.simpleMessage( + "Use public links for people not on ente"), + "useRecoveryKey": + MessageLookupByLibrary.simpleMessage("Use recovery key"), + "useSelectedPhoto": + MessageLookupByLibrary.simpleMessage("Use selected photo"), + "verificationFailedPleaseTryAgain": + MessageLookupByLibrary.simpleMessage( + "Verification failed, please try again"), + "verificationId": + MessageLookupByLibrary.simpleMessage("Verification ID"), + "verify": MessageLookupByLibrary.simpleMessage("Verify"), + "verifyEmail": MessageLookupByLibrary.simpleMessage("Verify email"), + "verifyEmailID": m54, + "verifyPassword": + MessageLookupByLibrary.simpleMessage("Verify password"), + "verifying": MessageLookupByLibrary.simpleMessage("Verifying..."), + "verifyingRecoveryKey": + MessageLookupByLibrary.simpleMessage("Verifying recovery key..."), + "videoSmallCase": MessageLookupByLibrary.simpleMessage("video"), + "viewActiveSessions": + MessageLookupByLibrary.simpleMessage("View active sessions"), + "viewAllExifData": + MessageLookupByLibrary.simpleMessage("View all EXIF data"), + "viewLogs": MessageLookupByLibrary.simpleMessage("View logs"), + "viewRecoveryKey": + MessageLookupByLibrary.simpleMessage("View recovery key"), + "viewer": MessageLookupByLibrary.simpleMessage("Viewer"), + "visitWebToManage": MessageLookupByLibrary.simpleMessage( + "Please visit web.ente.io to manage your subscription"), + "weAreOpenSource": + MessageLookupByLibrary.simpleMessage("We are open source!"), + "weDontSupportEditingPhotosAndAlbumsThatYouDont": + MessageLookupByLibrary.simpleMessage( + "We don\'t support editing photos and albums that you don\'t own yet"), + "weakStrength": MessageLookupByLibrary.simpleMessage("Weak"), + "welcomeBack": MessageLookupByLibrary.simpleMessage("Welcome back!"), + "weveSentAMailTo": + MessageLookupByLibrary.simpleMessage("We\'ve sent a mail to"), + "yearly": MessageLookupByLibrary.simpleMessage("Yearly"), + "yearsAgo": m55, + "yes": MessageLookupByLibrary.simpleMessage("Yes"), + "yesCancel": MessageLookupByLibrary.simpleMessage("Yes, cancel"), + "yesConvertToViewer": + MessageLookupByLibrary.simpleMessage("Yes, convert to viewer"), + "yesDelete": MessageLookupByLibrary.simpleMessage("Yes, delete"), + "yesLogout": MessageLookupByLibrary.simpleMessage("Yes, logout"), + "yesRemove": MessageLookupByLibrary.simpleMessage("Yes, remove"), + "yesRenew": MessageLookupByLibrary.simpleMessage("Yes, Renew"), + "you": MessageLookupByLibrary.simpleMessage("You"), + "youAreOnAFamilyPlan": + MessageLookupByLibrary.simpleMessage("You are on a family plan!"), + "youAreOnTheLatestVersion": MessageLookupByLibrary.simpleMessage( + "You are on the latest version"), + "youCanAtMaxDoubleYourStorage": MessageLookupByLibrary.simpleMessage( + "* You can at max double your storage"), + "youCanManageYourLinksInTheShareTab": + MessageLookupByLibrary.simpleMessage( + "You can manage your links in the share tab."), + "youCanTrySearchingForADifferentQuery": + MessageLookupByLibrary.simpleMessage( + "You can try searching for a different query."), + "youCannotDowngradeToThisPlan": MessageLookupByLibrary.simpleMessage( + "You cannot downgrade to this plan"), + "youCannotShareWithYourself": MessageLookupByLibrary.simpleMessage( + "You cannot share with yourself"), + "youDontHaveAnyArchivedItems": MessageLookupByLibrary.simpleMessage( + "You don\'t have any archived items."), + "youHaveSuccessfullyFreedUp": m56, + "yourAccountHasBeenDeleted": MessageLookupByLibrary.simpleMessage( + "Your account has been deleted"), + "yourPlanWasSuccessfullyDowngraded": + MessageLookupByLibrary.simpleMessage( + "Your plan was successfully downgraded"), + "yourPlanWasSuccessfullyUpgraded": MessageLookupByLibrary.simpleMessage( + "Your plan was successfully upgraded"), + "yourPurchaseWasSuccessful": MessageLookupByLibrary.simpleMessage( + "Your purchase was successful"), + "yourStorageDetailsCouldNotBeFetched": + MessageLookupByLibrary.simpleMessage( + "Your storage details could not be fetched"), + "yourSubscriptionHasExpired": MessageLookupByLibrary.simpleMessage( + "Your subscription has expired"), + "yourSubscriptionWasUpdatedSuccessfully": + MessageLookupByLibrary.simpleMessage( + "Your subscription was updated successfully"), + "yourVerificationCodeHasExpired": MessageLookupByLibrary.simpleMessage( + "Your verification code has expired"), + "youveNoDuplicateFilesThatCanBeCleared": + MessageLookupByLibrary.simpleMessage( + "You\'ve no duplicate files that can be cleared"), + "youveNoFilesInThisAlbumThatCanBeDeleted": + MessageLookupByLibrary.simpleMessage( + "You\'ve no files in this album that can be deleted") + }; +} diff --git a/lib/generated/intl/messages_es.dart b/lib/generated/intl/messages_es.dart new file mode 100644 index 000000000..2e78796cb --- /dev/null +++ b/lib/generated/intl/messages_es.dart @@ -0,0 +1,25 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a es locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'es'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => {}; +} diff --git a/lib/generated/intl/messages_fr.dart b/lib/generated/intl/messages_fr.dart new file mode 100644 index 000000000..92f453b4d --- /dev/null +++ b/lib/generated/intl/messages_fr.dart @@ -0,0 +1,1056 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a fr locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'fr'; + + static String m0(count) => + "${Intl.plural(count, one: 'Ajoutez un objet', other: 'Ajoutez des objets')}"; + + static String m2(albumName) => "Ajouté avec succès à ${albumName}"; + + static String m3(paymentProvider) => + "Veuillez d\'abord annuler votre abonnement existant de ${paymentProvider}"; + + static String m4(user) => + "${user} ne pourra pas ajouter plus de photos à cet album\n\nIl pourrait toujours supprimer les photos existantes ajoutées par eux"; + + static String m5(isFamilyMember, storageAmountInGb) => + "${Intl.select(isFamilyMember, { + 'true': + 'Votre famille a demandé ${storageAmountInGb} Gb jusqu\'à présent', + 'false': + 'Vous avez réclamé ${storageAmountInGb} Gb jusqu\'à présent', + 'other': + 'Vous avez réclamé ${storageAmountInGb} Gbjusqu\'à présent!', + })}"; + + static String m6(albumName) => "Lien collaboratif créé pour ${albumName}"; + + static String m8(provider) => + "Veuillez nous contacter à support@ente.io pour gérer votre abonnement ${provider}."; + + static String m9(currentlyDeleting, totalCount) => + "Suppression de ${currentlyDeleting} / ${totalCount}"; + + static String m10(albumName) => + "Cela supprimera le lien public pour accéder à \"${albumName}\"."; + + static String m11(supportEmail) => + "Veuillez envoyer un e-mail à ${supportEmail} depuis votre adresse enregistrée"; + + static String m12(count, storageSaved) => + "Vous avez nettoyé ${Intl.plural(count, one: '${count} fichier dupliqué', other: '${count} fichiers dupliqués')}, sauvegarde (${storageSaved}!)"; + + static String m13(newEmail) => "L\'e-mail a été changé en ${newEmail}"; + + static String m14(email) => + "${email} n\'a pas de compte ente.\n\nEnvoyez une invitation pour partager des photos."; + + static String m17(storageAmountInGB) => + "${storageAmountInGB} Go chaque fois que quelqu\'un s\'inscrit à une offre payante et applique votre code"; + + static String m22(count) => + "${Intl.plural(count, one: '${count} objet', other: '${count} objets')}"; + + static String m23(count) => "${count} sélectionné"; + + static String m24(expiryTime) => "Le lien expirera le ${expiryTime}"; + + static String m25(maxValue) => + "Lorsqu\'elle est définie au maximum (${maxValue}), la limite de l\'appareil sera assouplie pour permettre des pointes temporaires d\'un grand nombre de téléspectateurs."; + + static String m26(count) => + "${Intl.plural(count, one: '${count} mémoire', other: '${count} souvenirs')}"; + + static String m27(count) => + "${Intl.plural(count, one: 'Déplacez l\'objet', other: 'Déplacez des objets')}"; + + static String m28(albumName) => "Déplacé avec succès vers ${albumName}"; + + static String m29(passwordStrengthValue) => + "Puissance du mot de passe : ${passwordStrengthValue}"; + + static String m32(toEmail) => "Merci de nous envoyer un e-mail à ${toEmail}"; + + static String m33(toEmail) => "Envoyez les logs à ${toEmail}"; + + static String m34(storeName) => "Notez-nous sur ${storeName}"; + + static String m35(storageInGB) => + "3. Vous recevez tous les deux ${storageInGB} GB* gratuits"; + + static String m36(userEmail) => + "${userEmail} sera retiré de cet album partagé\n\nToutes les photos ajoutées par eux seront également retirées de l\'album"; + + static String m37(endDate) => "Renouvellement le ${endDate}"; + + static String m38(count) => "${count} sélectionné"; + + static String m39(count, yourCount) => + "${count} sélectionné (${yourCount} votre)"; + + static String m40(verificationID) => + "Voici mon ID de vérification : ${verificationID} pour ente.io."; + + static String m41(verificationID) => + "Hé, pouvez-vous confirmer qu\'il s\'agit de votre ID de vérification ente.io : ${verificationID}"; + + static String m42(referralCode, referralStorageInGB) => + "code de parrainage ente : ${referralCode} \n\nAppliquez le dans Paramètres → Général → Références pour obtenir ${referralStorageInGB} Go gratuitement après votre inscription à un plan payant\n\nhttps://ente.io"; + + static String m43(numberOfPeople) => + "${Intl.plural(numberOfPeople, zero: 'Partagez avec des personnes spécifiques', one: 'Partagé avec 1 personne', other: 'Partagé avec ${numberOfPeople} des gens')}"; + + static String m44(emailIDs) => "Partagé avec ${emailIDs}"; + + static String m45(fileType) => + "Ce ${fileType} sera supprimé de votre appareil."; + + static String m46(fileType) => + "Ce ${fileType} est à la fois dans ente et votre appareil."; + + static String m47(fileType) => "Ce ${fileType} sera supprimé de ente."; + + static String m48(storageAmountInGB) => "${storageAmountInGB} Go"; + + static String m50(endDate) => "Votre abonnement sera annulé le ${endDate}"; + + static String m51(completed, total) => + "${completed}/${total} souvenirs préservés"; + + static String m52(storageAmountInGB) => + "Ils obtiennent aussi ${storageAmountInGB} Go"; + + static String m53(email) => "Ceci est l\'ID de vérification de ${email}"; + + static String m54(email) => "Vérifier ${email}"; + + static String m55(count) => + "${Intl.plural(count, one: '${count} il y a un an', other: '${count} il y a des années')}"; + + static String m56(storageSaved) => + "Vous avez libéré ${storageSaved} avec succès !"; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "aNewVersionOfEnteIsAvailable": MessageLookupByLibrary.simpleMessage( + "Une nouvelle version de Wire est disponible."), + "about": MessageLookupByLibrary.simpleMessage("À propos"), + "account": MessageLookupByLibrary.simpleMessage("Compte"), + "accountWelcomeBack": + MessageLookupByLibrary.simpleMessage("Bienvenue !"), + "ackPasswordLostWarning": MessageLookupByLibrary.simpleMessage( + "Je comprends que si je perds mon mot de passe, je risque de perdre mes données puisque mes données sont chiffrées de bout en bout."), + "activeSessions": + MessageLookupByLibrary.simpleMessage("Sessions actives"), + "addANewEmail": + MessageLookupByLibrary.simpleMessage("Ajouter un nouvel email"), + "addCollaborator": + MessageLookupByLibrary.simpleMessage("Ajouter un collaborateur"), + "addItem": m0, + "addMore": MessageLookupByLibrary.simpleMessage("Ajouter Plus"), + "addToAlbum": + MessageLookupByLibrary.simpleMessage("Ajouter à l\'album"), + "addToEnte": MessageLookupByLibrary.simpleMessage("Ajouter à ente"), + "addViewer": + MessageLookupByLibrary.simpleMessage("Ajouter un observateur"), + "addedAs": MessageLookupByLibrary.simpleMessage("Ajouté comme"), + "addedSuccessfullyTo": m2, + "addingToFavorites": + MessageLookupByLibrary.simpleMessage("Ajout aux favoris..."), + "advanced": MessageLookupByLibrary.simpleMessage("Avancé"), + "advancedSettings": MessageLookupByLibrary.simpleMessage("Avancé"), + "after1Day": MessageLookupByLibrary.simpleMessage("Après 1 jour"), + "after1Hour": MessageLookupByLibrary.simpleMessage("Après 1 heure"), + "after1Month": MessageLookupByLibrary.simpleMessage("Après 1 mois"), + "after1Week": MessageLookupByLibrary.simpleMessage("Après 1 semaine"), + "after1Year": MessageLookupByLibrary.simpleMessage("Après 1 an"), + "albumOwner": MessageLookupByLibrary.simpleMessage("Propriétaire"), + "albumTitle": MessageLookupByLibrary.simpleMessage("Titre de l\'album"), + "albumUpdated": + MessageLookupByLibrary.simpleMessage("Album mis à jour"), + "albums": MessageLookupByLibrary.simpleMessage("Albums"), + "allClear": MessageLookupByLibrary.simpleMessage("✨ Tout est effacé"), + "allMemoriesPreserved": MessageLookupByLibrary.simpleMessage( + "Tous les souvenirs conservés"), + "allowAddPhotosDescription": MessageLookupByLibrary.simpleMessage( + "Autoriser les personnes avec le lien à ajouter des photos à l\'album partagé."), + "allowAddingPhotos": MessageLookupByLibrary.simpleMessage( + "Autoriser l\'ajout de photos"), + "allowDownloads": MessageLookupByLibrary.simpleMessage( + "Autoriser les téléchargements"), + "allowPeopleToAddPhotos": MessageLookupByLibrary.simpleMessage( + "Autoriser les personnes à ajouter des photos"), + "appleId": MessageLookupByLibrary.simpleMessage("Apple ID"), + "apply": MessageLookupByLibrary.simpleMessage("Appliquer"), + "applyCodeTitle": + MessageLookupByLibrary.simpleMessage("Utiliser le code"), + "archive": MessageLookupByLibrary.simpleMessage("Archiver"), + "archiving": + MessageLookupByLibrary.simpleMessage("Archivage en cours..."), + "areYouSureYouWantToCancel": MessageLookupByLibrary.simpleMessage( + "Es-tu sûre de vouloir annuler?"), + "areYouSureYouWantToChangeYourPlan": + MessageLookupByLibrary.simpleMessage( + "Êtes-vous certains de vouloir changer d\'offre ?"), + "areYouSureYouWantToLogout": MessageLookupByLibrary.simpleMessage( + "Voulez-vous vraiment vous déconnecter ?"), + "areYouSureYouWantToRenew": MessageLookupByLibrary.simpleMessage( + "Êtes-vous sûr de vouloir renouveler ?"), + "askCancelReason": MessageLookupByLibrary.simpleMessage( + "Votre abonnement a été annulé. Souhaitez-vous partager la raison ?"), + "askDeleteReason": MessageLookupByLibrary.simpleMessage( + "Quelle est la principale raison pour laquelle vous supprimez votre compte ?"), + "askYourLovedOnesToShare": MessageLookupByLibrary.simpleMessage( + "Demandez à vos proches de partager"), + "authToChangeLockscreenSetting": MessageLookupByLibrary.simpleMessage( + "Veuillez vous authentifier pour modifier les paramètres de l\'écran de verrouillage"), + "authToChangeYourEmail": MessageLookupByLibrary.simpleMessage( + "Veuillez vous authentifier pour modifier votre adresse e-mail"), + "authToChangeYourPassword": MessageLookupByLibrary.simpleMessage( + "Veuillez vous authentifier pour modifier votre mot de passe"), + "authToConfigureTwofactorAuthentication": + MessageLookupByLibrary.simpleMessage( + "Veuillez vous authentifier pour configurer l\'authentification à deux facteurs"), + "authToInitiateAccountDeletion": MessageLookupByLibrary.simpleMessage( + "Veuillez vous authentifier pour débuter la suppression du compte"), + "authToViewYourActiveSessions": MessageLookupByLibrary.simpleMessage( + "Veuillez vous authentifier pour voir vos sessions actives"), + "authToViewYourHiddenFiles": MessageLookupByLibrary.simpleMessage( + "Veuillez vous authentifier pour voir vos fichiers cachés"), + "authToViewYourRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Veuillez vous authentifier pour afficher votre clé de récupération"), + "backedUpFolders": + MessageLookupByLibrary.simpleMessage("Dossiers sauvegardés"), + "backup": MessageLookupByLibrary.simpleMessage("Sauvegarde"), + "backupOverMobileData": MessageLookupByLibrary.simpleMessage( + "Sauvegarde sur données mobiles"), + "backupSettings": + MessageLookupByLibrary.simpleMessage("Paramètres de la sauvegarde"), + "backupVideos": + MessageLookupByLibrary.simpleMessage("Sauvegarde des vidéos"), + "blog": MessageLookupByLibrary.simpleMessage("Blog"), + "cachedData": + MessageLookupByLibrary.simpleMessage("Données mises en cache"), + "canNotUploadToAlbumsOwnedByOthers": MessageLookupByLibrary.simpleMessage( + "Impossible de télécharger dans les albums appartenant à d\'autres personnes"), + "canOnlyCreateLinkForFilesOwnedByYou": + MessageLookupByLibrary.simpleMessage( + "Ne peut créer de lien que pour les fichiers que vous possédez"), + "canOnlyRemoveFilesOwnedByYou": MessageLookupByLibrary.simpleMessage( + "Vous ne pouvez supprimer que les fichiers que vous possédez"), + "cancel": MessageLookupByLibrary.simpleMessage("Annuler"), + "cancelOtherSubscription": m3, + "cancelSubscription": + MessageLookupByLibrary.simpleMessage("Annuler l\'abonnement"), + "cannotAddMorePhotosAfterBecomingViewer": m4, + "changeEmail": + MessageLookupByLibrary.simpleMessage("Modifier l\'e-mail"), + "changePassword": + MessageLookupByLibrary.simpleMessage("Modifier le mot de passe"), + "changePasswordTitle": + MessageLookupByLibrary.simpleMessage("Modifier le mot de passe"), + "changePermissions": + MessageLookupByLibrary.simpleMessage("Modifier les permissions ?"), + "checkForUpdates": + MessageLookupByLibrary.simpleMessage("Vérifier les mises à jour"), + "checkInboxAndSpamFolder": MessageLookupByLibrary.simpleMessage( + "Veuillez consulter votre boîte de courriels (et les indésirables) pour compléter la vérification"), + "checking": MessageLookupByLibrary.simpleMessage("Vérification..."), + "claimFreeStorage": MessageLookupByLibrary.simpleMessage( + "Réclamer le stockage gratuit"), + "claimMore": MessageLookupByLibrary.simpleMessage("Réclamez plus !"), + "claimed": MessageLookupByLibrary.simpleMessage("Réclamée"), + "claimedStorageSoFar": m5, + "clearCaches": + MessageLookupByLibrary.simpleMessage("Nettoyer le cache"), + "close": MessageLookupByLibrary.simpleMessage("Fermer"), + "clubByCaptureTime": + MessageLookupByLibrary.simpleMessage("Durée du Club par capture"), + "codeAppliedPageTitle": + MessageLookupByLibrary.simpleMessage("Code appliqué"), + "codeCopiedToClipboard": MessageLookupByLibrary.simpleMessage( + "Code copié dans le presse-papiers"), + "codeUsedByYou": + MessageLookupByLibrary.simpleMessage("Code utilisé par vous"), + "collabLinkSectionDescription": MessageLookupByLibrary.simpleMessage( + "Créez un lien pour permettre aux gens d\'ajouter et de voir des photos dans votre album partagé sans avoir besoin d\'une application ente ou d\'un compte. Idéal pour collecter des photos d\'événement."), + "collaborativeLink": + MessageLookupByLibrary.simpleMessage("Lien collaboratif"), + "collaborativeLinkCreatedFor": m6, + "collaborator": MessageLookupByLibrary.simpleMessage("Collaborateur"), + "collaboratorsCanAddPhotosAndVideosToTheSharedAlbum": + MessageLookupByLibrary.simpleMessage( + "Les collaborateurs peuvent ajouter des photos et des vidéos à l\'album partagé."), + "collectEventPhotos": MessageLookupByLibrary.simpleMessage( + "Collecter des photos de l\'événement"), + "collectPhotos": + MessageLookupByLibrary.simpleMessage("Récupérer les photos"), + "confirm": MessageLookupByLibrary.simpleMessage("Confirmer"), + "confirm2FADisable": MessageLookupByLibrary.simpleMessage( + "Voulez-vous vraiment désactiver l\'authentification à deux facteurs ?"), + "confirmAccountDeletion": MessageLookupByLibrary.simpleMessage( + "Confirmer la suppression du compte"), + "confirmDeletePrompt": MessageLookupByLibrary.simpleMessage( + "Oui, je veux supprimer définitivement ce compte et toutes ses données."), + "confirmPassword": + MessageLookupByLibrary.simpleMessage("Confirmer le mot de passe"), + "confirmPlanChange": MessageLookupByLibrary.simpleMessage( + "Confirmer le changement de l\'offre"), + "confirmRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Confirmer la clé de récupération"), + "confirmYourRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Confirmer la clé de récupération"), + "contactSupport": + MessageLookupByLibrary.simpleMessage("Contacter l\'assistance"), + "contactToManageSubscription": m8, + "continueLabel": MessageLookupByLibrary.simpleMessage("Continuer"), + "copyEmailAddress": + MessageLookupByLibrary.simpleMessage("Copier l’adresse e-mail"), + "copyLink": MessageLookupByLibrary.simpleMessage("Copier le lien"), + "copypasteThisCodentoYourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Copiez-collez ce code\ndans votre application d\'authentification"), + "couldNotFreeUpSpace": MessageLookupByLibrary.simpleMessage( + "Impossible de libérer de l\'espace"), + "createAccount": + MessageLookupByLibrary.simpleMessage("Créer un compte"), + "createAlbumActionHint": MessageLookupByLibrary.simpleMessage( + "Appuyez longuement pour sélectionner des photos et cliquez sur + pour créer un album"), + "createNewAccount": + MessageLookupByLibrary.simpleMessage("Créer un nouveau compte"), + "createOrSelectAlbum": MessageLookupByLibrary.simpleMessage( + "Créer ou sélectionner un album"), + "createPublicLink": + MessageLookupByLibrary.simpleMessage("Créer un lien public"), + "creatingLink": + MessageLookupByLibrary.simpleMessage("Création du lien..."), + "criticalUpdateAvailable": MessageLookupByLibrary.simpleMessage( + "Mise à jour critique disponible"), + "currentUsageIs": MessageLookupByLibrary.simpleMessage( + "L\'utilisation actuelle est "), + "custom": MessageLookupByLibrary.simpleMessage("Personnaliser"), + "darkTheme": MessageLookupByLibrary.simpleMessage("Sombre"), + "decrypting": MessageLookupByLibrary.simpleMessage("Déchiffrage..."), + "deduplicateFiles": + MessageLookupByLibrary.simpleMessage("Déduplication de fichiers"), + "delete": MessageLookupByLibrary.simpleMessage("Supprimer"), + "deleteAccount": + MessageLookupByLibrary.simpleMessage("Supprimer mon compte"), + "deleteAccountFeedbackPrompt": MessageLookupByLibrary.simpleMessage( + "Nous sommes désolés de vous voir partir. Veuillez partager vos commentaires pour nous aider à nous améliorer."), + "deleteAccountPermanentlyButton": MessageLookupByLibrary.simpleMessage( + "Supprimer définitivement le compte"), + "deleteAlbum": + MessageLookupByLibrary.simpleMessage("Supprimer l\'album"), + "deleteAlbumDialog": MessageLookupByLibrary.simpleMessage( + "Supprimer aussi les photos (et vidéos) présentes dans cet album depuis tous les autres albums dont ils font partie ?"), + "deleteAlbumsDialogBody": MessageLookupByLibrary.simpleMessage( + "Ceci supprimera tous les albums vides. Ceci est utile lorsque vous voulez réduire l\'encombrement dans votre liste d\'albums."), + "deleteAll": MessageLookupByLibrary.simpleMessage("Tout Supprimer"), + "deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage( + "Vous allez supprimer définitivement votre compte et toutes ses données.\nCette action est irréversible."), + "deleteEmailRequest": MessageLookupByLibrary.simpleMessage( + "Veuillez envoyer un e-mail à account-deletion@ente.io à partir de votre adresse e-mail enregistrée."), + "deleteEmptyAlbums": + MessageLookupByLibrary.simpleMessage("Supprimer les albums vides"), + "deleteEmptyAlbumsWithQuestionMark": + MessageLookupByLibrary.simpleMessage( + "Supprimer les albums vides ?"), + "deleteFromBoth": + MessageLookupByLibrary.simpleMessage("Supprimer des deux"), + "deleteFromDevice": + MessageLookupByLibrary.simpleMessage("Supprimer de l\'appareil"), + "deleteFromEnte": + MessageLookupByLibrary.simpleMessage("Supprimer de ente"), + "deletePhotos": + MessageLookupByLibrary.simpleMessage("Supprimer des photos"), + "deleteProgress": m9, + "deleteReason1": MessageLookupByLibrary.simpleMessage( + "Il manque une fonction clé dont j\'ai besoin"), + "deleteReason2": MessageLookupByLibrary.simpleMessage( + "L\'application ou une certaine fonctionnalité ne se comporte pas \ncomme je pense qu\'elle devrait"), + "deleteReason3": MessageLookupByLibrary.simpleMessage( + "J\'ai trouvé un autre service que je préfère"), + "deleteReason4": + MessageLookupByLibrary.simpleMessage("Ma raison n\'est pas listée"), + "deleteRequestSLAText": MessageLookupByLibrary.simpleMessage( + "Votre demande sera traitée en moins de 72 heures."), + "deleteSharedAlbum": MessageLookupByLibrary.simpleMessage( + "Supprimer l\'album partagé ?"), + "deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage( + "L\'album sera supprimé pour tout le monde\n\nVous perdrez l\'accès aux photos partagées dans cet album qui est détenues par d\'autres personnes"), + "deselectAll": + MessageLookupByLibrary.simpleMessage("Tout déselectionner"), + "details": MessageLookupByLibrary.simpleMessage("Détails"), + "deviceLockExplanation": MessageLookupByLibrary.simpleMessage( + "Désactiver le verrouillage de l\'écran de l\'appareil lorsque ente est au premier plan et il y a une sauvegarde en cours. Ce n\'est normalement pas nécessaire, mais peut aider les gros téléchargements et les premières importations de grandes bibliothèques plus rapidement."), + "didYouKnow": MessageLookupByLibrary.simpleMessage("Le savais-tu ?"), + "disableAutoLock": MessageLookupByLibrary.simpleMessage( + "Désactiver le verrouillage automatique"), + "disableDownloadWarningBody": MessageLookupByLibrary.simpleMessage( + "Les téléspectateurs peuvent toujours prendre des captures d\'écran ou enregistrer une copie de vos photos en utilisant des outils externes"), + "disableDownloadWarningTitle": + MessageLookupByLibrary.simpleMessage("Veuillez remarquer"), + "disableLinkMessage": m10, + "disableTwofactor": MessageLookupByLibrary.simpleMessage( + "Désactiver la double-authentification"), + "disablingTwofactorAuthentication": + MessageLookupByLibrary.simpleMessage( + "Désactiver la double-authentification..."), + "discord": MessageLookupByLibrary.simpleMessage("Discord"), + "dismiss": MessageLookupByLibrary.simpleMessage("Rejeter"), + "doThisLater": MessageLookupByLibrary.simpleMessage("Plus tard"), + "done": MessageLookupByLibrary.simpleMessage("Terminé"), + "doubleYourStorage": MessageLookupByLibrary.simpleMessage( + "Doubler votre espace de stockage"), + "download": MessageLookupByLibrary.simpleMessage("Télécharger"), + "downloadFailed": + MessageLookupByLibrary.simpleMessage("Échec du téléchargement"), + "downloading": + MessageLookupByLibrary.simpleMessage("Téléchargement en cours..."), + "dropSupportEmail": m11, + "duplicateFileCountWithStorageSaved": m12, + "eligible": MessageLookupByLibrary.simpleMessage("éligible"), + "email": MessageLookupByLibrary.simpleMessage("E-mail"), + "emailChangedTo": m13, + "emailNoEnteAccount": m14, + "emailYourLogs": + MessageLookupByLibrary.simpleMessage("Envoyez vos logs par e-mail"), + "empty": MessageLookupByLibrary.simpleMessage("Vide"), + "emptyTrash": + MessageLookupByLibrary.simpleMessage("Vider la corbeille ?"), + "encryptingBackup": MessageLookupByLibrary.simpleMessage( + "Chiffrement de la sauvegarde..."), + "encryption": MessageLookupByLibrary.simpleMessage("Chiffrement"), + "encryptionKeys": + MessageLookupByLibrary.simpleMessage("Clés de chiffrement"), + "enteSubscriptionPitch": MessageLookupByLibrary.simpleMessage( + "ente conserve vos souvenirs, donc ils sont toujours disponibles pour vous, même si vous perdez votre appareil."), + "enteSubscriptionShareWithFamily": MessageLookupByLibrary.simpleMessage( + "Vous pouvez également ajouter votre famille à votre forfait."), + "enterAlbumName": + MessageLookupByLibrary.simpleMessage("Saisir un nom d\'album"), + "enterCode": MessageLookupByLibrary.simpleMessage("Entrer le code"), + "enterCodeDescription": MessageLookupByLibrary.simpleMessage( + "Entrez le code fourni par votre ami pour réclamer de l\'espace de stockage gratuit pour vous deux"), + "enterEmail": MessageLookupByLibrary.simpleMessage("Entrer e-mail"), + "enterFileName": + MessageLookupByLibrary.simpleMessage("Entrez le nom du fichier"), + "enterNewPasswordToEncrypt": MessageLookupByLibrary.simpleMessage( + "Entrez un nouveau mot de passe que nous pouvons utiliser pour chiffrer vos données"), + "enterPassword": + MessageLookupByLibrary.simpleMessage("Saisissez le mot de passe"), + "enterPasswordToEncrypt": MessageLookupByLibrary.simpleMessage( + "Entrez un mot de passe que nous pouvons utiliser pour chiffrer vos données"), + "enterReferralCode": MessageLookupByLibrary.simpleMessage( + "Entrez le code de parrainage"), + "enterThe6digitCodeFromnyourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Entrez le code à 6 chiffres de\nvotre application d\'authentification"), + "enterValidEmail": MessageLookupByLibrary.simpleMessage( + "Veuillez entrer une adresse email valide."), + "enterYourEmailAddress": + MessageLookupByLibrary.simpleMessage("Entrez votre adresse e-mail"), + "enterYourPassword": + MessageLookupByLibrary.simpleMessage("Entrez votre mot de passe"), + "enterYourRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Entrez votre clé de récupération"), + "error": MessageLookupByLibrary.simpleMessage("Erreur"), + "exif": MessageLookupByLibrary.simpleMessage("EXIF"), + "expiredLinkInfo": MessageLookupByLibrary.simpleMessage( + "Ce lien a expiré. Veuillez sélectionner un nouveau délai d\'expiration ou désactiver l\'expiration du lien."), + "exportLogs": MessageLookupByLibrary.simpleMessage("Exporter les logs"), + "exportYourData": + MessageLookupByLibrary.simpleMessage("Exportez vos données"), + "failedToApplyCode": MessageLookupByLibrary.simpleMessage( + "Impossible d\'appliquer le code"), + "failedToCancel": + MessageLookupByLibrary.simpleMessage("Échec de l\'annulation"), + "failedToFetchOriginalForEdit": MessageLookupByLibrary.simpleMessage( + "Impossible de récupérer l\'original pour l\'édition"), + "failedToFetchReferralDetails": MessageLookupByLibrary.simpleMessage( + "Impossible de récupérer les détails du parrainage. Veuillez réessayer plus tard."), + "failedToLoadAlbums": MessageLookupByLibrary.simpleMessage( + "Impossible de charger les albums"), + "failedToRenew": + MessageLookupByLibrary.simpleMessage("Échec du renouvellement"), + "familyPlans": MessageLookupByLibrary.simpleMessage("Forfaits famille"), + "faq": MessageLookupByLibrary.simpleMessage("FAQ"), + "faqs": MessageLookupByLibrary.simpleMessage("FAQ"), + "favorite": MessageLookupByLibrary.simpleMessage("Favori"), + "feedback": MessageLookupByLibrary.simpleMessage("Commentaires"), + "fileSavedToGallery": MessageLookupByLibrary.simpleMessage( + "Fichier enregistré dans la galerie"), + "filesDeleted": + MessageLookupByLibrary.simpleMessage("Fichiers supprimés"), + "forgotPassword": + MessageLookupByLibrary.simpleMessage("Mot de passe oublié"), + "freeStorageClaimed": + MessageLookupByLibrary.simpleMessage("Stockage gratuit réclamé"), + "freeStorageOnReferralSuccess": m17, + "freeStorageUsable": + MessageLookupByLibrary.simpleMessage("Stockage gratuit utilisable"), + "freeTrial": MessageLookupByLibrary.simpleMessage("Essai gratuit"), + "freeUpDeviceSpace": MessageLookupByLibrary.simpleMessage( + "Libérer de l\'espace sur l\'appareil"), + "general": MessageLookupByLibrary.simpleMessage("Général"), + "generatingEncryptionKeys": MessageLookupByLibrary.simpleMessage( + "Génération des clés de chiffrement..."), + "googlePlayId": + MessageLookupByLibrary.simpleMessage("Identifiant Google Play"), + "hidden": MessageLookupByLibrary.simpleMessage("Masqué"), + "hide": MessageLookupByLibrary.simpleMessage("Masquer"), + "howItWorks": + MessageLookupByLibrary.simpleMessage("Comment ça fonctionne"), + "howToViewShareeVerificationID": MessageLookupByLibrary.simpleMessage( + "Demandez-leur d\'appuyer longuement sur leur adresse e-mail sur l\'écran des paramètres et de vérifier que les identifiants des deux appareils correspondent."), + "ignoredFolderUploadReason": MessageLookupByLibrary.simpleMessage( + "Certains fichiers de cet album sont ignorés parce qu\'ils avaient été précédemment supprimés de ente."), + "importing": + MessageLookupByLibrary.simpleMessage("Importation en cours..."), + "incorrectPasswordTitle": + MessageLookupByLibrary.simpleMessage("Mot de passe incorrect"), + "incorrectRecoveryKeyBody": MessageLookupByLibrary.simpleMessage( + "La clé de récupération que vous avez entrée est incorrecte"), + "incorrectRecoveryKeyTitle": MessageLookupByLibrary.simpleMessage( + "Clé de récupération non valide"), + "insecureDevice": + MessageLookupByLibrary.simpleMessage("Appareil non sécurisé"), + "installManually": + MessageLookupByLibrary.simpleMessage("Installation manuelle"), + "invalidEmailAddress": + MessageLookupByLibrary.simpleMessage("Adresse e-mail invalide"), + "invalidKey": MessageLookupByLibrary.simpleMessage("Clé invalide"), + "invalidRecoveryKey": MessageLookupByLibrary.simpleMessage( + "La clé de récupération que vous avez saisie n\'est pas valide. Veuillez vous assurer qu\'elle "), + "inviteToEnte": MessageLookupByLibrary.simpleMessage("Inviter à ente"), + "inviteYourFriends": + MessageLookupByLibrary.simpleMessage("Invite tes ami(e)s"), + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": + MessageLookupByLibrary.simpleMessage( + "Il semble qu\'une erreur s\'est produite. Veuillez réessayer après un certain temps. Si l\'erreur persiste, veuillez contacter notre équipe d\'assistance."), + "itemCount": m22, + "itemSelectedCount": m23, + "itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion": + MessageLookupByLibrary.simpleMessage( + "Les éléments montrent le nombre de jours restants avant la suppression définitive"), + "itemsWillBeRemovedFromAlbum": MessageLookupByLibrary.simpleMessage( + "Les éléments sélectionnés seront supprimés de cet album"), + "keepPhotos": + MessageLookupByLibrary.simpleMessage("Conserver les photos"), + "kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage( + "Veuillez nous aider avec cette information"), + "language": MessageLookupByLibrary.simpleMessage("Langue"), + "lastUpdated": + MessageLookupByLibrary.simpleMessage("Dernière mise à jour"), + "leaveAlbum": MessageLookupByLibrary.simpleMessage("Quitter l\'album"), + "leaveSharedAlbum": + MessageLookupByLibrary.simpleMessage("Quitter l\'album partagé?"), + "lightTheme": MessageLookupByLibrary.simpleMessage("Clair"), + "linkCopiedToClipboard": MessageLookupByLibrary.simpleMessage( + "Lien copié dans le presse-papiers"), + "linkDeviceLimit": + MessageLookupByLibrary.simpleMessage("Limite d\'appareil"), + "linkEnabled": MessageLookupByLibrary.simpleMessage("Activé"), + "linkExpired": MessageLookupByLibrary.simpleMessage("Expiré"), + "linkExpiresOn": m24, + "linkExpiry": + MessageLookupByLibrary.simpleMessage("Expiration du lien"), + "linkHasExpired": + MessageLookupByLibrary.simpleMessage("Le lien a expiré"), + "linkNeverExpires": MessageLookupByLibrary.simpleMessage("Jamais"), + "loadingGallery": + MessageLookupByLibrary.simpleMessage("Chargement de la galerie..."), + "loadingMessage": + MessageLookupByLibrary.simpleMessage("Chargement de vos photos..."), + "localGallery": MessageLookupByLibrary.simpleMessage("Galerie locale"), + "lockButtonLabel": MessageLookupByLibrary.simpleMessage("Verrouiller"), + "lockScreenEnablePreSteps": MessageLookupByLibrary.simpleMessage( + "Pour activer l\'écran de verrouillage, veuillez configurer le code d\'accès de l\'appareil ou le verrouillage de l\'écran dans les paramètres de votre système."), + "lockscreen": + MessageLookupByLibrary.simpleMessage("Ecran de vérouillage"), + "logInLabel": MessageLookupByLibrary.simpleMessage("Se connecter"), + "loggingOut": MessageLookupByLibrary.simpleMessage("Deconnexion..."), + "loginTerms": MessageLookupByLibrary.simpleMessage( + "En cliquant sur connecter, j\'accepte les conditions d\'utilisation et la politique de confidentialité"), + "logout": MessageLookupByLibrary.simpleMessage("Déconnexion"), + "logsDialogBody": MessageLookupByLibrary.simpleMessage( + "Cela enverra des logs pour nous aider à déboguer votre problème. Veuillez noter que les noms de fichiers seront inclus pour aider à suivre les problèmes avec des fichiers spécifiques."), + "lostDevice": MessageLookupByLibrary.simpleMessage("Appareil perdu ?"), + "manage": MessageLookupByLibrary.simpleMessage("Gérer"), + "manageDeviceStorage": MessageLookupByLibrary.simpleMessage( + "Gérer le stockage de l\'appareil"), + "manageFamily": + MessageLookupByLibrary.simpleMessage("Gérer la famille"), + "manageLink": MessageLookupByLibrary.simpleMessage("Gérer le lien"), + "manageParticipants": MessageLookupByLibrary.simpleMessage("Gérer"), + "manageSubscription": + MessageLookupByLibrary.simpleMessage("Gérer l\'abonnement"), + "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), + "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), + "maxDeviceLimitSpikeHandling": m25, + "memoryCount": m26, + "merchandise": MessageLookupByLibrary.simpleMessage("Marchandise"), + "moderateStrength": MessageLookupByLibrary.simpleMessage("Modéré"), + "monthly": MessageLookupByLibrary.simpleMessage("Mensuel"), + "moveItem": m27, + "moveToAlbum": + MessageLookupByLibrary.simpleMessage("Déplacer vers l\'album"), + "movedSuccessfullyTo": m28, + "movedToTrash": + MessageLookupByLibrary.simpleMessage("Déplacé dans la corbeille"), + "movingFilesToAlbum": MessageLookupByLibrary.simpleMessage( + "Déplacement des fichiers vers l\'album..."), + "name": MessageLookupByLibrary.simpleMessage("Nom"), + "never": MessageLookupByLibrary.simpleMessage("Jamais"), + "newAlbum": MessageLookupByLibrary.simpleMessage("Nouvel album"), + "newest": MessageLookupByLibrary.simpleMessage("Le plus récent"), + "no": MessageLookupByLibrary.simpleMessage("Non"), + "noDeviceThatCanBeDeleted": MessageLookupByLibrary.simpleMessage( + "Vous n\'avez pas de fichiers sur cet appareil qui peuvent être supprimés"), + "noDuplicates": MessageLookupByLibrary.simpleMessage("✨ Aucun doublon"), + "noRecoveryKey": + MessageLookupByLibrary.simpleMessage("Aucune clé de récupération?"), + "noRecoveryKeyNoDecryption": MessageLookupByLibrary.simpleMessage( + "En raison de notre protocole de chiffrement de bout en bout, vos données ne peuvent pas être déchiffré sans votre mot de passe ou clé de récupération"), + "noResults": MessageLookupByLibrary.simpleMessage("Aucun résultat"), + "ok": MessageLookupByLibrary.simpleMessage("OK"), + "oops": MessageLookupByLibrary.simpleMessage("Oups"), + "oopsSomethingWentWrong": MessageLookupByLibrary.simpleMessage( + "Oups, une erreur est arrivée"), + "optionalAsShortAsYouLike": MessageLookupByLibrary.simpleMessage( + "Optionnel, aussi court que vous le souhaitez..."), + "orPickAnExistingOne": MessageLookupByLibrary.simpleMessage( + "Sélectionner un fichier existant"), + "password": MessageLookupByLibrary.simpleMessage("Mot de passe"), + "passwordChangedSuccessfully": MessageLookupByLibrary.simpleMessage( + "Le mot de passe a été modifié"), + "passwordLock": + MessageLookupByLibrary.simpleMessage("Mot de passe verrou"), + "passwordStrength": m29, + "passwordWarning": MessageLookupByLibrary.simpleMessage( + "Nous ne stockons pas ce mot de passe, donc si vous l\'oubliez, nous ne pouvons pas déchiffrer vos données"), + "paymentDetails": + MessageLookupByLibrary.simpleMessage("Détails de paiement"), + "pendingSync": + MessageLookupByLibrary.simpleMessage("Synchronisation en attente"), + "peopleUsingYourCode": MessageLookupByLibrary.simpleMessage( + "Personnes utilisant votre code"), + "permDeleteWarning": MessageLookupByLibrary.simpleMessage( + "Tous les éléments de la corbeille seront définitivement supprimés\n\nCette action ne peut pas être annulée"), + "permanentlyDelete": + MessageLookupByLibrary.simpleMessage("Supprimer définitivement"), + "permanentlyDeleteFromDevice": MessageLookupByLibrary.simpleMessage( + "Supprimer définitivement de l\'appareil ?"), + "photoGridSize": + MessageLookupByLibrary.simpleMessage("Taille de la grille photo"), + "photoSmallCase": MessageLookupByLibrary.simpleMessage("photo"), + "photosAddedByYouWillBeRemovedFromTheAlbum": + MessageLookupByLibrary.simpleMessage( + "Les photos ajoutées par vous seront retirées de l\'album"), + "playstoreSubscription": + MessageLookupByLibrary.simpleMessage("Abonnement au PlayStore"), + "pleaseEmailUsAt": m32, + "pleaseSendTheLogsTo": m33, + "pleaseTryAgain": + MessageLookupByLibrary.simpleMessage("Veuillez réessayer"), + "pleaseWait": + MessageLookupByLibrary.simpleMessage("Veuillez patienter..."), + "pleaseWaitForSometimeBeforeRetrying": + MessageLookupByLibrary.simpleMessage( + "Veuillez attendre quelque temps avant de réessayer"), + "preparingLogs": + MessageLookupByLibrary.simpleMessage("Préparation des journaux..."), + "pressAndHoldToPlayVideo": MessageLookupByLibrary.simpleMessage( + "Appuyez et maintenez enfoncé pour lire la vidéo"), + "privacy": MessageLookupByLibrary.simpleMessage("Confidentialité"), + "privacyPolicyTitle": MessageLookupByLibrary.simpleMessage( + "Politique de Confidentialité"), + "publicLinkCreated": + MessageLookupByLibrary.simpleMessage("Lien public créé"), + "publicLinkEnabled": + MessageLookupByLibrary.simpleMessage("Lien public activé"), + "rateUs": MessageLookupByLibrary.simpleMessage("Évaluez-nous"), + "rateUsOnStore": m34, + "recover": MessageLookupByLibrary.simpleMessage("Restaurer"), + "recoverAccount": + MessageLookupByLibrary.simpleMessage("Récupérer un compte"), + "recoverButton": MessageLookupByLibrary.simpleMessage("Récupérer"), + "recoveryKey": + MessageLookupByLibrary.simpleMessage("Clé de récupération"), + "recoveryKeyCopiedToClipboard": MessageLookupByLibrary.simpleMessage( + "Clé de récupération copiée dans le presse-papiers"), + "recoveryKeyOnForgotPassword": MessageLookupByLibrary.simpleMessage( + "Si vous oubliez votre mot de passe, la seule façon de récupérer vos données sera grâce à cette clé."), + "recoveryKeySaveDescription": MessageLookupByLibrary.simpleMessage( + "Nous ne stockons pas cette clé, veuillez enregistrer cette clé de 24 mots dans un endroit sûr."), + "recoveryKeySuccessBody": MessageLookupByLibrary.simpleMessage( + "Génial ! Votre clé de récupération est valide. Merci de votre vérification.\n\nN\'oubliez pas de garder votre clé de récupération sauvegardée."), + "recoveryKeyVerified": MessageLookupByLibrary.simpleMessage( + "Clé de récupération vérifiée"), + "recoveryKeyVerifyReason": MessageLookupByLibrary.simpleMessage( + "Votre clé de récupération est la seule façon de récupérer vos photos si vous oubliez votre mot de passe. Vous pouvez trouver votre clé de récupération dans Paramètres > Compte.\n\nVeuillez entrer votre clé de récupération ici pour vous assurer que vous l\'avez enregistrée correctement."), + "recoverySuccessful": + MessageLookupByLibrary.simpleMessage("Récupération réussie !"), + "recreatePasswordBody": MessageLookupByLibrary.simpleMessage( + "L\'appareil actuel n\'est pas assez puissant pour vérifier votre mot de passe, mais nous pouvons régénérer d\'une manière qui fonctionne avec tous les appareils.\n\nVeuillez vous connecter à l\'aide de votre clé de récupération et régénérer votre mot de passe (vous pouvez réutiliser le même si vous le souhaitez)."), + "recreatePasswordTitle": + MessageLookupByLibrary.simpleMessage("Recréer le mot de passe"), + "reddit": MessageLookupByLibrary.simpleMessage("Reddit"), + "referFriendsAnd2xYourPlan": MessageLookupByLibrary.simpleMessage( + "Parrainez des amis et 2x votre abonnement"), + "referralStep1": MessageLookupByLibrary.simpleMessage( + "1. Donnez ce code à vos amis"), + "referralStep2": MessageLookupByLibrary.simpleMessage( + "2. Ils s\'inscrivent à une offre payante"), + "referralStep3": m35, + "referrals": MessageLookupByLibrary.simpleMessage("Parrainages"), + "referralsAreCurrentlyPaused": MessageLookupByLibrary.simpleMessage( + "Les recommandations sont actuellement en pause"), + "remindToEmptyDeviceTrash": MessageLookupByLibrary.simpleMessage( + "Également vide \"récemment supprimé\" de \"Paramètres\" -> \"Stockage\" pour réclamer l\'espace libéré"), + "remindToEmptyEnteTrash": MessageLookupByLibrary.simpleMessage( + "Vide aussi votre \"Corbeille\" pour réclamer l\'espace libéré"), + "remoteImages": + MessageLookupByLibrary.simpleMessage("Images distantes"), + "remoteThumbnails": + MessageLookupByLibrary.simpleMessage("Miniatures distantes"), + "remoteVideos": + MessageLookupByLibrary.simpleMessage("Vidéos distantes"), + "remove": MessageLookupByLibrary.simpleMessage("Enlever"), + "removeDuplicates": + MessageLookupByLibrary.simpleMessage("Supprimer les doublons"), + "removeFromAlbum": + MessageLookupByLibrary.simpleMessage("Retirer de l\'album"), + "removeFromAlbumTitle": + MessageLookupByLibrary.simpleMessage("Retirer de l\'album ?"), + "removeFromFavorite": + MessageLookupByLibrary.simpleMessage("Retirer des favoris"), + "removeLink": MessageLookupByLibrary.simpleMessage("Supprimer le lien"), + "removeParticipant": + MessageLookupByLibrary.simpleMessage("Supprimer le participant"), + "removeParticipantBody": m36, + "removePublicLink": + MessageLookupByLibrary.simpleMessage("Supprimer le lien public"), + "removeShareItemsWarning": MessageLookupByLibrary.simpleMessage( + "Certains des objets que vous êtes en train de retirer ont été ajoutés par d\'autres personnes, vous perdrez l\'accès vers ces objets"), + "removeWithQuestionMark": + MessageLookupByLibrary.simpleMessage("Enlever?"), + "removingFromFavorites": + MessageLookupByLibrary.simpleMessage("Suppression des favoris…"), + "rename": MessageLookupByLibrary.simpleMessage("Renommer"), + "renameAlbum": + MessageLookupByLibrary.simpleMessage("Renommer l\'album"), + "renameFile": + MessageLookupByLibrary.simpleMessage("Renommer le fichier"), + "renewSubscription": + MessageLookupByLibrary.simpleMessage("Renouveler l’abonnement"), + "renewsOn": m37, + "reportABug": MessageLookupByLibrary.simpleMessage("Signaler un bug"), + "reportBug": MessageLookupByLibrary.simpleMessage("Signaler un bug"), + "resendEmail": + MessageLookupByLibrary.simpleMessage("Renvoyer le courriel"), + "resetIgnoredFiles": MessageLookupByLibrary.simpleMessage( + "Réinitialiser les fichiers ignorés"), + "resetPasswordTitle": MessageLookupByLibrary.simpleMessage( + "Réinitialiser le mot de passe"), + "restore": MessageLookupByLibrary.simpleMessage("Restaurer"), + "restoreToAlbum": + MessageLookupByLibrary.simpleMessage("Restaurer vers l\'album"), + "restoringFiles": MessageLookupByLibrary.simpleMessage( + "Restauration des fichiers..."), + "retry": MessageLookupByLibrary.simpleMessage("Réessayer"), + "reviewDeduplicateItems": MessageLookupByLibrary.simpleMessage( + "Veuillez vérifier et supprimer les éléments que vous croyez dupliqués."), + "saveKey": MessageLookupByLibrary.simpleMessage("Enregistrer la clé"), + "saveYourRecoveryKeyIfYouHaventAlready": + MessageLookupByLibrary.simpleMessage( + "Enregistrez votre clé de récupération si vous ne l\'avez pas déjà fait"), + "scanCode": MessageLookupByLibrary.simpleMessage("Scanner le code"), + "scanThisBarcodeWithnyourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Scannez ce code-barres avec\nvotre application d\'authentification"), + "searchByAlbumNameHint": + MessageLookupByLibrary.simpleMessage("Nom de l\'album"), + "security": MessageLookupByLibrary.simpleMessage("Sécurité"), + "selectAlbum": + MessageLookupByLibrary.simpleMessage("Sélectionner album"), + "selectAll": MessageLookupByLibrary.simpleMessage("Tout sélectionner"), + "selectFoldersForBackup": MessageLookupByLibrary.simpleMessage( + "Sélectionner les dossiers à sauvegarder"), + "selectReason": + MessageLookupByLibrary.simpleMessage("Sélectionner une raison"), + "selectYourPlan": + MessageLookupByLibrary.simpleMessage("Sélectionner votre offre"), + "selectedFilesAreNotOnEnte": MessageLookupByLibrary.simpleMessage( + "Les fichiers sélectionnés ne sont pas sur ente"), + "selectedFoldersWillBeEncryptedAndBackedUp": + MessageLookupByLibrary.simpleMessage( + "Les dossiers sélectionnés seront cryptés et sauvegardés"), + "selectedItemsWillBeDeletedFromAllAlbumsAndMoved": + MessageLookupByLibrary.simpleMessage( + "Les éléments sélectionnés seront supprimés de tous les albums et déplacés dans la corbeille."), + "selectedPhotos": m38, + "selectedPhotosWithYours": m39, + "send": MessageLookupByLibrary.simpleMessage("Envoyer"), + "sendEmail": MessageLookupByLibrary.simpleMessage("Envoyer un e-mail"), + "sendInvite": + MessageLookupByLibrary.simpleMessage("Envoyer Invitations"), + "sendLink": MessageLookupByLibrary.simpleMessage("Envoyer le lien"), + "sessionExpired": + MessageLookupByLibrary.simpleMessage("Session expirée"), + "setAPassword": + MessageLookupByLibrary.simpleMessage("Définir un mot de passe"), + "setAs": MessageLookupByLibrary.simpleMessage("Définir comme"), + "setPasswordTitle": + MessageLookupByLibrary.simpleMessage("Définir le mot de passe"), + "setupComplete": + MessageLookupByLibrary.simpleMessage("Configuration fini"), + "share": MessageLookupByLibrary.simpleMessage("Partager"), + "shareALink": MessageLookupByLibrary.simpleMessage("Partager le lien"), + "shareAlbumHint": MessageLookupByLibrary.simpleMessage( + "Ouvrez un album et appuyez sur le bouton de partage en haut à droite pour le partager."), + "shareAnAlbumNow": MessageLookupByLibrary.simpleMessage( + "Partagez un album maintenant"), + "shareLink": MessageLookupByLibrary.simpleMessage("Partager le lien"), + "shareMyVerificationID": m40, + "shareTextConfirmOthersVerificationID": m41, + "shareTextRecommendUsingEnte": MessageLookupByLibrary.simpleMessage( + "Téléchargez ente pour que nous puissions facilement partager des photos et des vidéos de qualité originale\n\nhttps://ente.io/#download"), + "shareTextReferralCode": m42, + "shareWithNonenteUsers": MessageLookupByLibrary.simpleMessage( + "Partager avec des utilisateurs non-ente"), + "shareWithPeopleSectionTitle": m43, + "sharedAlbumSectionDescription": MessageLookupByLibrary.simpleMessage( + "Créez des albums partagés et collaboratifs avec d\'autres utilisateurs de ente, y compris des utilisateurs sur des plans gratuits."), + "sharedByMe": MessageLookupByLibrary.simpleMessage("Partagé par moi"), + "sharedWith": m44, + "sharing": MessageLookupByLibrary.simpleMessage("Partage..."), + "signUpTerms": MessageLookupByLibrary.simpleMessage( + "J\'accepte les conditions d\'utilisation et la politique de confidentialité"), + "singleFileDeleteFromDevice": m45, + "singleFileDeleteHighlight": MessageLookupByLibrary.simpleMessage( + "Il sera supprimé de tous les albums."), + "singleFileInBothLocalAndRemote": m46, + "singleFileInRemoteOnly": m47, + "skip": MessageLookupByLibrary.simpleMessage("Ignorer"), + "social": MessageLookupByLibrary.simpleMessage("Réseaux Sociaux"), + "someItemsAreInBothEnteAndYourDevice": + MessageLookupByLibrary.simpleMessage( + "Certains éléments sont à la fois dans ente et votre appareil."), + "someOfTheFilesYouAreTryingToDeleteAre": + MessageLookupByLibrary.simpleMessage( + "Certains des fichiers que vous essayez de supprimer ne sont disponibles que sur votre appareil et ne peuvent pas être récupérés s\'ils sont supprimés"), + "someoneSharingAlbumsWithYouShouldSeeTheSameId": + MessageLookupByLibrary.simpleMessage( + "Quelqu\'un qui partage des albums avec vous devrait voir le même ID sur son appareil."), + "somethingWentWrong": + MessageLookupByLibrary.simpleMessage("Un problème est survenu"), + "somethingWentWrongPleaseTryAgain": + MessageLookupByLibrary.simpleMessage( + "Quelque chose s\'est mal passé, veuillez recommencer"), + "sorry": MessageLookupByLibrary.simpleMessage("Désolé"), + "sorryCouldNotAddToFavorites": MessageLookupByLibrary.simpleMessage( + "Désolé, impossible d\'ajouter aux favoris !"), + "sorryCouldNotRemoveFromFavorites": + MessageLookupByLibrary.simpleMessage( + "Désolé, impossible de supprimer des favoris !"), + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": + MessageLookupByLibrary.simpleMessage( + "Désolé, nous n\'avons pas pu générer de clés sécurisées sur cet appareil.\n\nVeuillez vous inscrire depuis un autre appareil."), + "sparkleSuccess": MessageLookupByLibrary.simpleMessage("✨ Succès"), + "storageInGB": m48, + "strongStrength": MessageLookupByLibrary.simpleMessage("Fort"), + "subWillBeCancelledOn": m50, + "subscribe": MessageLookupByLibrary.simpleMessage("S\'abonner"), + "subscribeToEnableSharing": MessageLookupByLibrary.simpleMessage( + "Il semble que votre abonnement ait expiré. Veuillez vous abonner pour activer le partage."), + "subscription": MessageLookupByLibrary.simpleMessage("Abonnement"), + "success": MessageLookupByLibrary.simpleMessage("Succès"), + "successfullyArchived": + MessageLookupByLibrary.simpleMessage("Archivé avec succès"), + "successfullyUnarchived": + MessageLookupByLibrary.simpleMessage("Désarchivé avec succès"), + "suggestFeatures": MessageLookupByLibrary.simpleMessage( + "Suggérer des fonctionnalités"), + "support": MessageLookupByLibrary.simpleMessage("Support"), + "syncProgress": m51, + "syncStopped": + MessageLookupByLibrary.simpleMessage("Synchronisation arrêtée ?"), + "syncing": MessageLookupByLibrary.simpleMessage( + "En cours de synchronisation..."), + "systemTheme": MessageLookupByLibrary.simpleMessage("Système"), + "tapToCopy": MessageLookupByLibrary.simpleMessage("taper pour copier"), + "tapToEnterCode": + MessageLookupByLibrary.simpleMessage("Appuyez pour entrer un code"), + "tempErrorContactSupportIfPersists": MessageLookupByLibrary.simpleMessage( + "Il semble qu\'une erreur s\'est produite. Veuillez réessayer après un certain temps. Si l\'erreur persiste, veuillez contacter notre équipe d\'assistance."), + "terminate": MessageLookupByLibrary.simpleMessage("Quitte"), + "terminateSession": + MessageLookupByLibrary.simpleMessage("Quitter la session ?"), + "terms": MessageLookupByLibrary.simpleMessage("Conditions"), + "termsOfServicesTitle": + MessageLookupByLibrary.simpleMessage("Conditions d\'utilisation"), + "thankYou": MessageLookupByLibrary.simpleMessage("Merci"), + "thankYouForSubscribing": + MessageLookupByLibrary.simpleMessage("Merci de vous être abonné !"), + "theDownloadCouldNotBeCompleted": MessageLookupByLibrary.simpleMessage( + "Le téléchargement n\'a pas pu être terminé"), + "theme": MessageLookupByLibrary.simpleMessage("Thème"), + "theseItemsWillBeDeletedFromYourDevice": + MessageLookupByLibrary.simpleMessage( + "Ces éléments seront supprimés de votre appareil."), + "theyAlsoGetXGb": m52, + "theyWillBeDeletedFromAllAlbums": MessageLookupByLibrary.simpleMessage( + "Ils seront supprimés de tous les albums."), + "thisActionCannotBeUndone": MessageLookupByLibrary.simpleMessage( + "Cette action ne peut pas être annulée"), + "thisAlbumAlreadyHDACollaborativeLink": + MessageLookupByLibrary.simpleMessage( + "Cet album a déjà un lien collaboratif"), + "thisCanBeUsedToRecoverYourAccountIfYou": + MessageLookupByLibrary.simpleMessage( + "Cela peut être utilisé pour récupérer votre compte si vous perdez votre deuxième facteur"), + "thisDevice": MessageLookupByLibrary.simpleMessage("Cet appareil"), + "thisIsPersonVerificationId": m53, + "thisIsYourVerificationId": MessageLookupByLibrary.simpleMessage( + "Ceci est votre ID de vérification"), + "thisWillLogYouOutOfTheFollowingDevice": + MessageLookupByLibrary.simpleMessage( + "Cela vous déconnectera de l\'appareil suivant :"), + "thisWillLogYouOutOfThisDevice": MessageLookupByLibrary.simpleMessage( + "Cela vous déconnectera de cet appareil !"), + "todaysLogs": MessageLookupByLibrary.simpleMessage("Journaux du jour"), + "total": MessageLookupByLibrary.simpleMessage("total"), + "totalSize": MessageLookupByLibrary.simpleMessage("Taille totale"), + "trash": MessageLookupByLibrary.simpleMessage("Corbeille"), + "tryAgain": MessageLookupByLibrary.simpleMessage("Réessayer"), + "twitter": MessageLookupByLibrary.simpleMessage("Twitter"), + "twoMonthsFreeOnYearlyPlans": MessageLookupByLibrary.simpleMessage( + "2 mois gratuits sur les forfaits annuels"), + "twofactor": + MessageLookupByLibrary.simpleMessage("Double authentification"), + "twofactorAuthenticationPageTitle": + MessageLookupByLibrary.simpleMessage( + "Authentification à deux facteurs"), + "twofactorSetup": MessageLookupByLibrary.simpleMessage( + "Configuration de l\'authentification à deux facteurs"), + "unarchive": MessageLookupByLibrary.simpleMessage("Désarchiver"), + "unarchiving": + MessageLookupByLibrary.simpleMessage("Désarchivage en cours..."), + "uncategorized": + MessageLookupByLibrary.simpleMessage("Aucune catégorie"), + "unhide": MessageLookupByLibrary.simpleMessage("Dévoiler"), + "unhideToAlbum": + MessageLookupByLibrary.simpleMessage("Afficher dans l\'album"), + "unhidingFilesToAlbum": MessageLookupByLibrary.simpleMessage( + "Démasquage des fichiers vers l\'album"), + "unselectAll": + MessageLookupByLibrary.simpleMessage("Désélectionner tout"), + "update": MessageLookupByLibrary.simpleMessage("Mise à jour"), + "updateAvailable": MessageLookupByLibrary.simpleMessage( + "Une mise à jour est disponible"), + "updatingFolderSelection": MessageLookupByLibrary.simpleMessage( + "Mise à jour de la sélection du dossier..."), + "uploadingFilesToAlbum": MessageLookupByLibrary.simpleMessage( + "Envoi des fichiers vers l\'album..."), + "usableReferralStorageInfo": MessageLookupByLibrary.simpleMessage( + "Le stockage utilisable est limité par votre offre actuelle. Le stockage excédentaire deviendra automatiquement utilisable lorsque vous mettez à niveau votre offre."), + "usePublicLinksForPeopleNotOnEnte": MessageLookupByLibrary.simpleMessage( + "Utiliser des liens publics pour les personnes qui ne sont pas sur ente"), + "useRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Utiliser la clé de récupération"), + "verificationId": + MessageLookupByLibrary.simpleMessage("ID de vérification"), + "verify": MessageLookupByLibrary.simpleMessage("Vérifier"), + "verifyEmail": + MessageLookupByLibrary.simpleMessage("Vérifier l\'email"), + "verifyEmailID": m54, + "verifyPassword": + MessageLookupByLibrary.simpleMessage("Vérifier le mot de passe"), + "verifying": + MessageLookupByLibrary.simpleMessage("Validation en cours..."), + "verifyingRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Vérification de la clé de récupération..."), + "videoSmallCase": MessageLookupByLibrary.simpleMessage("vidéo"), + "viewActiveSessions": MessageLookupByLibrary.simpleMessage( + "Afficher les sessions actives"), + "viewLogs": + MessageLookupByLibrary.simpleMessage("Afficher les journaux"), + "viewRecoveryKey": + MessageLookupByLibrary.simpleMessage("Voir la clé de récupération"), + "viewer": MessageLookupByLibrary.simpleMessage("Observateur"), + "weAreOpenSource": + MessageLookupByLibrary.simpleMessage("Nous sommes open source !"), + "weDontSupportEditingPhotosAndAlbumsThatYouDont": + MessageLookupByLibrary.simpleMessage( + "Nous ne prenons pas en charge l\'édition des photos et des albums que vous ne possédez pas encore"), + "weakStrength": MessageLookupByLibrary.simpleMessage("Faible"), + "welcomeBack": MessageLookupByLibrary.simpleMessage("Bienvenue !"), + "weveSentAMailTo": MessageLookupByLibrary.simpleMessage( + "Nous avons envoyé un email à"), + "yearly": MessageLookupByLibrary.simpleMessage("Annuel"), + "yearsAgo": m55, + "yes": MessageLookupByLibrary.simpleMessage("Oui"), + "yesCancel": MessageLookupByLibrary.simpleMessage("Oui, annuler"), + "yesConvertToViewer": MessageLookupByLibrary.simpleMessage( + "Oui, convertir en observateur"), + "yesDelete": MessageLookupByLibrary.simpleMessage("Oui, supprimer"), + "yesLogout": + MessageLookupByLibrary.simpleMessage("Oui, se déconnecter"), + "yesRemove": MessageLookupByLibrary.simpleMessage("Oui, supprimer"), + "yesRenew": MessageLookupByLibrary.simpleMessage("Oui, renouveler"), + "you": MessageLookupByLibrary.simpleMessage("Vous"), + "youAreOnTheLatestVersion": MessageLookupByLibrary.simpleMessage( + "Vous êtes sur la dernière version"), + "youCanAtMaxDoubleYourStorage": MessageLookupByLibrary.simpleMessage( + "* Vous pouvez au maximum doubler votre espace de stockage"), + "youCanManageYourLinksInTheShareTab": + MessageLookupByLibrary.simpleMessage( + "Vous pouvez gérer vos liens dans l\'onglet Partage."), + "youCannotDowngradeToThisPlan": MessageLookupByLibrary.simpleMessage( + "Vous ne pouvez pas rétrograder vers cette offre"), + "youCannotShareWithYourself": MessageLookupByLibrary.simpleMessage( + "Vous ne pouvez pas partager avec vous-même"), + "youHaveSuccessfullyFreedUp": m56, + "yourAccountHasBeenDeleted": + MessageLookupByLibrary.simpleMessage("Votre compte a été supprimé"), + "yourPlanWasSuccessfullyDowngraded": + MessageLookupByLibrary.simpleMessage( + "Votre plan a été rétrogradé avec succès"), + "yourPlanWasSuccessfullyUpgraded": MessageLookupByLibrary.simpleMessage( + "Votre offre a été mise à jour avec succès"), + "yourPurchaseWasSuccessful": MessageLookupByLibrary.simpleMessage( + "Votre achat a été effectué avec succès"), + "yourStorageDetailsCouldNotBeFetched": + MessageLookupByLibrary.simpleMessage( + "Vos informations de stockage n\'ont pas pu être récupérées"), + "yourSubscriptionWasUpdatedSuccessfully": + MessageLookupByLibrary.simpleMessage( + "Votre abonnement a été mis à jour avec succès"), + "youveNoDuplicateFilesThatCanBeCleared": + MessageLookupByLibrary.simpleMessage( + "Vous n\'avez aucun fichier dédupliqué pouvant être nettoyé"), + "youveNoFilesInThisAlbumThatCanBeDeleted": + MessageLookupByLibrary.simpleMessage( + "Vous n\'avez pas de fichiers dans cet album qui peuvent être supprimés") + }; +} diff --git a/lib/generated/intl/messages_it.dart b/lib/generated/intl/messages_it.dart new file mode 100644 index 000000000..e21165462 --- /dev/null +++ b/lib/generated/intl/messages_it.dart @@ -0,0 +1,25 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a it locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'it'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => {}; +} diff --git a/lib/generated/intl/messages_ko.dart b/lib/generated/intl/messages_ko.dart new file mode 100644 index 000000000..8d8edc845 --- /dev/null +++ b/lib/generated/intl/messages_ko.dart @@ -0,0 +1,25 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a ko locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'ko'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => {}; +} diff --git a/lib/generated/intl/messages_nl.dart b/lib/generated/intl/messages_nl.dart new file mode 100644 index 000000000..687b45990 --- /dev/null +++ b/lib/generated/intl/messages_nl.dart @@ -0,0 +1,548 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a nl locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'nl'; + + static String m4(user) => + "${user} zal geen foto\'s meer kunnen toevoegen aan dit album\n\nDe gebruiker zal nog steeds bestaande foto\'s kunnen verwijderen die door hen zijn toegevoegd"; + + static String m10(albumName) => + "Dit verwijdert de openbare link voor toegang tot \"${albumName}\"."; + + static String m11(supportEmail) => + "Stuur een e-mail naar ${supportEmail} vanaf het door jou geregistreerde e-mailadres"; + + static String m14(email) => + "${email} heeft geen ente account.\n\nStuur ze een uitnodiging om foto\'s te delen."; + + static String m22(count) => + "${Intl.plural(count, one: '${count} item', other: '${count} items')}"; + + static String m24(expiryTime) => "Link vervalt op ${expiryTime}"; + + static String m25(maxValue) => + "Wanneer ingesteld op het maximum (${maxValue}), wordt het apparaatlimiet versoepeld om tijdelijke pieken van grote aantallen kijkers mogelijk te maken."; + + static String m26(count) => + "${Intl.plural(count, zero: 'geen herinneringen', one: '${count} herinnering', other: '${count} herinneringen')}"; + + static String m29(passwordStrengthValue) => + "Wachtwoord sterkte: ${passwordStrengthValue}"; + + static String m36(userEmail) => + "${userEmail} zal worden verwijderd uit dit gedeelde album\n\nAlle door hen toegevoegde foto\'s worden ook uit het album verwijderd"; + + static String m38(count) => "${count} geselecteerd"; + + static String m39(count, yourCount) => + "${count} geselecteerd (${yourCount} van jou)"; + + static String m40(verificationID) => + "Hier is mijn verificatie-ID: ${verificationID} voor ente.io."; + + static String m41(verificationID) => + "Hey, kunt u bevestigen dat dit uw ente.io verificatie-ID is: ${verificationID}"; + + static String m43(numberOfPeople) => + "${Intl.plural(numberOfPeople, zero: 'Deel met specifieke mensen', one: 'Gedeeld met 1 persoon', other: 'Gedeeld met ${numberOfPeople} mensen')}"; + + static String m45(fileType) => + "Dit ${fileType} zal worden verwijderd van jouw apparaat."; + + static String m46(fileType) => + "Dit ${fileType} staat zowel in ente als in jouw apparaat."; + + static String m47(fileType) => + "Dit ${fileType} zal worden verwijderd uit ente."; + + static String m53(email) => "Dit is de verificatie-ID van ${email}"; + + static String m54(email) => "Verifieer ${email}"; + + static String m55(count) => + "${Intl.plural(count, one: '${count} jaar geleden', other: '${count} jaren geleden')}"; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "aNewVersionOfEnteIsAvailable": MessageLookupByLibrary.simpleMessage( + "Er is een nieuwe versie van ente beschikbaar."), + "about": MessageLookupByLibrary.simpleMessage("Over"), + "account": MessageLookupByLibrary.simpleMessage("Account"), + "accountWelcomeBack": + MessageLookupByLibrary.simpleMessage("Welkom terug!"), + "ackPasswordLostWarning": MessageLookupByLibrary.simpleMessage( + "Ik begrijp dat als ik mijn wachtwoord verlies, ik mijn gegevens kan verliezen omdat mijn gegevens end-to-end versleuteld zijn."), + "activeSessions": + MessageLookupByLibrary.simpleMessage("Actieve sessies"), + "addANewEmail": + MessageLookupByLibrary.simpleMessage("Nieuw e-mailadres toevoegen"), + "addCollaborator": + MessageLookupByLibrary.simpleMessage("Samenwerker toevoegen"), + "addMore": MessageLookupByLibrary.simpleMessage("Meer toevoegen"), + "addViewer": MessageLookupByLibrary.simpleMessage("Voeg kijker toe"), + "addedAs": MessageLookupByLibrary.simpleMessage("Toegevoegd als"), + "addingToFavorites": + MessageLookupByLibrary.simpleMessage("Toevoegen aan favorieten..."), + "advancedSettings": MessageLookupByLibrary.simpleMessage("Geavanceerd"), + "after1Day": MessageLookupByLibrary.simpleMessage("Na 1 dag"), + "after1Hour": MessageLookupByLibrary.simpleMessage("Na 1 uur"), + "after1Month": MessageLookupByLibrary.simpleMessage("Na 1 maand"), + "after1Week": MessageLookupByLibrary.simpleMessage("Na 1 week"), + "after1Year": MessageLookupByLibrary.simpleMessage("Na 1 jaar"), + "albumOwner": MessageLookupByLibrary.simpleMessage("Eigenaar"), + "albumUpdated": + MessageLookupByLibrary.simpleMessage("Album bijgewerkt"), + "albums": MessageLookupByLibrary.simpleMessage("Albums"), + "allowAddPhotosDescription": MessageLookupByLibrary.simpleMessage( + "Sta toe dat mensen met de link ook foto\'s kunnen toevoegen aan het gedeelde album."), + "allowAddingPhotos": + MessageLookupByLibrary.simpleMessage("Foto\'s toevoegen toestaan"), + "allowDownloads": + MessageLookupByLibrary.simpleMessage("Downloads toestaan"), + "applyCodeTitle": + MessageLookupByLibrary.simpleMessage("Code toepassen"), + "archive": MessageLookupByLibrary.simpleMessage("Archiveer"), + "areYouSureYouWantToLogout": MessageLookupByLibrary.simpleMessage( + "Weet je zeker dat je wilt uitloggen?"), + "askDeleteReason": MessageLookupByLibrary.simpleMessage( + "Wat is de voornaamste reden dat je jouw account verwijdert?"), + "authToChangeYourEmail": MessageLookupByLibrary.simpleMessage( + "Gelieve te verifiëren om je e-mailadres te wijzigen"), + "authToChangeYourPassword": MessageLookupByLibrary.simpleMessage( + "Gelieve te verifiëren om je wachtwoord te wijzigen"), + "authToInitiateAccountDeletion": MessageLookupByLibrary.simpleMessage( + "Gelieve te verifiëren om het verwijderen van je account te starten"), + "authToViewYourHiddenFiles": MessageLookupByLibrary.simpleMessage( + "Gelieve te verifiëren om je verborgen bestanden te bekijken"), + "backupOverMobileData": MessageLookupByLibrary.simpleMessage( + "Back-up maken via mobiele data"), + "backupSettings": + MessageLookupByLibrary.simpleMessage("Back-up instellingen"), + "backupVideos": + MessageLookupByLibrary.simpleMessage("Back-up video\'s"), + "canOnlyRemoveFilesOwnedByYou": MessageLookupByLibrary.simpleMessage( + "Kan alleen bestanden verwijderen die jouw eigendom zijn"), + "cancel": MessageLookupByLibrary.simpleMessage("Annuleer"), + "cannotAddMorePhotosAfterBecomingViewer": m4, + "changeEmail": MessageLookupByLibrary.simpleMessage("E-mail wijzigen"), + "changePassword": + MessageLookupByLibrary.simpleMessage("Wachtwoord wijzigen"), + "changePasswordTitle": + MessageLookupByLibrary.simpleMessage("Wachtwoord wijzigen"), + "changePermissions": + MessageLookupByLibrary.simpleMessage("Rechten aanpassen?"), + "checkForUpdates": + MessageLookupByLibrary.simpleMessage("Controleer op updates"), + "checkInboxAndSpamFolder": MessageLookupByLibrary.simpleMessage( + "Controleer je inbox (en spam) om verificatie te voltooien"), + "checking": MessageLookupByLibrary.simpleMessage("Controleren..."), + "codeCopiedToClipboard": MessageLookupByLibrary.simpleMessage( + "Code gekopieerd naar klembord"), + "collabLinkSectionDescription": MessageLookupByLibrary.simpleMessage( + "Maak een link waarmee mensen foto\'s in jouw gedeelde album kunnen toevoegen en bekijken zonder dat ze daarvoor een ente app of account nodig hebben. Handig voor het verzamelen van foto\'s van evenementen."), + "collaborativeLink": + MessageLookupByLibrary.simpleMessage("Gezamenlijke link"), + "collaborator": MessageLookupByLibrary.simpleMessage("Samenwerker"), + "collaboratorsCanAddPhotosAndVideosToTheSharedAlbum": + MessageLookupByLibrary.simpleMessage( + "Samenwerkers kunnen foto\'s en video\'s toevoegen aan het gedeelde album."), + "collectPhotos": + MessageLookupByLibrary.simpleMessage("Foto\'s verzamelen"), + "confirm": MessageLookupByLibrary.simpleMessage("Bevestig"), + "confirmAccountDeletion": MessageLookupByLibrary.simpleMessage( + "Account verwijderen bevestigen"), + "confirmDeletePrompt": MessageLookupByLibrary.simpleMessage( + "Ja, ik wil permanent mijn account inclusief alle gegevens verwijderen."), + "confirmPassword": + MessageLookupByLibrary.simpleMessage("Wachtwoord bevestigen"), + "confirmRecoveryKey": + MessageLookupByLibrary.simpleMessage("Bevestig herstelsleutel"), + "confirmYourRecoveryKey": + MessageLookupByLibrary.simpleMessage("Bevestig herstelsleutel"), + "contactSupport": + MessageLookupByLibrary.simpleMessage("Contacteer ondersteuning"), + "continueLabel": MessageLookupByLibrary.simpleMessage("Doorgaan"), + "copyLink": MessageLookupByLibrary.simpleMessage("Kopieer link"), + "copypasteThisCodentoYourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Kopieer en plak deze code\nnaar je authenticator app"), + "createAccount": + MessageLookupByLibrary.simpleMessage("Account aanmaken"), + "createAlbumActionHint": MessageLookupByLibrary.simpleMessage( + "Lang indrukken om foto\'s te selecteren en klik + om een album te maken"), + "createNewAccount": + MessageLookupByLibrary.simpleMessage("Nieuw account aanmaken"), + "createPublicLink": + MessageLookupByLibrary.simpleMessage("Maak publieke link"), + "creatingLink": + MessageLookupByLibrary.simpleMessage("Link aanmaken..."), + "criticalUpdateAvailable": MessageLookupByLibrary.simpleMessage( + "Belangrijke update beschikbaar"), + "custom": MessageLookupByLibrary.simpleMessage("Aangepast"), + "decrypting": MessageLookupByLibrary.simpleMessage("Ontsleutelen..."), + "deleteAccount": + MessageLookupByLibrary.simpleMessage("Account verwijderen"), + "deleteAccountFeedbackPrompt": MessageLookupByLibrary.simpleMessage( + "We vinden het jammer je te zien gaan. Deel je feedback om ons te helpen verbeteren."), + "deleteAccountPermanentlyButton": MessageLookupByLibrary.simpleMessage( + "Account permanent verwijderen"), + "deleteAlbum": MessageLookupByLibrary.simpleMessage("Verwijder album"), + "deleteAlbumDialog": MessageLookupByLibrary.simpleMessage( + "Verwijder de foto\'s (en video\'s) van dit album ook uit alle andere albums waar deze deel van uitmaken?"), + "deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage( + "Je staat op het punt je account en alle bijbehorende gegevens permanent te verwijderen.\nDeze actie is onomkeerbaar."), + "deleteEmailRequest": MessageLookupByLibrary.simpleMessage( + "Stuur een e-mail naar account-deletion@ente.io vanaf het door jou geregistreerde e-mailadres."), + "deleteFromBoth": + MessageLookupByLibrary.simpleMessage("Verwijder van beide"), + "deleteFromDevice": + MessageLookupByLibrary.simpleMessage("Verwijder van apparaat"), + "deleteFromEnte": + MessageLookupByLibrary.simpleMessage("Verwijder van ente"), + "deletePhotos": + MessageLookupByLibrary.simpleMessage("Foto\'s verwijderen"), + "deleteReason1": MessageLookupByLibrary.simpleMessage( + "Ik mis een belangrijke functie"), + "deleteReason2": MessageLookupByLibrary.simpleMessage( + "De app of een bepaalde functie functioneert niet \nzoals ik verwacht"), + "deleteReason3": MessageLookupByLibrary.simpleMessage( + "Ik heb een andere dienst gevonden die me beter bevalt"), + "deleteReason4": MessageLookupByLibrary.simpleMessage( + "Mijn reden wordt niet vermeld"), + "deleteRequestSLAText": MessageLookupByLibrary.simpleMessage( + "Je verzoek wordt binnen 72 uur verwerkt."), + "deleteSharedAlbum": + MessageLookupByLibrary.simpleMessage("Gedeeld album verwijderen?"), + "deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage( + "Het album wordt verwijderd voor iedereen\n\nJe verliest de toegang tot gedeelde foto\'s in dit album die eigendom zijn van anderen"), + "deviceLockExplanation": MessageLookupByLibrary.simpleMessage( + "Schakel de schermvergrendeling van het apparaat uit wanneer ente op de voorgrond is en er een back-up aan de gang is. Dit is normaal gesproken niet nodig, maar kan grote uploads en initiële imports van grote mappen sneller laten verlopen."), + "disableAutoLock": MessageLookupByLibrary.simpleMessage( + "Automatisch vergrendelen uitschakelen"), + "disableDownloadWarningBody": MessageLookupByLibrary.simpleMessage( + "Kijkers kunnen nog steeds screenshots maken of een kopie van je foto\'s opslaan met behulp van externe tools"), + "disableDownloadWarningTitle": + MessageLookupByLibrary.simpleMessage("Let op"), + "disableLinkMessage": m10, + "doThisLater": MessageLookupByLibrary.simpleMessage("Doe dit later"), + "done": MessageLookupByLibrary.simpleMessage("Voltooid"), + "downloading": MessageLookupByLibrary.simpleMessage("Downloaden..."), + "dropSupportEmail": m11, + "email": MessageLookupByLibrary.simpleMessage("E-mail"), + "emailNoEnteAccount": m14, + "encryption": MessageLookupByLibrary.simpleMessage("Encryptie"), + "encryptionKeys": + MessageLookupByLibrary.simpleMessage("Encryptiesleutels"), + "enterCode": MessageLookupByLibrary.simpleMessage("Voer code in"), + "enterEmail": + MessageLookupByLibrary.simpleMessage("Voer e-mailadres in"), + "enterNewPasswordToEncrypt": MessageLookupByLibrary.simpleMessage( + "Voer een nieuw wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen"), + "enterPassword": + MessageLookupByLibrary.simpleMessage("Voer wachtwoord in"), + "enterPasswordToEncrypt": MessageLookupByLibrary.simpleMessage( + "Voer een wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen"), + "enterThe6digitCodeFromnyourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Voer de 6-cijferige code van je verificatie-app in"), + "enterValidEmail": MessageLookupByLibrary.simpleMessage( + "Voer een geldig e-mailadres in."), + "enterYourEmailAddress": + MessageLookupByLibrary.simpleMessage("Voer je e-mailadres in"), + "enterYourPassword": + MessageLookupByLibrary.simpleMessage("Voer je wachtwoord in"), + "enterYourRecoveryKey": + MessageLookupByLibrary.simpleMessage("Voer je herstelcode in"), + "expiredLinkInfo": MessageLookupByLibrary.simpleMessage( + "Deze link is verlopen. Selecteer een nieuwe vervaltijd of schakel de vervaldatum uit."), + "exportYourData": + MessageLookupByLibrary.simpleMessage("Exporteer je gegevens"), + "failedToLoadAlbums": + MessageLookupByLibrary.simpleMessage("Laden van albums mislukt"), + "feedback": MessageLookupByLibrary.simpleMessage("Feedback"), + "forgotPassword": + MessageLookupByLibrary.simpleMessage("Wachtwoord vergeten"), + "generatingEncryptionKeys": MessageLookupByLibrary.simpleMessage( + "Encryptiesleutels genereren..."), + "hidden": MessageLookupByLibrary.simpleMessage("Verborgen"), + "howItWorks": MessageLookupByLibrary.simpleMessage("Hoe het werkt"), + "howToViewShareeVerificationID": MessageLookupByLibrary.simpleMessage( + "Vraag hen om hun e-mailadres lang in te drukken op het instellingenscherm en te controleren dat de ID\'s op beide apparaten overeenkomen."), + "importing": MessageLookupByLibrary.simpleMessage("Importeren...."), + "incorrectPasswordTitle": + MessageLookupByLibrary.simpleMessage("Onjuist wachtwoord"), + "incorrectRecoveryKeyBody": MessageLookupByLibrary.simpleMessage( + "De ingevoerde herstelsleutel is onjuist"), + "incorrectRecoveryKeyTitle": + MessageLookupByLibrary.simpleMessage("Onjuiste herstelsleutel"), + "insecureDevice": + MessageLookupByLibrary.simpleMessage("Onveilig apparaat"), + "installManually": + MessageLookupByLibrary.simpleMessage("Installeer handmatig"), + "invalidEmailAddress": + MessageLookupByLibrary.simpleMessage("Ongeldig e-mailadres"), + "invalidKey": MessageLookupByLibrary.simpleMessage("Ongeldige sleutel"), + "invalidRecoveryKey": MessageLookupByLibrary.simpleMessage( + "De herstelsleutel die je hebt ingevoerd is niet geldig. Zorg ervoor dat deze 24 woorden bevat en controleer de spelling van elk van deze woorden.\n\nAls je een oudere herstelcode hebt ingevoerd, zorg ervoor dat deze 64 tekens lang is, en controleer ze allemaal."), + "inviteToEnte": + MessageLookupByLibrary.simpleMessage("Uitnodigen voor ente"), + "itemCount": m22, + "keepPhotos": MessageLookupByLibrary.simpleMessage("Foto\'s behouden"), + "kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage( + "Help ons alsjeblieft met deze informatie"), + "linkDeviceLimit": + MessageLookupByLibrary.simpleMessage("Apparaat limiet"), + "linkEnabled": MessageLookupByLibrary.simpleMessage("Ingeschakeld"), + "linkExpired": MessageLookupByLibrary.simpleMessage("Verlopen"), + "linkExpiresOn": m24, + "linkExpiry": MessageLookupByLibrary.simpleMessage("Vervaldatum"), + "linkHasExpired": + MessageLookupByLibrary.simpleMessage("Link is vervallen"), + "linkNeverExpires": MessageLookupByLibrary.simpleMessage("Nooit"), + "lockButtonLabel": MessageLookupByLibrary.simpleMessage("Vergrendel"), + "logInLabel": MessageLookupByLibrary.simpleMessage("Inloggen"), + "loginTerms": MessageLookupByLibrary.simpleMessage( + "Door op inloggen te klikken, ga ik akkoord met de gebruiksvoorwaarden en privacybeleid"), + "logout": MessageLookupByLibrary.simpleMessage("Uitloggen"), + "lostDevice": + MessageLookupByLibrary.simpleMessage("Apparaat verloren?"), + "manage": MessageLookupByLibrary.simpleMessage("Beheren"), + "manageDeviceStorage": + MessageLookupByLibrary.simpleMessage("Apparaatopslag beheren"), + "manageLink": MessageLookupByLibrary.simpleMessage("Beheer link"), + "manageParticipants": MessageLookupByLibrary.simpleMessage("Beheren"), + "manageSubscription": + MessageLookupByLibrary.simpleMessage("Abonnement beheren"), + "maxDeviceLimitSpikeHandling": m25, + "memoryCount": m26, + "moderateStrength": MessageLookupByLibrary.simpleMessage("Matig"), + "movedToTrash": + MessageLookupByLibrary.simpleMessage("Naar prullenbak verplaatst"), + "never": MessageLookupByLibrary.simpleMessage("Nooit"), + "newAlbum": MessageLookupByLibrary.simpleMessage("Nieuw album"), + "noDuplicates": + MessageLookupByLibrary.simpleMessage("✨ Geen duplicaten"), + "noRecoveryKey": + MessageLookupByLibrary.simpleMessage("Geen herstelcode?"), + "noRecoveryKeyNoDecryption": MessageLookupByLibrary.simpleMessage( + "Door de aard van ons end-to-end encryptieprotocol kunnen je gegevens niet worden ontsleuteld zonder je wachtwoord of herstelsleutel"), + "ok": MessageLookupByLibrary.simpleMessage("Oké"), + "oops": MessageLookupByLibrary.simpleMessage("Oeps"), + "orPickAnExistingOne": + MessageLookupByLibrary.simpleMessage("Of kies een bestaande"), + "password": MessageLookupByLibrary.simpleMessage("Wachtwoord"), + "passwordChangedSuccessfully": MessageLookupByLibrary.simpleMessage( + "Wachtwoord succesvol aangepast"), + "passwordLock": MessageLookupByLibrary.simpleMessage("Wachtwoord slot"), + "passwordStrength": m29, + "passwordWarning": MessageLookupByLibrary.simpleMessage( + "Wij slaan dit wachtwoord niet op, dus als je het vergeet, kunnen we je gegevens niet ontsleutelen"), + "photoGridSize": + MessageLookupByLibrary.simpleMessage("Foto raster grootte"), + "photoSmallCase": MessageLookupByLibrary.simpleMessage("foto"), + "pleaseTryAgain": + MessageLookupByLibrary.simpleMessage("Probeer het nog eens"), + "pleaseWait": + MessageLookupByLibrary.simpleMessage("Een ogenblik geduld..."), + "privacy": MessageLookupByLibrary.simpleMessage("Privacy"), + "privacyPolicyTitle": + MessageLookupByLibrary.simpleMessage("Privacybeleid"), + "publicLinkEnabled": + MessageLookupByLibrary.simpleMessage("Publieke link ingeschakeld"), + "recover": MessageLookupByLibrary.simpleMessage("Herstellen"), + "recoverAccount": + MessageLookupByLibrary.simpleMessage("Account herstellen"), + "recoverButton": MessageLookupByLibrary.simpleMessage("Herstellen"), + "recoveryKey": MessageLookupByLibrary.simpleMessage("Herstelsleutel"), + "recoveryKeyCopiedToClipboard": MessageLookupByLibrary.simpleMessage( + "Herstelsleutel gekopieerd naar klembord"), + "recoveryKeyOnForgotPassword": MessageLookupByLibrary.simpleMessage( + "Als je je wachtwoord vergeet, kun je alleen met deze sleutel je gegevens herstellen."), + "recoveryKeySaveDescription": MessageLookupByLibrary.simpleMessage( + "We slaan deze sleutel niet op, bewaar deze 24 woorden sleutel op een veilige plaats."), + "recoveryKeySuccessBody": MessageLookupByLibrary.simpleMessage( + "Super! Je herstelsleutel is geldig. Bedankt voor het verifiëren.\n\nVergeet niet om je herstelsleutel veilig te bewaren."), + "recoveryKeyVerified": MessageLookupByLibrary.simpleMessage( + "Herstel sleutel geverifieerd"), + "recoveryKeyVerifyReason": MessageLookupByLibrary.simpleMessage( + "Je herstelsleutel is de enige manier om je foto\'s te herstellen als je je wachtwoord bent vergeten. Je vindt je herstelsleutel in Instellingen > Account.\n\nVoer hier je herstelsleutel in om te controleren of je hem correct hebt opgeslagen."), + "recoverySuccessful": + MessageLookupByLibrary.simpleMessage("Herstel succesvol!"), + "recreatePasswordBody": MessageLookupByLibrary.simpleMessage( + "Het huidige apparaat is niet krachtig genoeg om je wachtwoord te verifiëren, dus moeten we de code een keer opnieuw genereren op een manier die met alle apparaten werkt.\n\nLog in met behulp van uw herstelcode en genereer opnieuw uw wachtwoord (je kunt dezelfde indien gewenst opnieuw gebruiken)."), + "recreatePasswordTitle": MessageLookupByLibrary.simpleMessage( + "Wachtwoord opnieuw instellen"), + "remove": MessageLookupByLibrary.simpleMessage("Verwijder"), + "removeLink": MessageLookupByLibrary.simpleMessage("Verwijder link"), + "removeParticipant": + MessageLookupByLibrary.simpleMessage("Deelnemer verwijderen"), + "removeParticipantBody": m36, + "removePublicLink": + MessageLookupByLibrary.simpleMessage("Verwijder publieke link"), + "removeShareItemsWarning": MessageLookupByLibrary.simpleMessage( + "Sommige van de items die je verwijdert zijn door andere mensen toegevoegd, en je verliest de toegang daartoe"), + "removeWithQuestionMark": + MessageLookupByLibrary.simpleMessage("Verwijder?"), + "removingFromFavorites": MessageLookupByLibrary.simpleMessage( + "Verwijderen uit favorieten..."), + "resendEmail": + MessageLookupByLibrary.simpleMessage("E-mail opnieuw versturen"), + "resetPasswordTitle": + MessageLookupByLibrary.simpleMessage("Wachtwoord resetten"), + "retry": MessageLookupByLibrary.simpleMessage("Opnieuw"), + "saveKey": MessageLookupByLibrary.simpleMessage("Bewaar sleutel"), + "saveYourRecoveryKeyIfYouHaventAlready": + MessageLookupByLibrary.simpleMessage( + "Sla je herstelsleutel op als je dat nog niet gedaan hebt"), + "scanCode": MessageLookupByLibrary.simpleMessage("Scan code"), + "scanThisBarcodeWithnyourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Scan deze barcode met\nje authenticator app"), + "selectAll": MessageLookupByLibrary.simpleMessage("Selecteer alles"), + "selectFoldersForBackup": MessageLookupByLibrary.simpleMessage( + "Selecteer mappen voor back-up"), + "selectReason": MessageLookupByLibrary.simpleMessage("Selecteer reden"), + "selectedFoldersWillBeEncryptedAndBackedUp": + MessageLookupByLibrary.simpleMessage( + "Geselecteerde mappen worden versleuteld en geback-upt"), + "selectedPhotos": m38, + "selectedPhotosWithYours": m39, + "sendEmail": MessageLookupByLibrary.simpleMessage("E-mail versturen"), + "sendInvite": + MessageLookupByLibrary.simpleMessage("Stuur een uitnodiging"), + "sendLink": MessageLookupByLibrary.simpleMessage("Stuur link"), + "setAPassword": + MessageLookupByLibrary.simpleMessage("Stel een wachtwoord in"), + "setPasswordTitle": + MessageLookupByLibrary.simpleMessage("Wachtwoord instellen"), + "setupComplete": MessageLookupByLibrary.simpleMessage("Setup voltooid"), + "shareALink": MessageLookupByLibrary.simpleMessage("Deel een link"), + "shareMyVerificationID": m40, + "shareTextConfirmOthersVerificationID": m41, + "shareWithNonenteUsers": MessageLookupByLibrary.simpleMessage( + "Delen met niet-ente gebruikers"), + "shareWithPeopleSectionTitle": m43, + "sharedAlbumSectionDescription": MessageLookupByLibrary.simpleMessage( + "Maak gedeelde en collaboratieve albums met andere ente gebruikers, inclusief gebruikers met gratis abonnementen."), + "sharing": MessageLookupByLibrary.simpleMessage("Delen..."), + "signUpTerms": MessageLookupByLibrary.simpleMessage( + "Ik ga akkoord met de gebruiksvoorwaarden en privacybeleid"), + "singleFileDeleteFromDevice": m45, + "singleFileDeleteHighlight": MessageLookupByLibrary.simpleMessage( + "Het wordt uit alle albums verwijderd."), + "singleFileInBothLocalAndRemote": m46, + "singleFileInRemoteOnly": m47, + "skip": MessageLookupByLibrary.simpleMessage("Overslaan"), + "someoneSharingAlbumsWithYouShouldSeeTheSameId": + MessageLookupByLibrary.simpleMessage( + "Iemand die albums met je deelt zou hetzelfde ID op hun apparaat moeten zien."), + "somethingWentWrong": + MessageLookupByLibrary.simpleMessage("Er ging iets mis"), + "somethingWentWrongPleaseTryAgain": + MessageLookupByLibrary.simpleMessage( + "Er is iets fout gegaan, probeer het opnieuw"), + "sorry": MessageLookupByLibrary.simpleMessage("Sorry"), + "sorryCouldNotAddToFavorites": MessageLookupByLibrary.simpleMessage( + "Sorry, kon niet aan favorieten worden toegevoegd!"), + "sorryCouldNotRemoveFromFavorites": + MessageLookupByLibrary.simpleMessage( + "Sorry, kon niet uit favorieten worden verwijderd!"), + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": + MessageLookupByLibrary.simpleMessage( + "Sorry, we konden geen beveiligde sleutels genereren op dit apparaat.\n\nGelieve je aan te melden vanaf een ander apparaat."), + "sparkleSuccess": MessageLookupByLibrary.simpleMessage("✨ Succes"), + "strongStrength": MessageLookupByLibrary.simpleMessage("Sterk"), + "subscribe": MessageLookupByLibrary.simpleMessage("Abonneer"), + "subscribeToEnableSharing": MessageLookupByLibrary.simpleMessage( + "Het lijkt erop dat je abonnement is verlopen. Abonneer om delen mogelijk te maken."), + "tapToCopy": MessageLookupByLibrary.simpleMessage("tik om te kopiëren"), + "tapToEnterCode": + MessageLookupByLibrary.simpleMessage("Tik om code in te voeren"), + "terminate": MessageLookupByLibrary.simpleMessage("Beëindigen"), + "terminateSession": + MessageLookupByLibrary.simpleMessage("Sessie beëindigen?"), + "terms": MessageLookupByLibrary.simpleMessage("Voorwaarden"), + "termsOfServicesTitle": + MessageLookupByLibrary.simpleMessage("Voorwaarden"), + "theDownloadCouldNotBeCompleted": MessageLookupByLibrary.simpleMessage( + "De download kon niet worden voltooid"), + "thisCanBeUsedToRecoverYourAccountIfYou": + MessageLookupByLibrary.simpleMessage( + "Dit kan worden gebruikt om je account te herstellen als je je tweede factor verliest"), + "thisDevice": MessageLookupByLibrary.simpleMessage("Dit apparaat"), + "thisIsPersonVerificationId": m53, + "thisIsYourVerificationId": + MessageLookupByLibrary.simpleMessage("Dit is uw verificatie-ID"), + "thisWillLogYouOutOfTheFollowingDevice": + MessageLookupByLibrary.simpleMessage( + "Dit zal je uitloggen van het volgende apparaat:"), + "thisWillLogYouOutOfThisDevice": MessageLookupByLibrary.simpleMessage( + "Dit zal je uitloggen van dit apparaat!"), + "trash": MessageLookupByLibrary.simpleMessage("Prullenbak"), + "tryAgain": MessageLookupByLibrary.simpleMessage("Probeer opnieuw"), + "twofactorAuthenticationPageTitle": + MessageLookupByLibrary.simpleMessage("Tweestapsverificatie"), + "twofactorSetup": + MessageLookupByLibrary.simpleMessage("Tweestapsverificatie"), + "uncategorized": + MessageLookupByLibrary.simpleMessage("Ongecategoriseerd"), + "unselectAll": + MessageLookupByLibrary.simpleMessage("Deselecteer alles"), + "update": MessageLookupByLibrary.simpleMessage("Update"), + "updateAvailable": + MessageLookupByLibrary.simpleMessage("Update beschikbaar"), + "updatingFolderSelection": + MessageLookupByLibrary.simpleMessage("Map selectie bijwerken..."), + "useRecoveryKey": + MessageLookupByLibrary.simpleMessage("Herstelcode gebruiken"), + "verificationId": + MessageLookupByLibrary.simpleMessage("Verificatie ID"), + "verify": MessageLookupByLibrary.simpleMessage("Verifiëren"), + "verifyEmail": MessageLookupByLibrary.simpleMessage("Bevestig e-mail"), + "verifyEmailID": m54, + "verifyPassword": + MessageLookupByLibrary.simpleMessage("Bevestig wachtwoord"), + "verifyingRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Herstelsleutel verifiëren..."), + "videoSmallCase": MessageLookupByLibrary.simpleMessage("video"), + "viewRecoveryKey": + MessageLookupByLibrary.simpleMessage("Toon herstelsleutel"), + "viewer": MessageLookupByLibrary.simpleMessage("Kijker"), + "weAreOpenSource": + MessageLookupByLibrary.simpleMessage("We zijn open source!"), + "weakStrength": MessageLookupByLibrary.simpleMessage("Zwak"), + "welcomeBack": MessageLookupByLibrary.simpleMessage("Welkom terug!"), + "weveSentAMailTo": MessageLookupByLibrary.simpleMessage( + "We hebben een e-mail gestuurd naar"), + "yearsAgo": m55, + "yesConvertToViewer": + MessageLookupByLibrary.simpleMessage("Ja, converteren naar viewer"), + "yesDelete": MessageLookupByLibrary.simpleMessage("Ja, verwijderen"), + "yesLogout": MessageLookupByLibrary.simpleMessage("Ja, log uit"), + "yesRemove": MessageLookupByLibrary.simpleMessage("Ja, verwijderen"), + "you": MessageLookupByLibrary.simpleMessage("Jij"), + "youAreOnTheLatestVersion": + MessageLookupByLibrary.simpleMessage("Je hebt de laatste versie"), + "youCannotShareWithYourself": MessageLookupByLibrary.simpleMessage( + "Je kunt niet met jezelf delen"), + "yourAccountHasBeenDeleted": + MessageLookupByLibrary.simpleMessage("Je account is verwijderd") + }; +} diff --git a/lib/generated/intl/messages_pl.dart b/lib/generated/intl/messages_pl.dart new file mode 100644 index 000000000..1abb8b260 --- /dev/null +++ b/lib/generated/intl/messages_pl.dart @@ -0,0 +1,128 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a pl locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'pl'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "accountWelcomeBack": + MessageLookupByLibrary.simpleMessage("Witaj ponownie!"), + "activeSessions": MessageLookupByLibrary.simpleMessage("Aktywne sesje"), + "askDeleteReason": MessageLookupByLibrary.simpleMessage( + "Jaka jest przyczyna usunięcia konta?"), + "cancel": MessageLookupByLibrary.simpleMessage("Anuluj"), + "checkInboxAndSpamFolder": MessageLookupByLibrary.simpleMessage( + "Sprawdź swoją skrzynkę odbiorczą (i spam), aby zakończyć weryfikację"), + "confirm": MessageLookupByLibrary.simpleMessage("Potwierdź"), + "confirmAccountDeletion": + MessageLookupByLibrary.simpleMessage("Potwierdź usunięcie konta"), + "confirmDeletePrompt": MessageLookupByLibrary.simpleMessage( + "Tak, chcę trwale usunąć konto i wszystkie dane z nim powiązane."), + "confirmPassword": + MessageLookupByLibrary.simpleMessage("Powtórz hasło"), + "continueLabel": MessageLookupByLibrary.simpleMessage("Kontynuuj"), + "createAccount": MessageLookupByLibrary.simpleMessage("Stwórz konto"), + "createNewAccount": + MessageLookupByLibrary.simpleMessage("Stwórz nowe konto"), + "decrypting": + MessageLookupByLibrary.simpleMessage("Odszyfrowywanie..."), + "deleteAccount": MessageLookupByLibrary.simpleMessage("Usuń konto"), + "deleteAccountFeedbackPrompt": MessageLookupByLibrary.simpleMessage( + "Przykro nam, że odchodzisz. Wyjaśnij nam, dlaczego nas opuszczasz, aby pomóc ulepszać nasze usługi."), + "deleteAccountPermanentlyButton": + MessageLookupByLibrary.simpleMessage("Usuń konto na stałe"), + "deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage( + "Zamierzasz trwale usunąć swoje konto i wszystkie jego dane.\nTa akcja jest nieodwracalna."), + "deleteEmailRequest": MessageLookupByLibrary.simpleMessage( + "Wyślij wiadomość e-mail na account-deletion@ente.io z zarejestrowanego adresu e-mail."), + "deleteReason1": MessageLookupByLibrary.simpleMessage( + "Brakuje kluczowej funkcji, której potrzebuję"), + "deleteReason2": MessageLookupByLibrary.simpleMessage( + "Aplikacja lub określona funkcja nie \nzachowuje się tak, jak sądzę, że powinna"), + "deleteReason3": MessageLookupByLibrary.simpleMessage( + "Znalazłem inną, lepszą usługę"), + "deleteReason4": MessageLookupByLibrary.simpleMessage( + "Inna, niewymieniona wyżej przyczyna"), + "deleteRequestSLAText": MessageLookupByLibrary.simpleMessage( + "Twoje żądanie zostanie przetworzone w ciągu 72 godzin."), + "doThisLater": MessageLookupByLibrary.simpleMessage("Spróbuj później"), + "email": MessageLookupByLibrary.simpleMessage("Adres e-mail"), + "enterCode": MessageLookupByLibrary.simpleMessage("Wprowadź kod"), + "enterValidEmail": MessageLookupByLibrary.simpleMessage( + "Podaj poprawny adres e-mail."), + "enterYourEmailAddress": + MessageLookupByLibrary.simpleMessage("Podaj swój adres e-mail"), + "enterYourPassword": + MessageLookupByLibrary.simpleMessage("Wprowadź hasło"), + "enterYourRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Wprowadź swój klucz odzyskiwania"), + "feedback": MessageLookupByLibrary.simpleMessage("Informacja zwrotna"), + "forgotPassword": + MessageLookupByLibrary.simpleMessage("Nie pamiętam hasła"), + "incorrectRecoveryKeyBody": + MessageLookupByLibrary.simpleMessage("Kod jest nieprawidłowy"), + "incorrectRecoveryKeyTitle": MessageLookupByLibrary.simpleMessage( + "Nieprawidłowy klucz odzyskiwania"), + "invalidEmailAddress": + MessageLookupByLibrary.simpleMessage("Nieprawidłowy adres e-mail"), + "kindlyHelpUsWithThisInformation": + MessageLookupByLibrary.simpleMessage("Pomóż nam z tą informacją"), + "logInLabel": MessageLookupByLibrary.simpleMessage("Zaloguj się"), + "noRecoveryKey": + MessageLookupByLibrary.simpleMessage("Brak klucza odzyskiwania?"), + "noRecoveryKeyNoDecryption": MessageLookupByLibrary.simpleMessage( + "Ze względu na charakter naszego protokołu szyfrowania end-to-end, dane nie mogą być odszyfrowane bez hasła lub klucza odzyskiwania"), + "ok": MessageLookupByLibrary.simpleMessage("Ok"), + "oops": MessageLookupByLibrary.simpleMessage("Ups"), + "password": MessageLookupByLibrary.simpleMessage("Hasło"), + "pleaseWait": MessageLookupByLibrary.simpleMessage("Proszę czekać..."), + "recoverButton": MessageLookupByLibrary.simpleMessage("Odzyskaj"), + "recoverySuccessful": + MessageLookupByLibrary.simpleMessage("Odzyskano pomyślnie!"), + "resetPasswordTitle": + MessageLookupByLibrary.simpleMessage("Zresetuj hasło"), + "selectReason": MessageLookupByLibrary.simpleMessage("Wybierz powód"), + "sendEmail": MessageLookupByLibrary.simpleMessage("Wyślij e-mail"), + "somethingWentWrongPleaseTryAgain": + MessageLookupByLibrary.simpleMessage( + "Coś poszło nie tak, spróbuj ponownie"), + "sorry": MessageLookupByLibrary.simpleMessage("Przepraszamy"), + "strongStrength": MessageLookupByLibrary.simpleMessage("Silne"), + "terminate": MessageLookupByLibrary.simpleMessage("Zakończ"), + "terminateSession": + MessageLookupByLibrary.simpleMessage("Zakończyć sesję?"), + "termsOfServicesTitle": + MessageLookupByLibrary.simpleMessage("Regulamin"), + "thisDevice": MessageLookupByLibrary.simpleMessage("To urządzenie"), + "thisWillLogYouOutOfTheFollowingDevice": + MessageLookupByLibrary.simpleMessage( + "To wyloguje Cię z tego urządzenia:"), + "thisWillLogYouOutOfThisDevice": MessageLookupByLibrary.simpleMessage( + "To wyloguje Cię z tego urządzenia!"), + "tryAgain": MessageLookupByLibrary.simpleMessage("Spróbuj ponownie"), + "verify": MessageLookupByLibrary.simpleMessage("Weryfikuj"), + "verifyEmail": + MessageLookupByLibrary.simpleMessage("Zweryfikuj adres e-mail"), + "weakStrength": MessageLookupByLibrary.simpleMessage("Słabe"), + "welcomeBack": MessageLookupByLibrary.simpleMessage("Witaj ponownie!"), + "yourAccountHasBeenDeleted": + MessageLookupByLibrary.simpleMessage("Twoje konto zostało usunięte") + }; +} diff --git a/lib/generated/intl/messages_pt.dart b/lib/generated/intl/messages_pt.dart new file mode 100644 index 000000000..ea9d4b201 --- /dev/null +++ b/lib/generated/intl/messages_pt.dart @@ -0,0 +1,204 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a pt locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'pt'; + + static String m4(user) => + "${user} Não poderá adicionar mais fotos a este álbum\n\nEles ainda poderão remover as fotos existentes adicionadas por eles"; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "accountWelcomeBack": + MessageLookupByLibrary.simpleMessage("Bem-vindo de volta!"), + "activeSessions": + MessageLookupByLibrary.simpleMessage("Sessões ativas"), + "addANewEmail": + MessageLookupByLibrary.simpleMessage("Adicionar um novo email"), + "addCollaborator": + MessageLookupByLibrary.simpleMessage("Adicionar colaborador"), + "addMore": MessageLookupByLibrary.simpleMessage("Adicione mais"), + "addViewer": + MessageLookupByLibrary.simpleMessage("Adicionar visualizador"), + "addedAs": MessageLookupByLibrary.simpleMessage("Adicionado como"), + "albumOwner": MessageLookupByLibrary.simpleMessage("Proprietário"), + "allowAddPhotosDescription": MessageLookupByLibrary.simpleMessage( + "Permita que as pessoas com o link também adicionem fotos ao álbum compartilhado."), + "allowAddingPhotos": + MessageLookupByLibrary.simpleMessage("Permitir adicionar fotos"), + "allowDownloads": + MessageLookupByLibrary.simpleMessage("Permitir transferências"), + "askDeleteReason": MessageLookupByLibrary.simpleMessage( + "Qual é o principal motivo para você excluir sua conta?"), + "cancel": MessageLookupByLibrary.simpleMessage("Cancelar"), + "cannotAddMorePhotosAfterBecomingViewer": m4, + "changePermissions": + MessageLookupByLibrary.simpleMessage("Alterar permissões?"), + "checkInboxAndSpamFolder": MessageLookupByLibrary.simpleMessage( + "Verifique sua caixa de entrada (e ‘spam’) para concluir a verificação"), + "codeCopiedToClipboard": MessageLookupByLibrary.simpleMessage( + "Código copiado para a área de transferência"), + "collaborator": MessageLookupByLibrary.simpleMessage("Colaborador"), + "collaboratorsCanAddPhotosAndVideosToTheSharedAlbum": + MessageLookupByLibrary.simpleMessage( + "Os colaboradores podem adicionar fotos e vídeos ao álbum compartilhado."), + "confirm": MessageLookupByLibrary.simpleMessage("Confirme"), + "confirmAccountDeletion": + MessageLookupByLibrary.simpleMessage("Confirmar exclusão da conta"), + "confirmDeletePrompt": MessageLookupByLibrary.simpleMessage( + "Sim, desejo excluir permanentemente esta conta e todos os seus dados."), + "confirmPassword": + MessageLookupByLibrary.simpleMessage("Confirme sua senha"), + "confirmRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Confirme a chave de recuperação"), + "confirmYourRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Confirme sua chave de recuperação"), + "copypasteThisCodentoYourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Copie e cole este código\npara seu aplicativo autenticador"), + "createAccount": + MessageLookupByLibrary.simpleMessage("Criar uma conta"), + "createNewAccount": + MessageLookupByLibrary.simpleMessage("Criar nova conta"), + "decrypting": + MessageLookupByLibrary.simpleMessage("Descriptografando..."), + "deleteAccount": MessageLookupByLibrary.simpleMessage("Deletar conta"), + "deleteAccountFeedbackPrompt": MessageLookupByLibrary.simpleMessage( + "Lamentamos ver você partir. Por favor, compartilhe seus comentários para nos ajudar a melhorar."), + "deleteAccountPermanentlyButton": MessageLookupByLibrary.simpleMessage( + "Excluir conta permanentemente"), + "deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage( + "Você está prestes a excluir permanentemente sua conta e todos os seus dados.\nEsta ação é irreversível."), + "deleteEmailRequest": MessageLookupByLibrary.simpleMessage( + "Por favor, envie um email para account-deletion@ente.io a partir do seu endereço de email registrado."), + "deleteReason1": MessageLookupByLibrary.simpleMessage( + "Está faltando um recurso-chave que eu preciso"), + "deleteReason2": MessageLookupByLibrary.simpleMessage( + "O aplicativo ou um determinado recurso não\nestá funcionando como eu acredito que deveria"), + "deleteReason3": MessageLookupByLibrary.simpleMessage( + "Encontrei outro serviço que gosto mais"), + "deleteReason4": + MessageLookupByLibrary.simpleMessage("Meu motivo não está listado"), + "deleteRequestSLAText": MessageLookupByLibrary.simpleMessage( + "Sua solicitação será processada em até 72 horas."), + "disableDownloadWarningBody": MessageLookupByLibrary.simpleMessage( + "Os espectadores ainda podem tirar screenshots ou salvar uma cópia de suas fotos usando ferramentas externas"), + "disableDownloadWarningTitle": + MessageLookupByLibrary.simpleMessage("Observe"), + "enterCode": MessageLookupByLibrary.simpleMessage("Coloque o código"), + "enterEmail": MessageLookupByLibrary.simpleMessage("Digite o email"), + "enterThe6digitCodeFromnyourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Digite o código de 6 dígitos de\nseu aplicativo autenticador"), + "enterValidEmail": MessageLookupByLibrary.simpleMessage( + "Por, favor insira um endereço de email válido."), + "enterYourEmailAddress": MessageLookupByLibrary.simpleMessage( + "Insira o seu endereço de email"), + "enterYourRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Digite sua chave de recuperação"), + "feedback": MessageLookupByLibrary.simpleMessage("Opinião"), + "forgotPassword": + MessageLookupByLibrary.simpleMessage("Esqueceu sua senha"), + "incorrectRecoveryKeyBody": MessageLookupByLibrary.simpleMessage( + "A chave de recuperação que você digitou está incorreta"), + "incorrectRecoveryKeyTitle": MessageLookupByLibrary.simpleMessage( + "Chave de recuperação incorreta"), + "invalidEmailAddress": + MessageLookupByLibrary.simpleMessage("Endereço de email invalido"), + "invalidKey": MessageLookupByLibrary.simpleMessage("Chave inválida"), + "invalidRecoveryKey": MessageLookupByLibrary.simpleMessage( + "A chave de recuperação que você digitou não é válida. Certifique-se de que contém 24 palavras e verifique a ortografia de cada uma.\n\nSe você inseriu um código de recuperação mais antigo, verifique se ele tem 64 caracteres e verifique cada um deles."), + "kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage( + "Ajude-nos com esta informação"), + "linkDeviceLimit": + MessageLookupByLibrary.simpleMessage("Limite do dispositivo"), + "linkExpired": MessageLookupByLibrary.simpleMessage("Expirado"), + "linkExpiry": MessageLookupByLibrary.simpleMessage("Expiração do link"), + "lostDevice": + MessageLookupByLibrary.simpleMessage("Dispositivo perdido?"), + "manage": MessageLookupByLibrary.simpleMessage("Gerenciar"), + "noRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Nenhuma chave de recuperação?"), + "noRecoveryKeyNoDecryption": MessageLookupByLibrary.simpleMessage( + "Devido à natureza do nosso protocolo de criptografia de ponta a ponta, seus dados não podem ser descriptografados sem sua senha ou chave de recuperação"), + "oops": MessageLookupByLibrary.simpleMessage("Ops"), + "orPickAnExistingOne": + MessageLookupByLibrary.simpleMessage("Ou escolha um existente"), + "password": MessageLookupByLibrary.simpleMessage("Senha"), + "passwordLock": + MessageLookupByLibrary.simpleMessage("Bloqueio de senha"), + "recoverButton": MessageLookupByLibrary.simpleMessage("Recuperar"), + "recoveryKeySuccessBody": MessageLookupByLibrary.simpleMessage( + "Ótimo! Sua chave de recuperação é válida. Obrigado por verificar.\n\nLembre-se de manter o backup seguro de sua chave de recuperação."), + "recoveryKeyVerified": MessageLookupByLibrary.simpleMessage( + "Chave de recuperação verificada"), + "recoveryKeyVerifyReason": MessageLookupByLibrary.simpleMessage( + "Sua chave de recuperação é a única maneira de recuperar suas fotos se você esquecer sua senha. Você pode encontrar sua chave de recuperação em Configurações > Conta.\n\nDigite sua chave de recuperação aqui para verificar se você a salvou corretamente."), + "recoverySuccessful": + MessageLookupByLibrary.simpleMessage("Recuperação bem sucedida!"), + "remove": MessageLookupByLibrary.simpleMessage("Remover"), + "removeParticipant": + MessageLookupByLibrary.simpleMessage("Remover participante"), + "saveYourRecoveryKeyIfYouHaventAlready": + MessageLookupByLibrary.simpleMessage( + "Salve sua chave de recuperação, caso ainda não o tenha feito"), + "scanCode": MessageLookupByLibrary.simpleMessage("Escanear código"), + "scanThisBarcodeWithnyourAuthenticatorApp": + MessageLookupByLibrary.simpleMessage( + "Escaneie este código de barras com\nseu aplicativo autenticador"), + "selectReason": + MessageLookupByLibrary.simpleMessage("Selecione o motivo"), + "sendEmail": MessageLookupByLibrary.simpleMessage("Enviar email"), + "setupComplete": + MessageLookupByLibrary.simpleMessage("Configuração concluída"), + "somethingWentWrongPleaseTryAgain": + MessageLookupByLibrary.simpleMessage( + "Algo deu errado. Por favor, tente outra vez"), + "sorry": MessageLookupByLibrary.simpleMessage("Desculpe"), + "tapToCopy": MessageLookupByLibrary.simpleMessage("toque para copiar"), + "terminate": MessageLookupByLibrary.simpleMessage("Terminar"), + "terminateSession": + MessageLookupByLibrary.simpleMessage("Encerrar sessão?"), + "thisCanBeUsedToRecoverYourAccountIfYou": + MessageLookupByLibrary.simpleMessage( + "Isso pode ser usado para recuperar sua conta se você perder seu segundo fator"), + "thisDevice": MessageLookupByLibrary.simpleMessage("Este aparelho"), + "thisWillLogYouOutOfTheFollowingDevice": + MessageLookupByLibrary.simpleMessage( + "Isso fará com que você saia do seguinte dispositivo:"), + "thisWillLogYouOutOfThisDevice": MessageLookupByLibrary.simpleMessage( + "Isso fará com que você saia deste dispositivo!"), + "tryAgain": MessageLookupByLibrary.simpleMessage("Tente novamente"), + "twofactorAuthenticationPageTitle": + MessageLookupByLibrary.simpleMessage( + "Autenticação de dois fatores"), + "verify": MessageLookupByLibrary.simpleMessage("Verificar"), + "verifyEmail": MessageLookupByLibrary.simpleMessage("Verificar email"), + "verifyingRecoveryKey": MessageLookupByLibrary.simpleMessage( + "Verificando chave de recuperação..."), + "viewRecoveryKey": + MessageLookupByLibrary.simpleMessage("Ver chave de recuperação"), + "viewer": MessageLookupByLibrary.simpleMessage("Visualizador"), + "yesConvertToViewer": MessageLookupByLibrary.simpleMessage( + "Sim, converter para visualizador"), + "you": MessageLookupByLibrary.simpleMessage("Você"), + "yourAccountHasBeenDeleted": + MessageLookupByLibrary.simpleMessage("Sua conta foi deletada") + }; +} diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart new file mode 100644 index 000000000..e1ae2fbb4 --- /dev/null +++ b/lib/generated/l10n.dart @@ -0,0 +1,6686 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'intl/messages_all.dart'; + +// ************************************************************************** +// Generator: Flutter Intl IDE plugin +// Made by Localizely +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars +// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each +// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes + +class S { + S(); + + static S? _current; + + static S get current { + assert(_current != null, + 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); + return _current!; + } + + static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); + + static Future load(Locale locale) { + final name = (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); + final localeName = Intl.canonicalizedLocale(name); + return initializeMessages(localeName).then((_) { + Intl.defaultLocale = localeName; + final instance = S(); + S._current = instance; + + return instance; + }); + } + + static S of(BuildContext context) { + final instance = S.maybeOf(context); + assert(instance != null, + 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); + return instance!; + } + + static S? maybeOf(BuildContext context) { + return Localizations.of(context, S); + } + + /// `Enter your email address` + String get enterYourEmailAddress { + return Intl.message( + 'Enter your email address', + name: 'enterYourEmailAddress', + desc: '', + args: [], + ); + } + + /// `Welcome back!` + String get accountWelcomeBack { + return Intl.message( + 'Welcome back!', + name: 'accountWelcomeBack', + desc: '', + args: [], + ); + } + + /// `Email` + String get email { + return Intl.message( + 'Email', + name: 'email', + desc: '', + args: [], + ); + } + + /// `Cancel` + String get cancel { + return Intl.message( + 'Cancel', + name: 'cancel', + desc: '', + args: [], + ); + } + + /// `Verify` + String get verify { + return Intl.message( + 'Verify', + name: 'verify', + desc: '', + args: [], + ); + } + + /// `Invalid email address` + String get invalidEmailAddress { + return Intl.message( + 'Invalid email address', + name: 'invalidEmailAddress', + desc: '', + args: [], + ); + } + + /// `Please enter a valid email address.` + String get enterValidEmail { + return Intl.message( + 'Please enter a valid email address.', + name: 'enterValidEmail', + desc: '', + args: [], + ); + } + + /// `Delete account` + String get deleteAccount { + return Intl.message( + 'Delete account', + name: 'deleteAccount', + desc: '', + args: [], + ); + } + + /// `What is the main reason you are deleting your account?` + String get askDeleteReason { + return Intl.message( + 'What is the main reason you are deleting your account?', + name: 'askDeleteReason', + desc: '', + args: [], + ); + } + + /// `We are sorry to see you go. Please share your feedback to help us improve.` + String get deleteAccountFeedbackPrompt { + return Intl.message( + 'We are sorry to see you go. Please share your feedback to help us improve.', + name: 'deleteAccountFeedbackPrompt', + desc: '', + args: [], + ); + } + + /// `Feedback` + String get feedback { + return Intl.message( + 'Feedback', + name: 'feedback', + desc: '', + args: [], + ); + } + + /// `Kindly help us with this information` + String get kindlyHelpUsWithThisInformation { + return Intl.message( + 'Kindly help us with this information', + name: 'kindlyHelpUsWithThisInformation', + desc: '', + args: [], + ); + } + + /// `Yes, I want to permanently delete this account and all its data.` + String get confirmDeletePrompt { + return Intl.message( + 'Yes, I want to permanently delete this account and all its data.', + name: 'confirmDeletePrompt', + desc: '', + args: [], + ); + } + + /// `Confirm Account Deletion` + String get confirmAccountDeletion { + return Intl.message( + 'Confirm Account Deletion', + name: 'confirmAccountDeletion', + desc: '', + args: [], + ); + } + + /// `You are about to permanently delete your account and all its data.\nThis action is irreversible.` + String get deleteConfirmDialogBody { + return Intl.message( + 'You are about to permanently delete your account and all its data.\nThis action is irreversible.', + name: 'deleteConfirmDialogBody', + desc: '', + args: [], + ); + } + + /// `Delete Account Permanently` + String get deleteAccountPermanentlyButton { + return Intl.message( + 'Delete Account Permanently', + name: 'deleteAccountPermanentlyButton', + desc: '', + args: [], + ); + } + + /// `Your account has been deleted` + String get yourAccountHasBeenDeleted { + return Intl.message( + 'Your account has been deleted', + name: 'yourAccountHasBeenDeleted', + desc: '', + args: [], + ); + } + + /// `Select reason` + String get selectReason { + return Intl.message( + 'Select reason', + name: 'selectReason', + desc: '', + args: [], + ); + } + + /// `It’s missing a key feature that I need` + String get deleteReason1 { + return Intl.message( + 'It’s missing a key feature that I need', + name: 'deleteReason1', + desc: '', + args: [], + ); + } + + /// `The app or a certain feature does not \nbehave as I think it should` + String get deleteReason2 { + return Intl.message( + 'The app or a certain feature does not \nbehave as I think it should', + name: 'deleteReason2', + desc: '', + args: [], + ); + } + + /// `I found another service that I like better` + String get deleteReason3 { + return Intl.message( + 'I found another service that I like better', + name: 'deleteReason3', + desc: '', + args: [], + ); + } + + /// `My reason isn’t listed` + String get deleteReason4 { + return Intl.message( + 'My reason isn’t listed', + name: 'deleteReason4', + desc: '', + args: [], + ); + } + + /// `Send email` + String get sendEmail { + return Intl.message( + 'Send email', + name: 'sendEmail', + desc: '', + args: [], + ); + } + + /// `Your request will be processed within 72 hours.` + String get deleteRequestSLAText { + return Intl.message( + 'Your request will be processed within 72 hours.', + name: 'deleteRequestSLAText', + desc: '', + args: [], + ); + } + + /// `Please send an email to account-deletion@ente.io from your registered email address.` + String get deleteEmailRequest { + return Intl.message( + 'Please send an email to account-deletion@ente.io from your registered email address.', + name: 'deleteEmailRequest', + desc: '', + args: [], + ); + } + + /// `Ok` + String get ok { + return Intl.message( + 'Ok', + name: 'ok', + desc: '', + args: [], + ); + } + + /// `Create account` + String get createAccount { + return Intl.message( + 'Create account', + name: 'createAccount', + desc: '', + args: [], + ); + } + + /// `Create new account` + String get createNewAccount { + return Intl.message( + 'Create new account', + name: 'createNewAccount', + desc: '', + args: [], + ); + } + + /// `Password` + String get password { + return Intl.message( + 'Password', + name: 'password', + desc: '', + args: [], + ); + } + + /// `Confirm password` + String get confirmPassword { + return Intl.message( + 'Confirm password', + name: 'confirmPassword', + desc: '', + args: [], + ); + } + + /// `Active sessions` + String get activeSessions { + return Intl.message( + 'Active sessions', + name: 'activeSessions', + desc: '', + args: [], + ); + } + + /// `Oops` + String get oops { + return Intl.message( + 'Oops', + name: 'oops', + desc: '', + args: [], + ); + } + + /// `Something went wrong, please try again` + String get somethingWentWrongPleaseTryAgain { + return Intl.message( + 'Something went wrong, please try again', + name: 'somethingWentWrongPleaseTryAgain', + desc: '', + args: [], + ); + } + + /// `This will log you out of this device!` + String get thisWillLogYouOutOfThisDevice { + return Intl.message( + 'This will log you out of this device!', + name: 'thisWillLogYouOutOfThisDevice', + desc: '', + args: [], + ); + } + + /// `This will log you out of the following device:` + String get thisWillLogYouOutOfTheFollowingDevice { + return Intl.message( + 'This will log you out of the following device:', + name: 'thisWillLogYouOutOfTheFollowingDevice', + desc: '', + args: [], + ); + } + + /// `Terminate session?` + String get terminateSession { + return Intl.message( + 'Terminate session?', + name: 'terminateSession', + desc: '', + args: [], + ); + } + + /// `Terminate` + String get terminate { + return Intl.message( + 'Terminate', + name: 'terminate', + desc: '', + args: [], + ); + } + + /// `This device` + String get thisDevice { + return Intl.message( + 'This device', + name: 'thisDevice', + desc: '', + args: [], + ); + } + + /// `Recover` + String get recoverButton { + return Intl.message( + 'Recover', + name: 'recoverButton', + desc: '', + args: [], + ); + } + + /// `Recovery successful!` + String get recoverySuccessful { + return Intl.message( + 'Recovery successful!', + name: 'recoverySuccessful', + desc: '', + args: [], + ); + } + + /// `Decrypting...` + String get decrypting { + return Intl.message( + 'Decrypting...', + name: 'decrypting', + desc: '', + args: [], + ); + } + + /// `Incorrect recovery key` + String get incorrectRecoveryKeyTitle { + return Intl.message( + 'Incorrect recovery key', + name: 'incorrectRecoveryKeyTitle', + desc: '', + args: [], + ); + } + + /// `The recovery key you entered is incorrect` + String get incorrectRecoveryKeyBody { + return Intl.message( + 'The recovery key you entered is incorrect', + name: 'incorrectRecoveryKeyBody', + desc: '', + args: [], + ); + } + + /// `Forgot password` + String get forgotPassword { + return Intl.message( + 'Forgot password', + name: 'forgotPassword', + desc: '', + args: [], + ); + } + + /// `Enter your recovery key` + String get enterYourRecoveryKey { + return Intl.message( + 'Enter your recovery key', + name: 'enterYourRecoveryKey', + desc: '', + args: [], + ); + } + + /// `No recovery key?` + String get noRecoveryKey { + return Intl.message( + 'No recovery key?', + name: 'noRecoveryKey', + desc: '', + args: [], + ); + } + + /// `Sorry` + String get sorry { + return Intl.message( + 'Sorry', + name: 'sorry', + desc: '', + args: [], + ); + } + + /// `Due to the nature of our end-to-end encryption protocol, your data cannot be decrypted without your password or recovery key` + String get noRecoveryKeyNoDecryption { + return Intl.message( + 'Due to the nature of our end-to-end encryption protocol, your data cannot be decrypted without your password or recovery key', + name: 'noRecoveryKeyNoDecryption', + desc: '', + args: [], + ); + } + + /// `Verify email` + String get verifyEmail { + return Intl.message( + 'Verify email', + name: 'verifyEmail', + desc: '', + args: [], + ); + } + + /// `Please check your inbox (and spam) to complete verification` + String get checkInboxAndSpamFolder { + return Intl.message( + 'Please check your inbox (and spam) to complete verification', + name: 'checkInboxAndSpamFolder', + desc: '', + args: [], + ); + } + + /// `Tap to enter code` + String get tapToEnterCode { + return Intl.message( + 'Tap to enter code', + name: 'tapToEnterCode', + desc: '', + args: [], + ); + } + + /// `Resend email` + String get resendEmail { + return Intl.message( + 'Resend email', + name: 'resendEmail', + desc: '', + args: [], + ); + } + + /// `We've sent a mail to` + String get weveSentAMailTo { + return Intl.message( + 'We\'ve sent a mail to', + name: 'weveSentAMailTo', + desc: '', + args: [], + ); + } + + /// `Set password` + String get setPasswordTitle { + return Intl.message( + 'Set password', + name: 'setPasswordTitle', + desc: '', + args: [], + ); + } + + /// `Change password` + String get changePasswordTitle { + return Intl.message( + 'Change password', + name: 'changePasswordTitle', + desc: '', + args: [], + ); + } + + /// `Reset password` + String get resetPasswordTitle { + return Intl.message( + 'Reset password', + name: 'resetPasswordTitle', + desc: '', + args: [], + ); + } + + /// `Encryption keys` + String get encryptionKeys { + return Intl.message( + 'Encryption keys', + name: 'encryptionKeys', + desc: '', + args: [], + ); + } + + /// `We don't store this password, so if you forget, we cannot decrypt your data` + String get passwordWarning { + return Intl.message( + 'We don\'t store this password, so if you forget, we cannot decrypt your data', + name: 'passwordWarning', + desc: '', + args: [], + ); + } + + /// `Enter a password we can use to encrypt your data` + String get enterPasswordToEncrypt { + return Intl.message( + 'Enter a password we can use to encrypt your data', + name: 'enterPasswordToEncrypt', + desc: '', + args: [], + ); + } + + /// `Enter a new password we can use to encrypt your data` + String get enterNewPasswordToEncrypt { + return Intl.message( + 'Enter a new password we can use to encrypt your data', + name: 'enterNewPasswordToEncrypt', + desc: '', + args: [], + ); + } + + /// `Weak` + String get weakStrength { + return Intl.message( + 'Weak', + name: 'weakStrength', + desc: '', + args: [], + ); + } + + /// `Strong` + String get strongStrength { + return Intl.message( + 'Strong', + name: 'strongStrength', + desc: '', + args: [], + ); + } + + /// `Moderate` + String get moderateStrength { + return Intl.message( + 'Moderate', + name: 'moderateStrength', + desc: '', + args: [], + ); + } + + /// `Password strength: {passwordStrengthValue}` + String passwordStrength(String passwordStrengthValue) { + return Intl.message( + 'Password strength: $passwordStrengthValue', + name: 'passwordStrength', + desc: 'Text to indicate the password strength', + args: [passwordStrengthValue], + ); + } + + /// `Password changed successfully` + String get passwordChangedSuccessfully { + return Intl.message( + 'Password changed successfully', + name: 'passwordChangedSuccessfully', + desc: '', + args: [], + ); + } + + /// `Generating encryption keys...` + String get generatingEncryptionKeys { + return Intl.message( + 'Generating encryption keys...', + name: 'generatingEncryptionKeys', + desc: '', + args: [], + ); + } + + /// `Please wait...` + String get pleaseWait { + return Intl.message( + 'Please wait...', + name: 'pleaseWait', + desc: '', + args: [], + ); + } + + /// `Continue` + String get continueLabel { + return Intl.message( + 'Continue', + name: 'continueLabel', + desc: '', + args: [], + ); + } + + /// `Insecure device` + String get insecureDevice { + return Intl.message( + 'Insecure device', + name: 'insecureDevice', + desc: '', + args: [], + ); + } + + /// `Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.` + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease { + return Intl.message( + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.', + name: 'sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease', + desc: '', + args: [], + ); + } + + /// `How it works` + String get howItWorks { + return Intl.message( + 'How it works', + name: 'howItWorks', + desc: '', + args: [], + ); + } + + /// `Encryption` + String get encryption { + return Intl.message( + 'Encryption', + name: 'encryption', + desc: '', + args: [], + ); + } + + /// `I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.` + String get ackPasswordLostWarning { + return Intl.message( + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.', + name: 'ackPasswordLostWarning', + desc: '', + args: [], + ); + } + + /// `Privacy Policy` + String get privacyPolicyTitle { + return Intl.message( + 'Privacy Policy', + name: 'privacyPolicyTitle', + desc: '', + args: [], + ); + } + + /// `Terms` + String get termsOfServicesTitle { + return Intl.message( + 'Terms', + name: 'termsOfServicesTitle', + desc: '', + args: [], + ); + } + + /// `I agree to the terms of service and privacy policy` + String get signUpTerms { + return Intl.message( + 'I agree to the terms of service and privacy policy', + name: 'signUpTerms', + desc: '', + args: [], + ); + } + + /// `Log in` + String get logInLabel { + return Intl.message( + 'Log in', + name: 'logInLabel', + desc: '', + args: [], + ); + } + + /// `By clicking log in, I agree to the terms of service and privacy policy` + String get loginTerms { + return Intl.message( + 'By clicking log in, I agree to the terms of service and privacy policy', + name: 'loginTerms', + desc: '', + args: [], + ); + } + + /// `Change email` + String get changeEmail { + return Intl.message( + 'Change email', + name: 'changeEmail', + desc: '', + args: [], + ); + } + + /// `Enter your password` + String get enterYourPassword { + return Intl.message( + 'Enter your password', + name: 'enterYourPassword', + desc: '', + args: [], + ); + } + + /// `Welcome back!` + String get welcomeBack { + return Intl.message( + 'Welcome back!', + name: 'welcomeBack', + desc: '', + args: [], + ); + } + + /// `Contact support` + String get contactSupport { + return Intl.message( + 'Contact support', + name: 'contactSupport', + desc: '', + args: [], + ); + } + + /// `Incorrect password` + String get incorrectPasswordTitle { + return Intl.message( + 'Incorrect password', + name: 'incorrectPasswordTitle', + desc: '', + args: [], + ); + } + + /// `Please try again` + String get pleaseTryAgain { + return Intl.message( + 'Please try again', + name: 'pleaseTryAgain', + desc: '', + args: [], + ); + } + + /// `Recreate password` + String get recreatePasswordTitle { + return Intl.message( + 'Recreate password', + name: 'recreatePasswordTitle', + desc: '', + args: [], + ); + } + + /// `Use recovery key` + String get useRecoveryKey { + return Intl.message( + 'Use recovery key', + name: 'useRecoveryKey', + desc: '', + args: [], + ); + } + + /// `The current device is not powerful enough to verify your password, but we can regenerate in a way that works with all devices.\n\nPlease login using your recovery key and regenerate your password (you can use the same one again if you wish).` + String get recreatePasswordBody { + return Intl.message( + 'The current device is not powerful enough to verify your password, but we can regenerate in a way that works with all devices.\n\nPlease login using your recovery key and regenerate your password (you can use the same one again if you wish).', + name: 'recreatePasswordBody', + desc: '', + args: [], + ); + } + + /// `Verify password` + String get verifyPassword { + return Intl.message( + 'Verify password', + name: 'verifyPassword', + desc: '', + args: [], + ); + } + + /// `Recovery key` + String get recoveryKey { + return Intl.message( + 'Recovery key', + name: 'recoveryKey', + desc: '', + args: [], + ); + } + + /// `If you forget your password, the only way you can recover your data is with this key.` + String get recoveryKeyOnForgotPassword { + return Intl.message( + 'If you forget your password, the only way you can recover your data is with this key.', + name: 'recoveryKeyOnForgotPassword', + desc: '', + args: [], + ); + } + + /// `We don't store this key, please save this 24 word key in a safe place.` + String get recoveryKeySaveDescription { + return Intl.message( + 'We don\'t store this key, please save this 24 word key in a safe place.', + name: 'recoveryKeySaveDescription', + desc: '', + args: [], + ); + } + + /// `Do this later` + String get doThisLater { + return Intl.message( + 'Do this later', + name: 'doThisLater', + desc: '', + args: [], + ); + } + + /// `Save key` + String get saveKey { + return Intl.message( + 'Save key', + name: 'saveKey', + desc: '', + args: [], + ); + } + + /// `Recovery key copied to clipboard` + String get recoveryKeyCopiedToClipboard { + return Intl.message( + 'Recovery key copied to clipboard', + name: 'recoveryKeyCopiedToClipboard', + desc: '', + args: [], + ); + } + + /// `Recover account` + String get recoverAccount { + return Intl.message( + 'Recover account', + name: 'recoverAccount', + desc: '', + args: [], + ); + } + + /// `Recover` + String get recover { + return Intl.message( + 'Recover', + name: 'recover', + desc: '', + args: [], + ); + } + + /// `Please drop an email to {supportEmail} from your registered email address` + String dropSupportEmail(String supportEmail) { + return Intl.message( + 'Please drop an email to $supportEmail from your registered email address', + name: 'dropSupportEmail', + desc: '', + args: [supportEmail], + ); + } + + /// `Two-factor setup` + String get twofactorSetup { + return Intl.message( + 'Two-factor setup', + name: 'twofactorSetup', + desc: '', + args: [], + ); + } + + /// `Enter code` + String get enterCode { + return Intl.message( + 'Enter code', + name: 'enterCode', + desc: '', + args: [], + ); + } + + /// `Scan code` + String get scanCode { + return Intl.message( + 'Scan code', + name: 'scanCode', + desc: '', + args: [], + ); + } + + /// `Code copied to clipboard` + String get codeCopiedToClipboard { + return Intl.message( + 'Code copied to clipboard', + name: 'codeCopiedToClipboard', + desc: '', + args: [], + ); + } + + /// `Copy-paste this code\nto your authenticator app` + String get copypasteThisCodentoYourAuthenticatorApp { + return Intl.message( + 'Copy-paste this code\nto your authenticator app', + name: 'copypasteThisCodentoYourAuthenticatorApp', + desc: '', + args: [], + ); + } + + /// `tap to copy` + String get tapToCopy { + return Intl.message( + 'tap to copy', + name: 'tapToCopy', + desc: '', + args: [], + ); + } + + /// `Scan this barcode with\nyour authenticator app` + String get scanThisBarcodeWithnyourAuthenticatorApp { + return Intl.message( + 'Scan this barcode with\nyour authenticator app', + name: 'scanThisBarcodeWithnyourAuthenticatorApp', + desc: '', + args: [], + ); + } + + /// `Enter the 6-digit code from\nyour authenticator app` + String get enterThe6digitCodeFromnyourAuthenticatorApp { + return Intl.message( + 'Enter the 6-digit code from\nyour authenticator app', + name: 'enterThe6digitCodeFromnyourAuthenticatorApp', + desc: '', + args: [], + ); + } + + /// `Confirm` + String get confirm { + return Intl.message( + 'Confirm', + name: 'confirm', + desc: '', + args: [], + ); + } + + /// `Setup complete` + String get setupComplete { + return Intl.message( + 'Setup complete', + name: 'setupComplete', + desc: '', + args: [], + ); + } + + /// `Save your recovery key if you haven't already` + String get saveYourRecoveryKeyIfYouHaventAlready { + return Intl.message( + 'Save your recovery key if you haven\'t already', + name: 'saveYourRecoveryKeyIfYouHaventAlready', + desc: '', + args: [], + ); + } + + /// `This can be used to recover your account if you lose your second factor` + String get thisCanBeUsedToRecoverYourAccountIfYou { + return Intl.message( + 'This can be used to recover your account if you lose your second factor', + name: 'thisCanBeUsedToRecoverYourAccountIfYou', + desc: '', + args: [], + ); + } + + /// `Two-factor authentication` + String get twofactorAuthenticationPageTitle { + return Intl.message( + 'Two-factor authentication', + name: 'twofactorAuthenticationPageTitle', + desc: '', + args: [], + ); + } + + /// `Lost device?` + String get lostDevice { + return Intl.message( + 'Lost device?', + name: 'lostDevice', + desc: '', + args: [], + ); + } + + /// `Verifying recovery key...` + String get verifyingRecoveryKey { + return Intl.message( + 'Verifying recovery key...', + name: 'verifyingRecoveryKey', + desc: '', + args: [], + ); + } + + /// `Recovery key verified` + String get recoveryKeyVerified { + return Intl.message( + 'Recovery key verified', + name: 'recoveryKeyVerified', + desc: '', + args: [], + ); + } + + /// `Great! Your recovery key is valid. Thank you for verifying.\n\nPlease remember to keep your recovery key safely backed up.` + String get recoveryKeySuccessBody { + return Intl.message( + 'Great! Your recovery key is valid. Thank you for verifying.\n\nPlease remember to keep your recovery key safely backed up.', + name: 'recoveryKeySuccessBody', + desc: '', + args: [], + ); + } + + /// `The recovery key you entered is not valid. Please make sure it contains 24 words, and check the spelling of each.\n\nIf you entered an older recovery code, make sure it is 64 characters long, and check each of them.` + String get invalidRecoveryKey { + return Intl.message( + 'The recovery key you entered is not valid. Please make sure it contains 24 words, and check the spelling of each.\n\nIf you entered an older recovery code, make sure it is 64 characters long, and check each of them.', + name: 'invalidRecoveryKey', + desc: '', + args: [], + ); + } + + /// `Invalid key` + String get invalidKey { + return Intl.message( + 'Invalid key', + name: 'invalidKey', + desc: '', + args: [], + ); + } + + /// `Try again` + String get tryAgain { + return Intl.message( + 'Try again', + name: 'tryAgain', + desc: '', + args: [], + ); + } + + /// `View recovery key` + String get viewRecoveryKey { + return Intl.message( + 'View recovery key', + name: 'viewRecoveryKey', + desc: '', + args: [], + ); + } + + /// `Confirm recovery key` + String get confirmRecoveryKey { + return Intl.message( + 'Confirm recovery key', + name: 'confirmRecoveryKey', + desc: '', + args: [], + ); + } + + /// `Your recovery key is the only way to recover your photos if you forget your password. You can find your recovery key in Settings > Account.\n\nPlease enter your recovery key here to verify that you have saved it correctly.` + String get recoveryKeyVerifyReason { + return Intl.message( + 'Your recovery key is the only way to recover your photos if you forget your password. You can find your recovery key in Settings > Account.\n\nPlease enter your recovery key here to verify that you have saved it correctly.', + name: 'recoveryKeyVerifyReason', + desc: '', + args: [], + ); + } + + /// `Confirm your recovery key` + String get confirmYourRecoveryKey { + return Intl.message( + 'Confirm your recovery key', + name: 'confirmYourRecoveryKey', + desc: '', + args: [], + ); + } + + /// `Add viewer` + String get addViewer { + return Intl.message( + 'Add viewer', + name: 'addViewer', + desc: '', + args: [], + ); + } + + /// `Add collaborator` + String get addCollaborator { + return Intl.message( + 'Add collaborator', + name: 'addCollaborator', + desc: '', + args: [], + ); + } + + /// `Add a new email` + String get addANewEmail { + return Intl.message( + 'Add a new email', + name: 'addANewEmail', + desc: '', + args: [], + ); + } + + /// `Or pick an existing one` + String get orPickAnExistingOne { + return Intl.message( + 'Or pick an existing one', + name: 'orPickAnExistingOne', + desc: '', + args: [], + ); + } + + /// `Collaborators can add photos and videos to the shared album.` + String get collaboratorsCanAddPhotosAndVideosToTheSharedAlbum { + return Intl.message( + 'Collaborators can add photos and videos to the shared album.', + name: 'collaboratorsCanAddPhotosAndVideosToTheSharedAlbum', + desc: '', + args: [], + ); + } + + /// `Enter email` + String get enterEmail { + return Intl.message( + 'Enter email', + name: 'enterEmail', + desc: '', + args: [], + ); + } + + /// `Owner` + String get albumOwner { + return Intl.message( + 'Owner', + name: 'albumOwner', + desc: 'Role of the album owner', + args: [], + ); + } + + /// `You` + String get you { + return Intl.message( + 'You', + name: 'you', + desc: '', + args: [], + ); + } + + /// `Collaborator` + String get collaborator { + return Intl.message( + 'Collaborator', + name: 'collaborator', + desc: '', + args: [], + ); + } + + /// `Add more` + String get addMore { + return Intl.message( + 'Add more', + name: 'addMore', + desc: 'Button text to add more collaborators/viewers', + args: [], + ); + } + + /// `Viewer` + String get viewer { + return Intl.message( + 'Viewer', + name: 'viewer', + desc: '', + args: [], + ); + } + + /// `Remove` + String get remove { + return Intl.message( + 'Remove', + name: 'remove', + desc: '', + args: [], + ); + } + + /// `Remove participant` + String get removeParticipant { + return Intl.message( + 'Remove participant', + name: 'removeParticipant', + desc: 'menuSectionTitle for removing a participant', + args: [], + ); + } + + /// `Manage` + String get manage { + return Intl.message( + 'Manage', + name: 'manage', + desc: '', + args: [], + ); + } + + /// `Added as` + String get addedAs { + return Intl.message( + 'Added as', + name: 'addedAs', + desc: '', + args: [], + ); + } + + /// `Change permissions?` + String get changePermissions { + return Intl.message( + 'Change permissions?', + name: 'changePermissions', + desc: '', + args: [], + ); + } + + /// `Yes, convert to viewer` + String get yesConvertToViewer { + return Intl.message( + 'Yes, convert to viewer', + name: 'yesConvertToViewer', + desc: '', + args: [], + ); + } + + /// `{user} will not be able to add more photos to this album\n\nThey will still be able to remove existing photos added by them` + String cannotAddMorePhotosAfterBecomingViewer(Object user) { + return Intl.message( + '$user will not be able to add more photos to this album\n\nThey will still be able to remove existing photos added by them', + name: 'cannotAddMorePhotosAfterBecomingViewer', + desc: '', + args: [user], + ); + } + + /// `Allow adding photos` + String get allowAddingPhotos { + return Intl.message( + 'Allow adding photos', + name: 'allowAddingPhotos', + desc: 'Switch button to enable uploading photos to a public link', + args: [], + ); + } + + /// `Allow people with the link to also add photos to the shared album.` + String get allowAddPhotosDescription { + return Intl.message( + 'Allow people with the link to also add photos to the shared album.', + name: 'allowAddPhotosDescription', + desc: '', + args: [], + ); + } + + /// `Password lock` + String get passwordLock { + return Intl.message( + 'Password lock', + name: 'passwordLock', + desc: '', + args: [], + ); + } + + /// `Please note` + String get disableDownloadWarningTitle { + return Intl.message( + 'Please note', + name: 'disableDownloadWarningTitle', + desc: '', + args: [], + ); + } + + /// `Viewers can still take screenshots or save a copy of your photos using external tools` + String get disableDownloadWarningBody { + return Intl.message( + 'Viewers can still take screenshots or save a copy of your photos using external tools', + name: 'disableDownloadWarningBody', + desc: '', + args: [], + ); + } + + /// `Allow downloads` + String get allowDownloads { + return Intl.message( + 'Allow downloads', + name: 'allowDownloads', + desc: '', + args: [], + ); + } + + /// `Device limit` + String get linkDeviceLimit { + return Intl.message( + 'Device limit', + name: 'linkDeviceLimit', + desc: '', + args: [], + ); + } + + /// `Link expiry` + String get linkExpiry { + return Intl.message( + 'Link expiry', + name: 'linkExpiry', + desc: '', + args: [], + ); + } + + /// `Expired` + String get linkExpired { + return Intl.message( + 'Expired', + name: 'linkExpired', + desc: '', + args: [], + ); + } + + /// `Enabled` + String get linkEnabled { + return Intl.message( + 'Enabled', + name: 'linkEnabled', + desc: '', + args: [], + ); + } + + /// `Never` + String get linkNeverExpires { + return Intl.message( + 'Never', + name: 'linkNeverExpires', + desc: '', + args: [], + ); + } + + /// `This link has expired. Please select a new expiry time or disable link expiry.` + String get expiredLinkInfo { + return Intl.message( + 'This link has expired. Please select a new expiry time or disable link expiry.', + name: 'expiredLinkInfo', + desc: '', + args: [], + ); + } + + /// `Set a password` + String get setAPassword { + return Intl.message( + 'Set a password', + name: 'setAPassword', + desc: '', + args: [], + ); + } + + /// `Lock` + String get lockButtonLabel { + return Intl.message( + 'Lock', + name: 'lockButtonLabel', + desc: '', + args: [], + ); + } + + /// `Enter password` + String get enterPassword { + return Intl.message( + 'Enter password', + name: 'enterPassword', + desc: '', + args: [], + ); + } + + /// `Remove link` + String get removeLink { + return Intl.message( + 'Remove link', + name: 'removeLink', + desc: '', + args: [], + ); + } + + /// `Manage link` + String get manageLink { + return Intl.message( + 'Manage link', + name: 'manageLink', + desc: '', + args: [], + ); + } + + /// `Link will expire on {expiryTime}` + String linkExpiresOn(Object expiryTime) { + return Intl.message( + 'Link will expire on $expiryTime', + name: 'linkExpiresOn', + desc: '', + args: [expiryTime], + ); + } + + /// `Album updated` + String get albumUpdated { + return Intl.message( + 'Album updated', + name: 'albumUpdated', + desc: '', + args: [], + ); + } + + /// `When set to the maximum ({maxValue}), the device limit will be relaxed to allow for temporary spikes of large number of viewers.` + String maxDeviceLimitSpikeHandling(int maxValue) { + return Intl.message( + 'When set to the maximum ($maxValue), the device limit will be relaxed to allow for temporary spikes of large number of viewers.', + name: 'maxDeviceLimitSpikeHandling', + desc: '', + args: [maxValue], + ); + } + + /// `Never` + String get never { + return Intl.message( + 'Never', + name: 'never', + desc: '', + args: [], + ); + } + + /// `Custom` + String get custom { + return Intl.message( + 'Custom', + name: 'custom', + desc: 'Label for setting custom value for link expiry', + args: [], + ); + } + + /// `After 1 hour` + String get after1Hour { + return Intl.message( + 'After 1 hour', + name: 'after1Hour', + desc: '', + args: [], + ); + } + + /// `After 1 day` + String get after1Day { + return Intl.message( + 'After 1 day', + name: 'after1Day', + desc: '', + args: [], + ); + } + + /// `After 1 week` + String get after1Week { + return Intl.message( + 'After 1 week', + name: 'after1Week', + desc: '', + args: [], + ); + } + + /// `After 1 month` + String get after1Month { + return Intl.message( + 'After 1 month', + name: 'after1Month', + desc: '', + args: [], + ); + } + + /// `After 1 year` + String get after1Year { + return Intl.message( + 'After 1 year', + name: 'after1Year', + desc: '', + args: [], + ); + } + + /// `Manage` + String get manageParticipants { + return Intl.message( + 'Manage', + name: 'manageParticipants', + desc: '', + args: [], + ); + } + + /// `Create a link to allow people to add and view photos in your shared album without needing an ente app or account. Great for collecting event photos.` + String get collabLinkSectionDescription { + return Intl.message( + 'Create a link to allow people to add and view photos in your shared album without needing an ente app or account. Great for collecting event photos.', + name: 'collabLinkSectionDescription', + desc: '', + args: [], + ); + } + + /// `Collect photos` + String get collectPhotos { + return Intl.message( + 'Collect photos', + name: 'collectPhotos', + desc: '', + args: [], + ); + } + + /// `Collaborative link` + String get collaborativeLink { + return Intl.message( + 'Collaborative link', + name: 'collaborativeLink', + desc: '', + args: [], + ); + } + + /// `Share with non-ente users` + String get shareWithNonenteUsers { + return Intl.message( + 'Share with non-ente users', + name: 'shareWithNonenteUsers', + desc: '', + args: [], + ); + } + + /// `Create public link` + String get createPublicLink { + return Intl.message( + 'Create public link', + name: 'createPublicLink', + desc: '', + args: [], + ); + } + + /// `Send link` + String get sendLink { + return Intl.message( + 'Send link', + name: 'sendLink', + desc: '', + args: [], + ); + } + + /// `Copy link` + String get copyLink { + return Intl.message( + 'Copy link', + name: 'copyLink', + desc: '', + args: [], + ); + } + + /// `Link has expired` + String get linkHasExpired { + return Intl.message( + 'Link has expired', + name: 'linkHasExpired', + desc: '', + args: [], + ); + } + + /// `Public link enabled` + String get publicLinkEnabled { + return Intl.message( + 'Public link enabled', + name: 'publicLinkEnabled', + desc: '', + args: [], + ); + } + + /// `Share a link` + String get shareALink { + return Intl.message( + 'Share a link', + name: 'shareALink', + desc: '', + args: [], + ); + } + + /// `Create shared and collaborative albums with other ente users, including users on free plans.` + String get sharedAlbumSectionDescription { + return Intl.message( + 'Create shared and collaborative albums with other ente users, including users on free plans.', + name: 'sharedAlbumSectionDescription', + desc: '', + args: [], + ); + } + + /// `{numberOfPeople, plural, =0 {Share with specific people} =1 {Shared with 1 person} other {Shared with {numberOfPeople} people}}` + String shareWithPeopleSectionTitle(int numberOfPeople) { + return Intl.plural( + numberOfPeople, + zero: 'Share with specific people', + one: 'Shared with 1 person', + other: 'Shared with $numberOfPeople people', + name: 'shareWithPeopleSectionTitle', + desc: '', + args: [numberOfPeople], + ); + } + + /// `This is your Verification ID` + String get thisIsYourVerificationId { + return Intl.message( + 'This is your Verification ID', + name: 'thisIsYourVerificationId', + desc: '', + args: [], + ); + } + + /// `Someone sharing albums with you should see the same ID on their device.` + String get someoneSharingAlbumsWithYouShouldSeeTheSameId { + return Intl.message( + 'Someone sharing albums with you should see the same ID on their device.', + name: 'someoneSharingAlbumsWithYouShouldSeeTheSameId', + desc: '', + args: [], + ); + } + + /// `Please ask them to long-press their email address on the settings screen, and verify that the IDs on both devices match.` + String get howToViewShareeVerificationID { + return Intl.message( + 'Please ask them to long-press their email address on the settings screen, and verify that the IDs on both devices match.', + name: 'howToViewShareeVerificationID', + desc: '', + args: [], + ); + } + + /// `This is {email}'s Verification ID` + String thisIsPersonVerificationId(String email) { + return Intl.message( + 'This is $email\'s Verification ID', + name: 'thisIsPersonVerificationId', + desc: '', + args: [email], + ); + } + + /// `Verification ID` + String get verificationId { + return Intl.message( + 'Verification ID', + name: 'verificationId', + desc: '', + args: [], + ); + } + + /// `Verify {email}` + String verifyEmailID(Object email) { + return Intl.message( + 'Verify $email', + name: 'verifyEmailID', + desc: '', + args: [email], + ); + } + + /// `{email} does not have an ente account.\n\nSend them an invite to share photos.` + String emailNoEnteAccount(Object email) { + return Intl.message( + '$email does not have an ente account.\n\nSend them an invite to share photos.', + name: 'emailNoEnteAccount', + desc: '', + args: [email], + ); + } + + /// `Here's my verification ID: {verificationID} for ente.io.` + String shareMyVerificationID(Object verificationID) { + return Intl.message( + 'Here\'s my verification ID: $verificationID for ente.io.', + name: 'shareMyVerificationID', + desc: '', + args: [verificationID], + ); + } + + /// `Hey, can you confirm that this is your ente.io verification ID: {verificationID}` + String shareTextConfirmOthersVerificationID(Object verificationID) { + return Intl.message( + 'Hey, can you confirm that this is your ente.io verification ID: $verificationID', + name: 'shareTextConfirmOthersVerificationID', + desc: '', + args: [verificationID], + ); + } + + /// `Something went wrong` + String get somethingWentWrong { + return Intl.message( + 'Something went wrong', + name: 'somethingWentWrong', + desc: '', + args: [], + ); + } + + /// `Send invite` + String get sendInvite { + return Intl.message( + 'Send invite', + name: 'sendInvite', + desc: '', + args: [], + ); + } + + /// `Download ente so we can easily share original quality photos and videos\n\nhttps://ente.io/#download` + String get shareTextRecommendUsingEnte { + return Intl.message( + 'Download ente so we can easily share original quality photos and videos\n\nhttps://ente.io/#download', + name: 'shareTextRecommendUsingEnte', + desc: '', + args: [], + ); + } + + /// `Done` + String get done { + return Intl.message( + 'Done', + name: 'done', + desc: '', + args: [], + ); + } + + /// `Apply code` + String get applyCodeTitle { + return Intl.message( + 'Apply code', + name: 'applyCodeTitle', + desc: '', + args: [], + ); + } + + /// `Enter the code provided by your friend to claim free storage for both of you` + String get enterCodeDescription { + return Intl.message( + 'Enter the code provided by your friend to claim free storage for both of you', + name: 'enterCodeDescription', + desc: '', + args: [], + ); + } + + /// `Apply` + String get apply { + return Intl.message( + 'Apply', + name: 'apply', + desc: '', + args: [], + ); + } + + /// `Failed to apply code` + String get failedToApplyCode { + return Intl.message( + 'Failed to apply code', + name: 'failedToApplyCode', + desc: '', + args: [], + ); + } + + /// `Enter referral code` + String get enterReferralCode { + return Intl.message( + 'Enter referral code', + name: 'enterReferralCode', + desc: '', + args: [], + ); + } + + /// `Code applied` + String get codeAppliedPageTitle { + return Intl.message( + 'Code applied', + name: 'codeAppliedPageTitle', + desc: '', + args: [], + ); + } + + /// `{storageAmountInGB} GB` + String storageInGB(Object storageAmountInGB) { + return Intl.message( + '$storageAmountInGB GB', + name: 'storageInGB', + desc: '', + args: [storageAmountInGB], + ); + } + + /// `Claimed` + String get claimed { + return Intl.message( + 'Claimed', + name: 'claimed', + desc: 'Used to indicate storage claimed, like 10GB Claimed', + args: [], + ); + } + + /// `Details` + String get details { + return Intl.message( + 'Details', + name: 'details', + desc: '', + args: [], + ); + } + + /// `Claim more!` + String get claimMore { + return Intl.message( + 'Claim more!', + name: 'claimMore', + desc: '', + args: [], + ); + } + + /// `They also get {storageAmountInGB} GB` + String theyAlsoGetXGb(Object storageAmountInGB) { + return Intl.message( + 'They also get $storageAmountInGB GB', + name: 'theyAlsoGetXGb', + desc: '', + args: [storageAmountInGB], + ); + } + + /// `{storageAmountInGB} GB each time someone signs up for a paid plan and applies your code` + String freeStorageOnReferralSuccess(Object storageAmountInGB) { + return Intl.message( + '$storageAmountInGB GB each time someone signs up for a paid plan and applies your code', + name: 'freeStorageOnReferralSuccess', + desc: '', + args: [storageAmountInGB], + ); + } + + /// `ente referral code: {referralCode} \n\nApply it in Settings → General → Referrals to get {referralStorageInGB} GB free after you signup for a paid plan\n\nhttps://ente.io` + String shareTextReferralCode( + Object referralCode, Object referralStorageInGB) { + return Intl.message( + 'ente referral code: $referralCode \n\nApply it in Settings → General → Referrals to get $referralStorageInGB GB free after you signup for a paid plan\n\nhttps://ente.io', + name: 'shareTextReferralCode', + desc: '', + args: [referralCode, referralStorageInGB], + ); + } + + /// `Claim free storage` + String get claimFreeStorage { + return Intl.message( + 'Claim free storage', + name: 'claimFreeStorage', + desc: '', + args: [], + ); + } + + /// `Invite your friends` + String get inviteYourFriends { + return Intl.message( + 'Invite your friends', + name: 'inviteYourFriends', + desc: '', + args: [], + ); + } + + /// `Unable to fetch referral details. Please try again later.` + String get failedToFetchReferralDetails { + return Intl.message( + 'Unable to fetch referral details. Please try again later.', + name: 'failedToFetchReferralDetails', + desc: '', + args: [], + ); + } + + /// `1. Give this code to your friends` + String get referralStep1 { + return Intl.message( + '1. Give this code to your friends', + name: 'referralStep1', + desc: '', + args: [], + ); + } + + /// `2. They sign up for a paid plan` + String get referralStep2 { + return Intl.message( + '2. They sign up for a paid plan', + name: 'referralStep2', + desc: '', + args: [], + ); + } + + /// `3. Both of you get {storageInGB} GB* free` + String referralStep3(Object storageInGB) { + return Intl.message( + '3. Both of you get $storageInGB GB* free', + name: 'referralStep3', + desc: '', + args: [storageInGB], + ); + } + + /// `Referrals are currently paused` + String get referralsAreCurrentlyPaused { + return Intl.message( + 'Referrals are currently paused', + name: 'referralsAreCurrentlyPaused', + desc: '', + args: [], + ); + } + + /// `* You can at max double your storage` + String get youCanAtMaxDoubleYourStorage { + return Intl.message( + '* You can at max double your storage', + name: 'youCanAtMaxDoubleYourStorage', + desc: '', + args: [], + ); + } + + /// `{isFamilyMember, select, true {Your family has claimed {storageAmountInGb} Gb so far} false {You have claimed {storageAmountInGb} Gb so far} other {You have claimed {storageAmountInGb} Gb so far!}}` + String claimedStorageSoFar(String isFamilyMember, int storageAmountInGb) { + return Intl.select( + isFamilyMember, + { + 'true': 'Your family has claimed $storageAmountInGb Gb so far', + 'false': 'You have claimed $storageAmountInGb Gb so far', + 'other': 'You have claimed $storageAmountInGb Gb so far!', + }, + name: 'claimedStorageSoFar', + desc: '', + args: [isFamilyMember, storageAmountInGb], + ); + } + + /// `FAQ` + String get faq { + return Intl.message( + 'FAQ', + name: 'faq', + desc: '', + args: [], + ); + } + + /// `Oops, something went wrong` + String get oopsSomethingWentWrong { + return Intl.message( + 'Oops, something went wrong', + name: 'oopsSomethingWentWrong', + desc: '', + args: [], + ); + } + + /// `People using your code` + String get peopleUsingYourCode { + return Intl.message( + 'People using your code', + name: 'peopleUsingYourCode', + desc: '', + args: [], + ); + } + + /// `eligible` + String get eligible { + return Intl.message( + 'eligible', + name: 'eligible', + desc: '', + args: [], + ); + } + + /// `total` + String get total { + return Intl.message( + 'total', + name: 'total', + desc: '', + args: [], + ); + } + + /// `Code used by you` + String get codeUsedByYou { + return Intl.message( + 'Code used by you', + name: 'codeUsedByYou', + desc: '', + args: [], + ); + } + + /// `Free storage claimed` + String get freeStorageClaimed { + return Intl.message( + 'Free storage claimed', + name: 'freeStorageClaimed', + desc: '', + args: [], + ); + } + + /// `Free storage usable` + String get freeStorageUsable { + return Intl.message( + 'Free storage usable', + name: 'freeStorageUsable', + desc: '', + args: [], + ); + } + + /// `Usable storage is limited by your current plan. Excess claimed storage will automatically become usable when you upgrade your plan.` + String get usableReferralStorageInfo { + return Intl.message( + 'Usable storage is limited by your current plan. Excess claimed storage will automatically become usable when you upgrade your plan.', + name: 'usableReferralStorageInfo', + desc: '', + args: [], + ); + } + + /// `Remove from album?` + String get removeFromAlbumTitle { + return Intl.message( + 'Remove from album?', + name: 'removeFromAlbumTitle', + desc: '', + args: [], + ); + } + + /// `Remove from album` + String get removeFromAlbum { + return Intl.message( + 'Remove from album', + name: 'removeFromAlbum', + desc: '', + args: [], + ); + } + + /// `Selected items will be removed from this album` + String get itemsWillBeRemovedFromAlbum { + return Intl.message( + 'Selected items will be removed from this album', + name: 'itemsWillBeRemovedFromAlbum', + desc: '', + args: [], + ); + } + + /// `Some of the items you are removing were added by other people, and you will lose access to them` + String get removeShareItemsWarning { + return Intl.message( + 'Some of the items you are removing were added by other people, and you will lose access to them', + name: 'removeShareItemsWarning', + desc: '', + args: [], + ); + } + + /// `Adding to favorites...` + String get addingToFavorites { + return Intl.message( + 'Adding to favorites...', + name: 'addingToFavorites', + desc: '', + args: [], + ); + } + + /// `Removing from favorites...` + String get removingFromFavorites { + return Intl.message( + 'Removing from favorites...', + name: 'removingFromFavorites', + desc: '', + args: [], + ); + } + + /// `Sorry, could not add to favorites!` + String get sorryCouldNotAddToFavorites { + return Intl.message( + 'Sorry, could not add to favorites!', + name: 'sorryCouldNotAddToFavorites', + desc: '', + args: [], + ); + } + + /// `Sorry, could not remove from favorites!` + String get sorryCouldNotRemoveFromFavorites { + return Intl.message( + 'Sorry, could not remove from favorites!', + name: 'sorryCouldNotRemoveFromFavorites', + desc: '', + args: [], + ); + } + + /// `Looks like your subscription has expired. Please subscribe to enable sharing.` + String get subscribeToEnableSharing { + return Intl.message( + 'Looks like your subscription has expired. Please subscribe to enable sharing.', + name: 'subscribeToEnableSharing', + desc: '', + args: [], + ); + } + + /// `Subscribe` + String get subscribe { + return Intl.message( + 'Subscribe', + name: 'subscribe', + desc: '', + args: [], + ); + } + + /// `Can only remove files owned by you` + String get canOnlyRemoveFilesOwnedByYou { + return Intl.message( + 'Can only remove files owned by you', + name: 'canOnlyRemoveFilesOwnedByYou', + desc: '', + args: [], + ); + } + + /// `Delete shared album?` + String get deleteSharedAlbum { + return Intl.message( + 'Delete shared album?', + name: 'deleteSharedAlbum', + desc: '', + args: [], + ); + } + + /// `Delete album` + String get deleteAlbum { + return Intl.message( + 'Delete album', + name: 'deleteAlbum', + desc: '', + args: [], + ); + } + + /// `Also delete the photos (and videos) present in this album from all other albums they are part of?` + String get deleteAlbumDialog { + return Intl.message( + 'Also delete the photos (and videos) present in this album from all other albums they are part of?', + name: 'deleteAlbumDialog', + desc: '', + args: [], + ); + } + + /// `The album will be deleted for everyone\n\nYou will lose access to shared photos in this album that are owned by others` + String get deleteSharedAlbumDialogBody { + return Intl.message( + 'The album will be deleted for everyone\n\nYou will lose access to shared photos in this album that are owned by others', + name: 'deleteSharedAlbumDialogBody', + desc: '', + args: [], + ); + } + + /// `Yes, remove` + String get yesRemove { + return Intl.message( + 'Yes, remove', + name: 'yesRemove', + desc: '', + args: [], + ); + } + + /// `Creating link...` + String get creatingLink { + return Intl.message( + 'Creating link...', + name: 'creatingLink', + desc: '', + args: [], + ); + } + + /// `Remove?` + String get removeWithQuestionMark { + return Intl.message( + 'Remove?', + name: 'removeWithQuestionMark', + desc: '', + args: [], + ); + } + + /// `{userEmail} will be removed from this shared album\n\nAny photos added by them will also be removed from the album` + String removeParticipantBody(Object userEmail) { + return Intl.message( + '$userEmail will be removed from this shared album\n\nAny photos added by them will also be removed from the album', + name: 'removeParticipantBody', + desc: '', + args: [userEmail], + ); + } + + /// `Keep Photos` + String get keepPhotos { + return Intl.message( + 'Keep Photos', + name: 'keepPhotos', + desc: '', + args: [], + ); + } + + /// `Delete photos` + String get deletePhotos { + return Intl.message( + 'Delete photos', + name: 'deletePhotos', + desc: '', + args: [], + ); + } + + /// `Invite to ente` + String get inviteToEnte { + return Intl.message( + 'Invite to ente', + name: 'inviteToEnte', + desc: '', + args: [], + ); + } + + /// `Remove public link` + String get removePublicLink { + return Intl.message( + 'Remove public link', + name: 'removePublicLink', + desc: '', + args: [], + ); + } + + /// `This will remove the public link for accessing "{albumName}".` + String disableLinkMessage(Object albumName) { + return Intl.message( + 'This will remove the public link for accessing "$albumName".', + name: 'disableLinkMessage', + desc: '', + args: [albumName], + ); + } + + /// `Sharing...` + String get sharing { + return Intl.message( + 'Sharing...', + name: 'sharing', + desc: '', + args: [], + ); + } + + /// `You cannot share with yourself` + String get youCannotShareWithYourself { + return Intl.message( + 'You cannot share with yourself', + name: 'youCannotShareWithYourself', + desc: '', + args: [], + ); + } + + /// `Archive` + String get archive { + return Intl.message( + 'Archive', + name: 'archive', + desc: '', + args: [], + ); + } + + /// `Long press to select photos and click + to create an album` + String get createAlbumActionHint { + return Intl.message( + 'Long press to select photos and click + to create an album', + name: 'createAlbumActionHint', + desc: '', + args: [], + ); + } + + /// `Importing....` + String get importing { + return Intl.message( + 'Importing....', + name: 'importing', + desc: '', + args: [], + ); + } + + /// `Failed to load albums` + String get failedToLoadAlbums { + return Intl.message( + 'Failed to load albums', + name: 'failedToLoadAlbums', + desc: '', + args: [], + ); + } + + /// `Hidden` + String get hidden { + return Intl.message( + 'Hidden', + name: 'hidden', + desc: '', + args: [], + ); + } + + /// `Please authenticate to view your hidden files` + String get authToViewYourHiddenFiles { + return Intl.message( + 'Please authenticate to view your hidden files', + name: 'authToViewYourHiddenFiles', + desc: '', + args: [], + ); + } + + /// `Trash` + String get trash { + return Intl.message( + 'Trash', + name: 'trash', + desc: '', + args: [], + ); + } + + /// `Uncategorized` + String get uncategorized { + return Intl.message( + 'Uncategorized', + name: 'uncategorized', + desc: '', + args: [], + ); + } + + /// `video` + String get videoSmallCase { + return Intl.message( + 'video', + name: 'videoSmallCase', + desc: '', + args: [], + ); + } + + /// `photo` + String get photoSmallCase { + return Intl.message( + 'photo', + name: 'photoSmallCase', + desc: '', + args: [], + ); + } + + /// `It will be deleted from all albums.` + String get singleFileDeleteHighlight { + return Intl.message( + 'It will be deleted from all albums.', + name: 'singleFileDeleteHighlight', + desc: '', + args: [], + ); + } + + /// `This {fileType} is in both ente and your device.` + String singleFileInBothLocalAndRemote(Object fileType) { + return Intl.message( + 'This $fileType is in both ente and your device.', + name: 'singleFileInBothLocalAndRemote', + desc: '', + args: [fileType], + ); + } + + /// `This {fileType} will be deleted from ente.` + String singleFileInRemoteOnly(Object fileType) { + return Intl.message( + 'This $fileType will be deleted from ente.', + name: 'singleFileInRemoteOnly', + desc: '', + args: [fileType], + ); + } + + /// `This {fileType} will be deleted from your device.` + String singleFileDeleteFromDevice(Object fileType) { + return Intl.message( + 'This $fileType will be deleted from your device.', + name: 'singleFileDeleteFromDevice', + desc: '', + args: [fileType], + ); + } + + /// `Delete from ente` + String get deleteFromEnte { + return Intl.message( + 'Delete from ente', + name: 'deleteFromEnte', + desc: '', + args: [], + ); + } + + /// `Yes, delete` + String get yesDelete { + return Intl.message( + 'Yes, delete', + name: 'yesDelete', + desc: '', + args: [], + ); + } + + /// `Moved to trash` + String get movedToTrash { + return Intl.message( + 'Moved to trash', + name: 'movedToTrash', + desc: '', + args: [], + ); + } + + /// `Delete from device` + String get deleteFromDevice { + return Intl.message( + 'Delete from device', + name: 'deleteFromDevice', + desc: '', + args: [], + ); + } + + /// `Delete from both` + String get deleteFromBoth { + return Intl.message( + 'Delete from both', + name: 'deleteFromBoth', + desc: '', + args: [], + ); + } + + /// `New album` + String get newAlbum { + return Intl.message( + 'New album', + name: 'newAlbum', + desc: '', + args: [], + ); + } + + /// `Albums` + String get albums { + return Intl.message( + 'Albums', + name: 'albums', + desc: '', + args: [], + ); + } + + /// `{count, plural, zero{no memories} one{{count} memory} other{{count} memories}}` + String memoryCount(int count) { + return Intl.plural( + count, + zero: 'no memories', + one: '$count memory', + other: '$count memories', + name: 'memoryCount', + desc: 'The text to display the number of memories', + args: [count], + ); + } + + /// `{count} selected` + String selectedPhotos(int count) { + return Intl.message( + '$count selected', + name: 'selectedPhotos', + desc: 'Display the number of selected photos', + args: [count], + ); + } + + /// `{count} selected ({yourCount} yours)` + String selectedPhotosWithYours(int count, int yourCount) { + return Intl.message( + '$count selected ($yourCount yours)', + name: 'selectedPhotosWithYours', + desc: + 'Display the number of selected photos, including the number of selected photos owned by the user', + args: [count, yourCount], + ); + } + + /// `Advanced` + String get advancedSettings { + return Intl.message( + 'Advanced', + name: 'advancedSettings', + desc: 'The text to display in the advanced settings section', + args: [], + ); + } + + /// `Photo grid size` + String get photoGridSize { + return Intl.message( + 'Photo grid size', + name: 'photoGridSize', + desc: '', + args: [], + ); + } + + /// `Manage device storage` + String get manageDeviceStorage { + return Intl.message( + 'Manage device storage', + name: 'manageDeviceStorage', + desc: '', + args: [], + ); + } + + /// `Select folders for backup` + String get selectFoldersForBackup { + return Intl.message( + 'Select folders for backup', + name: 'selectFoldersForBackup', + desc: '', + args: [], + ); + } + + /// `Selected folders will be encrypted and backed up` + String get selectedFoldersWillBeEncryptedAndBackedUp { + return Intl.message( + 'Selected folders will be encrypted and backed up', + name: 'selectedFoldersWillBeEncryptedAndBackedUp', + desc: '', + args: [], + ); + } + + /// `Unselect all` + String get unselectAll { + return Intl.message( + 'Unselect all', + name: 'unselectAll', + desc: '', + args: [], + ); + } + + /// `Select all` + String get selectAll { + return Intl.message( + 'Select all', + name: 'selectAll', + desc: '', + args: [], + ); + } + + /// `Skip` + String get skip { + return Intl.message( + 'Skip', + name: 'skip', + desc: '', + args: [], + ); + } + + /// `Updating folder selection...` + String get updatingFolderSelection { + return Intl.message( + 'Updating folder selection...', + name: 'updatingFolderSelection', + desc: '', + args: [], + ); + } + + /// `{count, plural, one{{count} item} other{{count} items}}` + String itemCount(num count) { + return Intl.plural( + count, + one: '$count item', + other: '$count items', + name: 'itemCount', + desc: '', + args: [count], + ); + } + + /// `{count, plural, one{{count} year ago} other{{count} years ago}}` + String yearsAgo(num count) { + return Intl.plural( + count, + one: '$count year ago', + other: '$count years ago', + name: 'yearsAgo', + desc: '', + args: [count], + ); + } + + /// `Backup settings` + String get backupSettings { + return Intl.message( + 'Backup settings', + name: 'backupSettings', + desc: '', + args: [], + ); + } + + /// `Backup over mobile data` + String get backupOverMobileData { + return Intl.message( + 'Backup over mobile data', + name: 'backupOverMobileData', + desc: '', + args: [], + ); + } + + /// `Backup videos` + String get backupVideos { + return Intl.message( + 'Backup videos', + name: 'backupVideos', + desc: '', + args: [], + ); + } + + /// `Disable auto lock` + String get disableAutoLock { + return Intl.message( + 'Disable auto lock', + name: 'disableAutoLock', + desc: '', + args: [], + ); + } + + /// `Disable the device screen lock when ente is in the foreground and there is a backup in progress. This is normally not needed, but may help big uploads and initial imports of large libraries complete faster.` + String get deviceLockExplanation { + return Intl.message( + 'Disable the device screen lock when ente is in the foreground and there is a backup in progress. This is normally not needed, but may help big uploads and initial imports of large libraries complete faster.', + name: 'deviceLockExplanation', + desc: '', + args: [], + ); + } + + /// `About` + String get about { + return Intl.message( + 'About', + name: 'about', + desc: '', + args: [], + ); + } + + /// `We are open source!` + String get weAreOpenSource { + return Intl.message( + 'We are open source!', + name: 'weAreOpenSource', + desc: '', + args: [], + ); + } + + /// `Privacy` + String get privacy { + return Intl.message( + 'Privacy', + name: 'privacy', + desc: '', + args: [], + ); + } + + /// `Terms` + String get terms { + return Intl.message( + 'Terms', + name: 'terms', + desc: '', + args: [], + ); + } + + /// `Check for updates` + String get checkForUpdates { + return Intl.message( + 'Check for updates', + name: 'checkForUpdates', + desc: '', + args: [], + ); + } + + /// `Checking...` + String get checking { + return Intl.message( + 'Checking...', + name: 'checking', + desc: '', + args: [], + ); + } + + /// `You are on the latest version` + String get youAreOnTheLatestVersion { + return Intl.message( + 'You are on the latest version', + name: 'youAreOnTheLatestVersion', + desc: '', + args: [], + ); + } + + /// `Account` + String get account { + return Intl.message( + 'Account', + name: 'account', + desc: '', + args: [], + ); + } + + /// `Manage subscription` + String get manageSubscription { + return Intl.message( + 'Manage subscription', + name: 'manageSubscription', + desc: '', + args: [], + ); + } + + /// `Please authenticate to change your email` + String get authToChangeYourEmail { + return Intl.message( + 'Please authenticate to change your email', + name: 'authToChangeYourEmail', + desc: '', + args: [], + ); + } + + /// `Change password` + String get changePassword { + return Intl.message( + 'Change password', + name: 'changePassword', + desc: '', + args: [], + ); + } + + /// `Please authenticate to change your password` + String get authToChangeYourPassword { + return Intl.message( + 'Please authenticate to change your password', + name: 'authToChangeYourPassword', + desc: '', + args: [], + ); + } + + /// `Export your data` + String get exportYourData { + return Intl.message( + 'Export your data', + name: 'exportYourData', + desc: '', + args: [], + ); + } + + /// `Logout` + String get logout { + return Intl.message( + 'Logout', + name: 'logout', + desc: '', + args: [], + ); + } + + /// `Please authenticate to initiate account deletion` + String get authToInitiateAccountDeletion { + return Intl.message( + 'Please authenticate to initiate account deletion', + name: 'authToInitiateAccountDeletion', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to logout?` + String get areYouSureYouWantToLogout { + return Intl.message( + 'Are you sure you want to logout?', + name: 'areYouSureYouWantToLogout', + desc: '', + args: [], + ); + } + + /// `Yes, logout` + String get yesLogout { + return Intl.message( + 'Yes, logout', + name: 'yesLogout', + desc: '', + args: [], + ); + } + + /// `A new version of ente is available.` + String get aNewVersionOfEnteIsAvailable { + return Intl.message( + 'A new version of ente is available.', + name: 'aNewVersionOfEnteIsAvailable', + desc: '', + args: [], + ); + } + + /// `Update` + String get update { + return Intl.message( + 'Update', + name: 'update', + desc: '', + args: [], + ); + } + + /// `Install manually` + String get installManually { + return Intl.message( + 'Install manually', + name: 'installManually', + desc: '', + args: [], + ); + } + + /// `Critical update available` + String get criticalUpdateAvailable { + return Intl.message( + 'Critical update available', + name: 'criticalUpdateAvailable', + desc: '', + args: [], + ); + } + + /// `Update available` + String get updateAvailable { + return Intl.message( + 'Update available', + name: 'updateAvailable', + desc: '', + args: [], + ); + } + + /// `Downloading...` + String get downloading { + return Intl.message( + 'Downloading...', + name: 'downloading', + desc: '', + args: [], + ); + } + + /// `The download could not be completed` + String get theDownloadCouldNotBeCompleted { + return Intl.message( + 'The download could not be completed', + name: 'theDownloadCouldNotBeCompleted', + desc: '', + args: [], + ); + } + + /// `Retry` + String get retry { + return Intl.message( + 'Retry', + name: 'retry', + desc: '', + args: [], + ); + } + + /// `Backed up folders` + String get backedUpFolders { + return Intl.message( + 'Backed up folders', + name: 'backedUpFolders', + desc: '', + args: [], + ); + } + + /// `Backup` + String get backup { + return Intl.message( + 'Backup', + name: 'backup', + desc: '', + args: [], + ); + } + + /// `Free up device space` + String get freeUpDeviceSpace { + return Intl.message( + 'Free up device space', + name: 'freeUpDeviceSpace', + desc: '', + args: [], + ); + } + + /// `✨ All clear` + String get allClear { + return Intl.message( + '✨ All clear', + name: 'allClear', + desc: '', + args: [], + ); + } + + /// `You've no files on this device that can be deleted` + String get noDeviceThatCanBeDeleted { + return Intl.message( + 'You\'ve no files on this device that can be deleted', + name: 'noDeviceThatCanBeDeleted', + desc: '', + args: [], + ); + } + + /// `Remove duplicates` + String get removeDuplicates { + return Intl.message( + 'Remove duplicates', + name: 'removeDuplicates', + desc: '', + args: [], + ); + } + + /// `✨ No duplicates` + String get noDuplicates { + return Intl.message( + '✨ No duplicates', + name: 'noDuplicates', + desc: '', + args: [], + ); + } + + /// `You've no duplicate files that can be cleared` + String get youveNoDuplicateFilesThatCanBeCleared { + return Intl.message( + 'You\'ve no duplicate files that can be cleared', + name: 'youveNoDuplicateFilesThatCanBeCleared', + desc: '', + args: [], + ); + } + + /// `Success` + String get success { + return Intl.message( + 'Success', + name: 'success', + desc: '', + args: [], + ); + } + + /// `Rate us` + String get rateUs { + return Intl.message( + 'Rate us', + name: 'rateUs', + desc: '', + args: [], + ); + } + + /// `Also empty "Recently Deleted" from "Settings" -> "Storage" to claim the freed space` + String get remindToEmptyDeviceTrash { + return Intl.message( + 'Also empty "Recently Deleted" from "Settings" -> "Storage" to claim the freed space', + name: 'remindToEmptyDeviceTrash', + desc: '', + args: [], + ); + } + + /// `You have successfully freed up {storageSaved}!` + String youHaveSuccessfullyFreedUp(String storageSaved) { + return Intl.message( + 'You have successfully freed up $storageSaved!', + name: 'youHaveSuccessfullyFreedUp', + desc: + 'The text to display when the user has successfully freed up storage', + args: [storageSaved], + ); + } + + /// `Also empty your "Trash" to claim the freed up space` + String get remindToEmptyEnteTrash { + return Intl.message( + 'Also empty your "Trash" to claim the freed up space', + name: 'remindToEmptyEnteTrash', + desc: '', + args: [], + ); + } + + /// `✨ Success` + String get sparkleSuccess { + return Intl.message( + '✨ Success', + name: 'sparkleSuccess', + desc: '', + args: [], + ); + } + + /// `Your have cleaned up {count, plural, one{{count} duplicate file} other{{count} duplicate files}}, saving ({storageSaved}!)` + String duplicateFileCountWithStorageSaved(int count, String storageSaved) { + return Intl.message( + 'Your have cleaned up ${Intl.plural(count, one: '$count duplicate file', other: '$count duplicate files')}, saving ($storageSaved!)', + name: 'duplicateFileCountWithStorageSaved', + desc: + 'The text to display when the user has successfully cleaned up duplicate files', + args: [count, storageSaved], + ); + } + + /// `Family plans` + String get familyPlans { + return Intl.message( + 'Family plans', + name: 'familyPlans', + desc: '', + args: [], + ); + } + + /// `Referrals` + String get referrals { + return Intl.message( + 'Referrals', + name: 'referrals', + desc: '', + args: [], + ); + } + + /// `Advanced` + String get advanced { + return Intl.message( + 'Advanced', + name: 'advanced', + desc: '', + args: [], + ); + } + + /// `General` + String get general { + return Intl.message( + 'General', + name: 'general', + desc: '', + args: [], + ); + } + + /// `Security` + String get security { + return Intl.message( + 'Security', + name: 'security', + desc: '', + args: [], + ); + } + + /// `Please authenticate to view your recovery key` + String get authToViewYourRecoveryKey { + return Intl.message( + 'Please authenticate to view your recovery key', + name: 'authToViewYourRecoveryKey', + desc: '', + args: [], + ); + } + + /// `Two-factor` + String get twofactor { + return Intl.message( + 'Two-factor', + name: 'twofactor', + desc: '', + args: [], + ); + } + + /// `Please authenticate to configure two-factor authentication` + String get authToConfigureTwofactorAuthentication { + return Intl.message( + 'Please authenticate to configure two-factor authentication', + name: 'authToConfigureTwofactorAuthentication', + desc: '', + args: [], + ); + } + + /// `Lockscreen` + String get lockscreen { + return Intl.message( + 'Lockscreen', + name: 'lockscreen', + desc: '', + args: [], + ); + } + + /// `Please authenticate to change lockscreen setting` + String get authToChangeLockscreenSetting { + return Intl.message( + 'Please authenticate to change lockscreen setting', + name: 'authToChangeLockscreenSetting', + desc: '', + args: [], + ); + } + + /// `To enable lockscreen, please setup device passcode or screen lock in your system settings.` + String get lockScreenEnablePreSteps { + return Intl.message( + 'To enable lockscreen, please setup device passcode or screen lock in your system settings.', + name: 'lockScreenEnablePreSteps', + desc: '', + args: [], + ); + } + + /// `View active sessions` + String get viewActiveSessions { + return Intl.message( + 'View active sessions', + name: 'viewActiveSessions', + desc: '', + args: [], + ); + } + + /// `Please authenticate to view your active sessions` + String get authToViewYourActiveSessions { + return Intl.message( + 'Please authenticate to view your active sessions', + name: 'authToViewYourActiveSessions', + desc: '', + args: [], + ); + } + + /// `Disable two-factor` + String get disableTwofactor { + return Intl.message( + 'Disable two-factor', + name: 'disableTwofactor', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to disable two-factor authentication?` + String get confirm2FADisable { + return Intl.message( + 'Are you sure you want to disable two-factor authentication?', + name: 'confirm2FADisable', + desc: '', + args: [], + ); + } + + /// `No` + String get no { + return Intl.message( + 'No', + name: 'no', + desc: '', + args: [], + ); + } + + /// `Yes` + String get yes { + return Intl.message( + 'Yes', + name: 'yes', + desc: '', + args: [], + ); + } + + /// `Social` + String get social { + return Intl.message( + 'Social', + name: 'social', + desc: '', + args: [], + ); + } + + /// `Rate us on {storeName}` + String rateUsOnStore(Object storeName) { + return Intl.message( + 'Rate us on $storeName', + name: 'rateUsOnStore', + desc: '', + args: [storeName], + ); + } + + /// `Blog` + String get blog { + return Intl.message( + 'Blog', + name: 'blog', + desc: '', + args: [], + ); + } + + /// `Merchandise` + String get merchandise { + return Intl.message( + 'Merchandise', + name: 'merchandise', + desc: '', + args: [], + ); + } + + /// `Twitter` + String get twitter { + return Intl.message( + 'Twitter', + name: 'twitter', + desc: '', + args: [], + ); + } + + /// `Mastodon` + String get mastodon { + return Intl.message( + 'Mastodon', + name: 'mastodon', + desc: '', + args: [], + ); + } + + /// `Matrix` + String get matrix { + return Intl.message( + 'Matrix', + name: 'matrix', + desc: '', + args: [], + ); + } + + /// `Discord` + String get discord { + return Intl.message( + 'Discord', + name: 'discord', + desc: '', + args: [], + ); + } + + /// `Reddit` + String get reddit { + return Intl.message( + 'Reddit', + name: 'reddit', + desc: '', + args: [], + ); + } + + /// `Your storage details could not be fetched` + String get yourStorageDetailsCouldNotBeFetched { + return Intl.message( + 'Your storage details could not be fetched', + name: 'yourStorageDetailsCouldNotBeFetched', + desc: '', + args: [], + ); + } + + /// `Report a bug` + String get reportABug { + return Intl.message( + 'Report a bug', + name: 'reportABug', + desc: '', + args: [], + ); + } + + /// `Report bug` + String get reportBug { + return Intl.message( + 'Report bug', + name: 'reportBug', + desc: '', + args: [], + ); + } + + /// `Suggest features` + String get suggestFeatures { + return Intl.message( + 'Suggest features', + name: 'suggestFeatures', + desc: '', + args: [], + ); + } + + /// `Support` + String get support { + return Intl.message( + 'Support', + name: 'support', + desc: '', + args: [], + ); + } + + /// `Theme` + String get theme { + return Intl.message( + 'Theme', + name: 'theme', + desc: '', + args: [], + ); + } + + /// `Light` + String get lightTheme { + return Intl.message( + 'Light', + name: 'lightTheme', + desc: '', + args: [], + ); + } + + /// `Dark` + String get darkTheme { + return Intl.message( + 'Dark', + name: 'darkTheme', + desc: '', + args: [], + ); + } + + /// `System` + String get systemTheme { + return Intl.message( + 'System', + name: 'systemTheme', + desc: '', + args: [], + ); + } + + /// `Free trial` + String get freeTrial { + return Intl.message( + 'Free trial', + name: 'freeTrial', + desc: '', + args: [], + ); + } + + /// `Select your plan` + String get selectYourPlan { + return Intl.message( + 'Select your plan', + name: 'selectYourPlan', + desc: '', + args: [], + ); + } + + /// `ente preserves your memories, so they're always available to you, even if you lose your device.` + String get enteSubscriptionPitch { + return Intl.message( + 'ente preserves your memories, so they\'re always available to you, even if you lose your device.', + name: 'enteSubscriptionPitch', + desc: '', + args: [], + ); + } + + /// `Your family can be added to your plan as well.` + String get enteSubscriptionShareWithFamily { + return Intl.message( + 'Your family can be added to your plan as well.', + name: 'enteSubscriptionShareWithFamily', + desc: '', + args: [], + ); + } + + /// `Current usage is ` + String get currentUsageIs { + return Intl.message( + 'Current usage is ', + name: 'currentUsageIs', + desc: 'This text is followed by storage usaged', + args: [], + ); + } + + /// `FAQs` + String get faqs { + return Intl.message( + 'FAQs', + name: 'faqs', + desc: '', + args: [], + ); + } + + /// `Renews on {endDate}` + String renewsOn(Object endDate) { + return Intl.message( + 'Renews on $endDate', + name: 'renewsOn', + desc: '', + args: [endDate], + ); + } + + /// `Free trial valid till {endDate}` + String freeTrialValidTill(Object endDate) { + return Intl.message( + 'Free trial valid till $endDate', + name: 'freeTrialValidTill', + desc: '', + args: [endDate], + ); + } + + /// `Your subscription will be cancelled on {endDate}` + String subWillBeCancelledOn(Object endDate) { + return Intl.message( + 'Your subscription will be cancelled on $endDate', + name: 'subWillBeCancelledOn', + desc: '', + args: [endDate], + ); + } + + /// `Subscription` + String get subscription { + return Intl.message( + 'Subscription', + name: 'subscription', + desc: '', + args: [], + ); + } + + /// `Payment details` + String get paymentDetails { + return Intl.message( + 'Payment details', + name: 'paymentDetails', + desc: '', + args: [], + ); + } + + /// `Manage Family` + String get manageFamily { + return Intl.message( + 'Manage Family', + name: 'manageFamily', + desc: '', + args: [], + ); + } + + /// `Please contact us at support@ente.io to manage your {provider} subscription.` + String contactToManageSubscription(Object provider) { + return Intl.message( + 'Please contact us at support@ente.io to manage your $provider subscription.', + name: 'contactToManageSubscription', + desc: '', + args: [provider], + ); + } + + /// `Renew subscription` + String get renewSubscription { + return Intl.message( + 'Renew subscription', + name: 'renewSubscription', + desc: '', + args: [], + ); + } + + /// `Cancel subscription` + String get cancelSubscription { + return Intl.message( + 'Cancel subscription', + name: 'cancelSubscription', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to renew?` + String get areYouSureYouWantToRenew { + return Intl.message( + 'Are you sure you want to renew?', + name: 'areYouSureYouWantToRenew', + desc: '', + args: [], + ); + } + + /// `Yes, Renew` + String get yesRenew { + return Intl.message( + 'Yes, Renew', + name: 'yesRenew', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to cancel?` + String get areYouSureYouWantToCancel { + return Intl.message( + 'Are you sure you want to cancel?', + name: 'areYouSureYouWantToCancel', + desc: '', + args: [], + ); + } + + /// `Yes, cancel` + String get yesCancel { + return Intl.message( + 'Yes, cancel', + name: 'yesCancel', + desc: '', + args: [], + ); + } + + /// `Failed to renew` + String get failedToRenew { + return Intl.message( + 'Failed to renew', + name: 'failedToRenew', + desc: '', + args: [], + ); + } + + /// `Failed to cancel` + String get failedToCancel { + return Intl.message( + 'Failed to cancel', + name: 'failedToCancel', + desc: '', + args: [], + ); + } + + /// `2 months free on yearly plans` + String get twoMonthsFreeOnYearlyPlans { + return Intl.message( + '2 months free on yearly plans', + name: 'twoMonthsFreeOnYearlyPlans', + desc: '', + args: [], + ); + } + + /// `Monthly` + String get monthly { + return Intl.message( + 'Monthly', + name: 'monthly', + desc: 'The text to display for monthly plans', + args: [], + ); + } + + /// `Yearly` + String get yearly { + return Intl.message( + 'Yearly', + name: 'yearly', + desc: 'The text to display for yearly plans', + args: [], + ); + } + + /// `Confirm plan change` + String get confirmPlanChange { + return Intl.message( + 'Confirm plan change', + name: 'confirmPlanChange', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to change your plan?` + String get areYouSureYouWantToChangeYourPlan { + return Intl.message( + 'Are you sure you want to change your plan?', + name: 'areYouSureYouWantToChangeYourPlan', + desc: '', + args: [], + ); + } + + /// `You cannot downgrade to this plan` + String get youCannotDowngradeToThisPlan { + return Intl.message( + 'You cannot downgrade to this plan', + name: 'youCannotDowngradeToThisPlan', + desc: '', + args: [], + ); + } + + /// `Please cancel your existing subscription from {paymentProvider} first` + String cancelOtherSubscription(String paymentProvider) { + return Intl.message( + 'Please cancel your existing subscription from $paymentProvider first', + name: 'cancelOtherSubscription', + desc: + 'The text to display when the user has an existing subscription from a different payment provider', + args: [paymentProvider], + ); + } + + /// `Optional, as short as you like...` + String get optionalAsShortAsYouLike { + return Intl.message( + 'Optional, as short as you like...', + name: 'optionalAsShortAsYouLike', + desc: '', + args: [], + ); + } + + /// `Send` + String get send { + return Intl.message( + 'Send', + name: 'send', + desc: '', + args: [], + ); + } + + /// `Your subscription was cancelled. Would you like to share the reason?` + String get askCancelReason { + return Intl.message( + 'Your subscription was cancelled. Would you like to share the reason?', + name: 'askCancelReason', + desc: '', + args: [], + ); + } + + /// `Thank you for subscribing!` + String get thankYouForSubscribing { + return Intl.message( + 'Thank you for subscribing!', + name: 'thankYouForSubscribing', + desc: '', + args: [], + ); + } + + /// `Your purchase was successful` + String get yourPurchaseWasSuccessful { + return Intl.message( + 'Your purchase was successful', + name: 'yourPurchaseWasSuccessful', + desc: '', + args: [], + ); + } + + /// `Your plan was successfully upgraded` + String get yourPlanWasSuccessfullyUpgraded { + return Intl.message( + 'Your plan was successfully upgraded', + name: 'yourPlanWasSuccessfullyUpgraded', + desc: '', + args: [], + ); + } + + /// `Your plan was successfully downgraded` + String get yourPlanWasSuccessfullyDowngraded { + return Intl.message( + 'Your plan was successfully downgraded', + name: 'yourPlanWasSuccessfullyDowngraded', + desc: '', + args: [], + ); + } + + /// `Your subscription was updated successfully` + String get yourSubscriptionWasUpdatedSuccessfully { + return Intl.message( + 'Your subscription was updated successfully', + name: 'yourSubscriptionWasUpdatedSuccessfully', + desc: '', + args: [], + ); + } + + /// `Google Play ID` + String get googlePlayId { + return Intl.message( + 'Google Play ID', + name: 'googlePlayId', + desc: '', + args: [], + ); + } + + /// `Apple ID` + String get appleId { + return Intl.message( + 'Apple ID', + name: 'appleId', + desc: '', + args: [], + ); + } + + /// `PlayStore subscription` + String get playstoreSubscription { + return Intl.message( + 'PlayStore subscription', + name: 'playstoreSubscription', + desc: '', + args: [], + ); + } + + /// `AppStore subscription` + String get appstoreSubscription { + return Intl.message( + 'AppStore subscription', + name: 'appstoreSubscription', + desc: '', + args: [], + ); + } + + /// `Your {id} is already linked to another ente account.\nIf you would like to use your {id} with this account, please contact our support''` + String subAlreadyLinkedErrMessage(Object id) { + return Intl.message( + 'Your $id is already linked to another ente account.\nIf you would like to use your $id with this account, please contact our support\'\'', + name: 'subAlreadyLinkedErrMessage', + desc: '', + args: [id], + ); + } + + /// `Please visit web.ente.io to manage your subscription` + String get visitWebToManage { + return Intl.message( + 'Please visit web.ente.io to manage your subscription', + name: 'visitWebToManage', + desc: '', + args: [], + ); + } + + /// `Could not update subscription` + String get couldNotUpdateSubscription { + return Intl.message( + 'Could not update subscription', + name: 'couldNotUpdateSubscription', + desc: '', + args: [], + ); + } + + /// `Please contact support@ente.io and we will be happy to help!` + String get pleaseContactSupportAndWeWillBeHappyToHelp { + return Intl.message( + 'Please contact support@ente.io and we will be happy to help!', + name: 'pleaseContactSupportAndWeWillBeHappyToHelp', + desc: '', + args: [], + ); + } + + /// `Payment failed` + String get paymentFailed { + return Intl.message( + 'Payment failed', + name: 'paymentFailed', + desc: '', + args: [], + ); + } + + /// `Please talk to {providerName} support if you were charged` + String paymentFailedTalkToProvider(String providerName) { + return Intl.message( + 'Please talk to $providerName support if you were charged', + name: 'paymentFailedTalkToProvider', + desc: 'The text to display when the payment failed', + args: [providerName], + ); + } + + /// `Continue on free trial` + String get continueOnFreeTrial { + return Intl.message( + 'Continue on free trial', + name: 'continueOnFreeTrial', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to exit?` + String get areYouSureYouWantToExit { + return Intl.message( + 'Are you sure you want to exit?', + name: 'areYouSureYouWantToExit', + desc: '', + args: [], + ); + } + + /// `Thank you` + String get thankYou { + return Intl.message( + 'Thank you', + name: 'thankYou', + desc: '', + args: [], + ); + } + + /// `Failed to verify payment status` + String get failedToVerifyPaymentStatus { + return Intl.message( + 'Failed to verify payment status', + name: 'failedToVerifyPaymentStatus', + desc: '', + args: [], + ); + } + + /// `Please wait for sometime before retrying` + String get pleaseWaitForSometimeBeforeRetrying { + return Intl.message( + 'Please wait for sometime before retrying', + name: 'pleaseWaitForSometimeBeforeRetrying', + desc: '', + args: [], + ); + } + + /// `Unfortunately your payment failed due to {reason}` + String paymentFailedWithReason(Object reason) { + return Intl.message( + 'Unfortunately your payment failed due to $reason', + name: 'paymentFailedWithReason', + desc: '', + args: [reason], + ); + } + + /// `You are on a family plan!` + String get youAreOnAFamilyPlan { + return Intl.message( + 'You are on a family plan!', + name: 'youAreOnAFamilyPlan', + desc: '', + args: [], + ); + } + + /// `Please contact {familyAdminEmail} to manage your subscription` + String contactFamilyAdmin(Object familyAdminEmail) { + return Intl.message( + 'Please contact $familyAdminEmail to manage your subscription', + name: 'contactFamilyAdmin', + desc: '', + args: [familyAdminEmail], + ); + } + + /// `Leave family` + String get leaveFamily { + return Intl.message( + 'Leave family', + name: 'leaveFamily', + desc: '', + args: [], + ); + } + + /// `Are you sure that you want to leave the family plan?` + String get areYouSureThatYouWantToLeaveTheFamily { + return Intl.message( + 'Are you sure that you want to leave the family plan?', + name: 'areYouSureThatYouWantToLeaveTheFamily', + desc: '', + args: [], + ); + } + + /// `Leave` + String get leave { + return Intl.message( + 'Leave', + name: 'leave', + desc: '', + args: [], + ); + } + + /// `Rate the app` + String get rateTheApp { + return Intl.message( + 'Rate the app', + name: 'rateTheApp', + desc: '', + args: [], + ); + } + + /// `Start backup` + String get startBackup { + return Intl.message( + 'Start backup', + name: 'startBackup', + desc: '', + args: [], + ); + } + + /// `No photos are being backed up right now` + String get noPhotosAreBeingBackedUpRightNow { + return Intl.message( + 'No photos are being backed up right now', + name: 'noPhotosAreBeingBackedUpRightNow', + desc: '', + args: [], + ); + } + + /// `Preserve more` + String get preserveMore { + return Intl.message( + 'Preserve more', + name: 'preserveMore', + desc: '', + args: [], + ); + } + + /// `Existing user` + String get existingUser { + return Intl.message( + 'Existing user', + name: 'existingUser', + desc: '', + args: [], + ); + } + + /// `Private backups` + String get privateBackups { + return Intl.message( + 'Private backups', + name: 'privateBackups', + desc: '', + args: [], + ); + } + + /// `for your memories` + String get forYourMemories { + return Intl.message( + 'for your memories', + name: 'forYourMemories', + desc: '', + args: [], + ); + } + + /// `End-to-end encrypted by default` + String get endtoendEncryptedByDefault { + return Intl.message( + 'End-to-end encrypted by default', + name: 'endtoendEncryptedByDefault', + desc: '', + args: [], + ); + } + + /// `Safely stored` + String get safelyStored { + return Intl.message( + 'Safely stored', + name: 'safelyStored', + desc: '', + args: [], + ); + } + + /// `at a fallout shelter` + String get atAFalloutShelter { + return Intl.message( + 'at a fallout shelter', + name: 'atAFalloutShelter', + desc: '', + args: [], + ); + } + + /// `Designed to outlive` + String get designedToOutlive { + return Intl.message( + 'Designed to outlive', + name: 'designedToOutlive', + desc: '', + args: [], + ); + } + + /// `Available` + String get available { + return Intl.message( + 'Available', + name: 'available', + desc: '', + args: [], + ); + } + + /// `everywhere` + String get everywhere { + return Intl.message( + 'everywhere', + name: 'everywhere', + desc: '', + args: [], + ); + } + + /// `Android, iOS, Web, Desktop` + String get androidIosWebDesktop { + return Intl.message( + 'Android, iOS, Web, Desktop', + name: 'androidIosWebDesktop', + desc: '', + args: [], + ); + } + + /// `Mobile, Web, Desktop` + String get mobileWebDesktop { + return Intl.message( + 'Mobile, Web, Desktop', + name: 'mobileWebDesktop', + desc: '', + args: [], + ); + } + + /// `New to ente` + String get newToEnte { + return Intl.message( + 'New to ente', + name: 'newToEnte', + desc: '', + args: [], + ); + } + + /// `Please login again` + String get pleaseLoginAgain { + return Intl.message( + 'Please login again', + name: 'pleaseLoginAgain', + desc: '', + args: [], + ); + } + + /// `The developer account we use to publish ente on App Store has changed. Because of this, you will need to login again.\n\nOur apologies for the inconvenience, but this was unavoidable.` + String get devAccountChanged { + return Intl.message( + 'The developer account we use to publish ente on App Store has changed. Because of this, you will need to login again.\n\nOur apologies for the inconvenience, but this was unavoidable.', + name: 'devAccountChanged', + desc: '', + args: [], + ); + } + + /// `Your subscription has expired` + String get yourSubscriptionHasExpired { + return Intl.message( + 'Your subscription has expired', + name: 'yourSubscriptionHasExpired', + desc: '', + args: [], + ); + } + + /// `Storage limit exceeded` + String get storageLimitExceeded { + return Intl.message( + 'Storage limit exceeded', + name: 'storageLimitExceeded', + desc: '', + args: [], + ); + } + + /// `Upgrade` + String get upgrade { + return Intl.message( + 'Upgrade', + name: 'upgrade', + desc: '', + args: [], + ); + } + + /// `Raise ticket` + String get raiseTicket { + return Intl.message( + 'Raise ticket', + name: 'raiseTicket', + desc: '', + args: [], + ); + } + + /// `Backup failed` + String get backupFailed { + return Intl.message( + 'Backup failed', + name: 'backupFailed', + desc: '', + args: [], + ); + } + + /// `We could not backup your data.\nWe will retry later.` + String get couldNotBackUpTryLater { + return Intl.message( + 'We could not backup your data.\nWe will retry later.', + name: 'couldNotBackUpTryLater', + desc: '', + args: [], + ); + } + + /// `ente can encrypt and preserve files only if you grant access to them` + String get enteCanEncryptAndPreserveFilesOnlyIfYouGrant { + return Intl.message( + 'ente can encrypt and preserve files only if you grant access to them', + name: 'enteCanEncryptAndPreserveFilesOnlyIfYouGrant', + desc: '', + args: [], + ); + } + + /// `Please grant permissions` + String get pleaseGrantPermissions { + return Intl.message( + 'Please grant permissions', + name: 'pleaseGrantPermissions', + desc: '', + args: [], + ); + } + + /// `Grant permission` + String get grantPermission { + return Intl.message( + 'Grant permission', + name: 'grantPermission', + desc: '', + args: [], + ); + } + + /// `Private sharing` + String get privateSharing { + return Intl.message( + 'Private sharing', + name: 'privateSharing', + desc: '', + args: [], + ); + } + + /// `Share only with the people you want` + String get shareOnlyWithThePeopleYouWant { + return Intl.message( + 'Share only with the people you want', + name: 'shareOnlyWithThePeopleYouWant', + desc: '', + args: [], + ); + } + + /// `Use public links for people not on ente` + String get usePublicLinksForPeopleNotOnEnte { + return Intl.message( + 'Use public links for people not on ente', + name: 'usePublicLinksForPeopleNotOnEnte', + desc: '', + args: [], + ); + } + + /// `Allow people to add photos` + String get allowPeopleToAddPhotos { + return Intl.message( + 'Allow people to add photos', + name: 'allowPeopleToAddPhotos', + desc: '', + args: [], + ); + } + + /// `Share an album now` + String get shareAnAlbumNow { + return Intl.message( + 'Share an album now', + name: 'shareAnAlbumNow', + desc: '', + args: [], + ); + } + + /// `Collect event photos` + String get collectEventPhotos { + return Intl.message( + 'Collect event photos', + name: 'collectEventPhotos', + desc: '', + args: [], + ); + } + + /// `Session expired` + String get sessionExpired { + return Intl.message( + 'Session expired', + name: 'sessionExpired', + desc: '', + args: [], + ); + } + + /// `Logging out...` + String get loggingOut { + return Intl.message( + 'Logging out...', + name: 'loggingOut', + desc: '', + args: [], + ); + } + + /// `Name` + String get name { + return Intl.message( + 'Name', + name: 'name', + desc: '', + args: [], + ); + } + + /// `Newest` + String get newest { + return Intl.message( + 'Newest', + name: 'newest', + desc: '', + args: [], + ); + } + + /// `Last updated` + String get lastUpdated { + return Intl.message( + 'Last updated', + name: 'lastUpdated', + desc: '', + args: [], + ); + } + + /// `Delete empty albums` + String get deleteEmptyAlbums { + return Intl.message( + 'Delete empty albums', + name: 'deleteEmptyAlbums', + desc: '', + args: [], + ); + } + + /// `Delete empty albums?` + String get deleteEmptyAlbumsWithQuestionMark { + return Intl.message( + 'Delete empty albums?', + name: 'deleteEmptyAlbumsWithQuestionMark', + desc: '', + args: [], + ); + } + + /// `This will delete all empty albums. This is useful when you want to reduce the clutter in your album list.` + String get deleteAlbumsDialogBody { + return Intl.message( + 'This will delete all empty albums. This is useful when you want to reduce the clutter in your album list.', + name: 'deleteAlbumsDialogBody', + desc: '', + args: [], + ); + } + + /// `Deleting {currentlyDeleting} / {totalCount}` + String deleteProgress(Object currentlyDeleting, Object totalCount) { + return Intl.message( + 'Deleting $currentlyDeleting / $totalCount', + name: 'deleteProgress', + desc: '', + args: [currentlyDeleting, totalCount], + ); + } + + /// `Permanently delete` + String get permanentlyDelete { + return Intl.message( + 'Permanently delete', + name: 'permanentlyDelete', + desc: '', + args: [], + ); + } + + /// `Can only create link for files owned by you` + String get canOnlyCreateLinkForFilesOwnedByYou { + return Intl.message( + 'Can only create link for files owned by you', + name: 'canOnlyCreateLinkForFilesOwnedByYou', + desc: '', + args: [], + ); + } + + /// `Public link created` + String get publicLinkCreated { + return Intl.message( + 'Public link created', + name: 'publicLinkCreated', + desc: '', + args: [], + ); + } + + /// `You can manage your links in the share tab.` + String get youCanManageYourLinksInTheShareTab { + return Intl.message( + 'You can manage your links in the share tab.', + name: 'youCanManageYourLinksInTheShareTab', + desc: '', + args: [], + ); + } + + /// `Link copied to clipboard` + String get linkCopiedToClipboard { + return Intl.message( + 'Link copied to clipboard', + name: 'linkCopiedToClipboard', + desc: '', + args: [], + ); + } + + /// `Restore` + String get restore { + return Intl.message( + 'Restore', + name: 'restore', + desc: + 'Display text for an action which triggers a restore of item from trash', + args: [], + ); + } + + /// `Move to album` + String get moveToAlbum { + return Intl.message( + 'Move to album', + name: 'moveToAlbum', + desc: '', + args: [], + ); + } + + /// `Unhide` + String get unhide { + return Intl.message( + 'Unhide', + name: 'unhide', + desc: '', + args: [], + ); + } + + /// `Unarchive` + String get unarchive { + return Intl.message( + 'Unarchive', + name: 'unarchive', + desc: '', + args: [], + ); + } + + /// `Favorite` + String get favorite { + return Intl.message( + 'Favorite', + name: 'favorite', + desc: '', + args: [], + ); + } + + /// `Remove from favorite` + String get removeFromFavorite { + return Intl.message( + 'Remove from favorite', + name: 'removeFromFavorite', + desc: '', + args: [], + ); + } + + /// `Share link` + String get shareLink { + return Intl.message( + 'Share link', + name: 'shareLink', + desc: '', + args: [], + ); + } + + /// `Add to ente` + String get addToEnte { + return Intl.message( + 'Add to ente', + name: 'addToEnte', + desc: '', + args: [], + ); + } + + /// `Add to album` + String get addToAlbum { + return Intl.message( + 'Add to album', + name: 'addToAlbum', + desc: '', + args: [], + ); + } + + /// `Delete` + String get delete { + return Intl.message( + 'Delete', + name: 'delete', + desc: '', + args: [], + ); + } + + /// `Hide` + String get hide { + return Intl.message( + 'Hide', + name: 'hide', + desc: '', + args: [], + ); + } + + /// `{count} selected` + String itemSelectedCount(int count) { + return Intl.message( + '$count selected', + name: 'itemSelectedCount', + desc: 'Text to indicate number of items selected', + args: [count], + ); + } + + /// `Share` + String get share { + return Intl.message( + 'Share', + name: 'share', + desc: '', + args: [], + ); + } + + /// `Unhide to album` + String get unhideToAlbum { + return Intl.message( + 'Unhide to album', + name: 'unhideToAlbum', + desc: '', + args: [], + ); + } + + /// `Restore to album` + String get restoreToAlbum { + return Intl.message( + 'Restore to album', + name: 'restoreToAlbum', + desc: '', + args: [], + ); + } + + /// `{count, plural, one {Move item} other {Move items}}` + String moveItem(num count) { + return Intl.plural( + count, + one: 'Move item', + other: 'Move items', + name: 'moveItem', + desc: 'Page title while moving one or more items to an album', + args: [count], + ); + } + + /// `{count, plural, one {Add item} other {Add items}}` + String addItem(num count) { + return Intl.plural( + count, + one: 'Add item', + other: 'Add items', + name: 'addItem', + desc: 'Page title while adding one or more items to album', + args: [count], + ); + } + + /// `Create or select album` + String get createOrSelectAlbum { + return Intl.message( + 'Create or select album', + name: 'createOrSelectAlbum', + desc: '', + args: [], + ); + } + + /// `Select album` + String get selectAlbum { + return Intl.message( + 'Select album', + name: 'selectAlbum', + desc: '', + args: [], + ); + } + + /// `Album name` + String get searchByAlbumNameHint { + return Intl.message( + 'Album name', + name: 'searchByAlbumNameHint', + desc: '', + args: [], + ); + } + + /// `Album title` + String get albumTitle { + return Intl.message( + 'Album title', + name: 'albumTitle', + desc: '', + args: [], + ); + } + + /// `Enter album name` + String get enterAlbumName { + return Intl.message( + 'Enter album name', + name: 'enterAlbumName', + desc: '', + args: [], + ); + } + + /// `Restoring files...` + String get restoringFiles { + return Intl.message( + 'Restoring files...', + name: 'restoringFiles', + desc: '', + args: [], + ); + } + + /// `Moving files to album...` + String get movingFilesToAlbum { + return Intl.message( + 'Moving files to album...', + name: 'movingFilesToAlbum', + desc: '', + args: [], + ); + } + + /// `Unhiding files to album` + String get unhidingFilesToAlbum { + return Intl.message( + 'Unhiding files to album', + name: 'unhidingFilesToAlbum', + desc: '', + args: [], + ); + } + + /// `Can not upload to albums owned by others` + String get canNotUploadToAlbumsOwnedByOthers { + return Intl.message( + 'Can not upload to albums owned by others', + name: 'canNotUploadToAlbumsOwnedByOthers', + desc: '', + args: [], + ); + } + + /// `Uploading files to album...` + String get uploadingFilesToAlbum { + return Intl.message( + 'Uploading files to album...', + name: 'uploadingFilesToAlbum', + desc: '', + args: [], + ); + } + + /// `Added successfully to {albumName}` + String addedSuccessfullyTo(Object albumName) { + return Intl.message( + 'Added successfully to $albumName', + name: 'addedSuccessfullyTo', + desc: '', + args: [albumName], + ); + } + + /// `Moved successfully to {albumName}` + String movedSuccessfullyTo(Object albumName) { + return Intl.message( + 'Moved successfully to $albumName', + name: 'movedSuccessfullyTo', + desc: '', + args: [albumName], + ); + } + + /// `This album already has a collaborative link` + String get thisAlbumAlreadyHDACollaborativeLink { + return Intl.message( + 'This album already has a collaborative link', + name: 'thisAlbumAlreadyHDACollaborativeLink', + desc: '', + args: [], + ); + } + + /// `Collaborative link created for {albumName}` + String collaborativeLinkCreatedFor(Object albumName) { + return Intl.message( + 'Collaborative link created for $albumName', + name: 'collaborativeLinkCreatedFor', + desc: '', + args: [albumName], + ); + } + + /// `Ask your loved ones to share` + String get askYourLovedOnesToShare { + return Intl.message( + 'Ask your loved ones to share', + name: 'askYourLovedOnesToShare', + desc: '', + args: [], + ); + } + + /// `Invite` + String get invite { + return Intl.message( + 'Invite', + name: 'invite', + desc: '', + args: [], + ); + } + + /// `Share your first album` + String get shareYourFirstAlbum { + return Intl.message( + 'Share your first album', + name: 'shareYourFirstAlbum', + desc: '', + args: [], + ); + } + + /// `Shared with {emailIDs}` + String sharedWith(Object emailIDs) { + return Intl.message( + 'Shared with $emailIDs', + name: 'sharedWith', + desc: '', + args: [emailIDs], + ); + } + + /// `Shared with me` + String get sharedWithMe { + return Intl.message( + 'Shared with me', + name: 'sharedWithMe', + desc: '', + args: [], + ); + } + + /// `Shared by me` + String get sharedByMe { + return Intl.message( + 'Shared by me', + name: 'sharedByMe', + desc: '', + args: [], + ); + } + + /// `Double your storage` + String get doubleYourStorage { + return Intl.message( + 'Double your storage', + name: 'doubleYourStorage', + desc: '', + args: [], + ); + } + + /// `Refer friends and 2x your plan` + String get referFriendsAnd2xYourPlan { + return Intl.message( + 'Refer friends and 2x your plan', + name: 'referFriendsAnd2xYourPlan', + desc: '', + args: [], + ); + } + + /// `Open an album and tap the share button on the top right to share.` + String get shareAlbumHint { + return Intl.message( + 'Open an album and tap the share button on the top right to share.', + name: 'shareAlbumHint', + desc: '', + args: [], + ); + } + + /// `Items show the number of days remaining before permanent deletion` + String get itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion { + return Intl.message( + 'Items show the number of days remaining before permanent deletion', + name: 'itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion', + desc: '', + args: [], + ); + } + + /// `Delete All` + String get deleteAll { + return Intl.message( + 'Delete All', + name: 'deleteAll', + desc: '', + args: [], + ); + } + + /// `Rename album` + String get renameAlbum { + return Intl.message( + 'Rename album', + name: 'renameAlbum', + desc: '', + args: [], + ); + } + + /// `Rename` + String get rename { + return Intl.message( + 'Rename', + name: 'rename', + desc: '', + args: [], + ); + } + + /// `Leave shared album?` + String get leaveSharedAlbum { + return Intl.message( + 'Leave shared album?', + name: 'leaveSharedAlbum', + desc: '', + args: [], + ); + } + + /// `Leave album` + String get leaveAlbum { + return Intl.message( + 'Leave album', + name: 'leaveAlbum', + desc: '', + args: [], + ); + } + + /// `Photos added by you will be removed from the album` + String get photosAddedByYouWillBeRemovedFromTheAlbum { + return Intl.message( + 'Photos added by you will be removed from the album', + name: 'photosAddedByYouWillBeRemovedFromTheAlbum', + desc: '', + args: [], + ); + } + + /// `You've no files in this album that can be deleted` + String get youveNoFilesInThisAlbumThatCanBeDeleted { + return Intl.message( + 'You\'ve no files in this album that can be deleted', + name: 'youveNoFilesInThisAlbumThatCanBeDeleted', + desc: '', + args: [], + ); + } + + /// `You don't have any archived items.` + String get youDontHaveAnyArchivedItems { + return Intl.message( + 'You don\'t have any archived items.', + name: 'youDontHaveAnyArchivedItems', + desc: '', + args: [], + ); + } + + /// `Some files in this album are ignored from upload because they had previously been deleted from ente.` + String get ignoredFolderUploadReason { + return Intl.message( + 'Some files in this album are ignored from upload because they had previously been deleted from ente.', + name: 'ignoredFolderUploadReason', + desc: '', + args: [], + ); + } + + /// `Reset ignored files` + String get resetIgnoredFiles { + return Intl.message( + 'Reset ignored files', + name: 'resetIgnoredFiles', + desc: '', + args: [], + ); + } + + /// `Files added to this device album will automatically get uploaded to ente.` + String get deviceFilesAutoUploading { + return Intl.message( + 'Files added to this device album will automatically get uploaded to ente.', + name: 'deviceFilesAutoUploading', + desc: '', + args: [], + ); + } + + /// `Turn on backup to automatically upload files added to this device folder to ente.` + String get turnOnBackupForAutoUpload { + return Intl.message( + 'Turn on backup to automatically upload files added to this device folder to ente.', + name: 'turnOnBackupForAutoUpload', + desc: '', + args: [], + ); + } + + /// `No hidden photos or videos` + String get noHiddenPhotosOrVideos { + return Intl.message( + 'No hidden photos or videos', + name: 'noHiddenPhotosOrVideos', + desc: '', + args: [], + ); + } + + /// `To hide a photo or video` + String get toHideAPhotoOrVideo { + return Intl.message( + 'To hide a photo or video', + name: 'toHideAPhotoOrVideo', + desc: '', + args: [], + ); + } + + /// `• Open the item` + String get openTheItem { + return Intl.message( + '• Open the item', + name: 'openTheItem', + desc: '', + args: [], + ); + } + + /// `• Click on the overflow menu` + String get clickOnTheOverflowMenu { + return Intl.message( + '• Click on the overflow menu', + name: 'clickOnTheOverflowMenu', + desc: '', + args: [], + ); + } + + /// `• Click` + String get click { + return Intl.message( + '• Click', + name: 'click', + desc: '', + args: [], + ); + } + + /// `Nothing to see here! 👀` + String get nothingToSeeHere { + return Intl.message( + 'Nothing to see here! 👀', + name: 'nothingToSeeHere', + desc: '', + args: [], + ); + } + + /// `Unarchive album` + String get unarchiveAlbum { + return Intl.message( + 'Unarchive album', + name: 'unarchiveAlbum', + desc: '', + args: [], + ); + } + + /// `Archive album` + String get archiveAlbum { + return Intl.message( + 'Archive album', + name: 'archiveAlbum', + desc: '', + args: [], + ); + } + + /// `Calculating...` + String get calculating { + return Intl.message( + 'Calculating...', + name: 'calculating', + desc: '', + args: [], + ); + } + + /// `Please wait, deleting album` + String get pleaseWaitDeletingAlbum { + return Intl.message( + 'Please wait, deleting album', + name: 'pleaseWaitDeletingAlbum', + desc: '', + args: [], + ); + } + + /// `Albums, months, days, years, ...` + String get searchHintText { + return Intl.message( + 'Albums, months, days, years, ...', + name: 'searchHintText', + desc: '', + args: [], + ); + } + + /// `• Album names (e.g. "Camera")\n• Types of files (e.g. "Videos", ".gif")\n• Years and months (e.g. "2022", "January")\n• Holidays (e.g. "Christmas")\n• Photo descriptions (e.g. “#fun”)` + String get searchByExamples { + return Intl.message( + '• Album names (e.g. "Camera")\n• Types of files (e.g. "Videos", ".gif")\n• Years and months (e.g. "2022", "January")\n• Holidays (e.g. "Christmas")\n• Photo descriptions (e.g. “#fun”)', + name: 'searchByExamples', + desc: '', + args: [], + ); + } + + /// `You can try searching for a different query.` + String get youCanTrySearchingForADifferentQuery { + return Intl.message( + 'You can try searching for a different query.', + name: 'youCanTrySearchingForADifferentQuery', + desc: '', + args: [], + ); + } + + /// `No results found` + String get noResultsFound { + return Intl.message( + 'No results found', + name: 'noResultsFound', + desc: '', + args: [], + ); + } + + /// `Added by {emailOrName}` + String addedBy(Object emailOrName) { + return Intl.message( + 'Added by $emailOrName', + name: 'addedBy', + desc: '', + args: [emailOrName], + ); + } + + /// `Loading EXIF data...` + String get loadingExifData { + return Intl.message( + 'Loading EXIF data...', + name: 'loadingExifData', + desc: '', + args: [], + ); + } + + /// `View all EXIF data` + String get viewAllExifData { + return Intl.message( + 'View all EXIF data', + name: 'viewAllExifData', + desc: '', + args: [], + ); + } + + /// `No EXIF data` + String get noExifData { + return Intl.message( + 'No EXIF data', + name: 'noExifData', + desc: '', + args: [], + ); + } + + /// `This image has no exif data` + String get thisImageHasNoExifData { + return Intl.message( + 'This image has no exif data', + name: 'thisImageHasNoExifData', + desc: '', + args: [], + ); + } + + /// `EXIF` + String get exif { + return Intl.message( + 'EXIF', + name: 'exif', + desc: '', + args: [], + ); + } + + /// `No results` + String get noResults { + return Intl.message( + 'No results', + name: 'noResults', + desc: '', + args: [], + ); + } + + /// `We don't support editing photos and albums that you don't own yet` + String get weDontSupportEditingPhotosAndAlbumsThatYouDont { + return Intl.message( + 'We don\'t support editing photos and albums that you don\'t own yet', + name: 'weDontSupportEditingPhotosAndAlbumsThatYouDont', + desc: '', + args: [], + ); + } + + /// `Failed to fetch original for edit` + String get failedToFetchOriginalForEdit { + return Intl.message( + 'Failed to fetch original for edit', + name: 'failedToFetchOriginalForEdit', + desc: '', + args: [], + ); + } + + /// `Close` + String get close { + return Intl.message( + 'Close', + name: 'close', + desc: '', + args: [], + ); + } + + /// `Set as` + String get setAs { + return Intl.message( + 'Set as', + name: 'setAs', + desc: '', + args: [], + ); + } + + /// `File saved to gallery` + String get fileSavedToGallery { + return Intl.message( + 'File saved to gallery', + name: 'fileSavedToGallery', + desc: '', + args: [], + ); + } + + /// `Download` + String get download { + return Intl.message( + 'Download', + name: 'download', + desc: '', + args: [], + ); + } + + /// `Press and hold to play video` + String get pressAndHoldToPlayVideo { + return Intl.message( + 'Press and hold to play video', + name: 'pressAndHoldToPlayVideo', + desc: '', + args: [], + ); + } + + /// `Download failed` + String get downloadFailed { + return Intl.message( + 'Download failed', + name: 'downloadFailed', + desc: '', + args: [], + ); + } + + /// `Deduplicate Files` + String get deduplicateFiles { + return Intl.message( + 'Deduplicate Files', + name: 'deduplicateFiles', + desc: '', + args: [], + ); + } + + /// `Deselect all` + String get deselectAll { + return Intl.message( + 'Deselect all', + name: 'deselectAll', + desc: '', + args: [], + ); + } + + /// `Please review and delete the items you believe are duplicates.` + String get reviewDeduplicateItems { + return Intl.message( + 'Please review and delete the items you believe are duplicates.', + name: 'reviewDeduplicateItems', + desc: '', + args: [], + ); + } + + /// `Club by capture time` + String get clubByCaptureTime { + return Intl.message( + 'Club by capture time', + name: 'clubByCaptureTime', + desc: '', + args: [], + ); + } + + /// `Count` + String get count { + return Intl.message( + 'Count', + name: 'count', + desc: '', + args: [], + ); + } + + /// `Total size` + String get totalSize { + return Intl.message( + 'Total size', + name: 'totalSize', + desc: '', + args: [], + ); + } + + /// `Time` + String get time { + return Intl.message( + 'Time', + name: 'time', + desc: '', + args: [], + ); + } + + /// `Long-press on an item to view in full-screen` + String get longpressOnAnItemToViewInFullscreen { + return Intl.message( + 'Long-press on an item to view in full-screen', + name: 'longpressOnAnItemToViewInFullscreen', + desc: '', + args: [], + ); + } + + /// `Decrypting video...` + String get decryptingVideo { + return Intl.message( + 'Decrypting video...', + name: 'decryptingVideo', + desc: '', + args: [], + ); + } + + /// `Please authenticate to view your memories` + String get authToViewYourMemories { + return Intl.message( + 'Please authenticate to view your memories', + name: 'authToViewYourMemories', + desc: '', + args: [], + ); + } + + /// `Unlock` + String get unlock { + return Intl.message( + 'Unlock', + name: 'unlock', + desc: '', + args: [], + ); + } + + /// `Free up space` + String get freeUpSpace { + return Intl.message( + 'Free up space', + name: 'freeUpSpace', + desc: '', + args: [], + ); + } + + /// `{count, plural, one {It can be deleted from the device to free up {formattedSize}} other {They can be deleted from the device to free up {formattedSize}}}` + String freeUpSpaceSaving(num count, Object formattedSize) { + return Intl.plural( + count, + one: 'It can be deleted from the device to free up $formattedSize', + other: 'They can be deleted from the device to free up $formattedSize', + name: 'freeUpSpaceSaving', + desc: + 'Text to tell user how much space they can free up by deleting items from the device', + args: [count, formattedSize], + ); + } + + /// `{count, plural, one {1 file} other {{formattedNumber} files}} in this album has been backed up safely` + String filesBackedUpInAlbum(int count, String formattedNumber) { + return Intl.message( + '${Intl.plural(count, one: '1 file', other: '$formattedNumber files')} in this album has been backed up safely', + name: 'filesBackedUpInAlbum', + desc: 'Text to tell user how many files have been backed up in the album', + args: [count, formattedNumber], + ); + } + + /// `{count, plural, one {1 file} other {{formattedNumber} files}} on this device have been backed up safely` + String filesBackedUpFromDevice(int count, String formattedNumber) { + return Intl.message( + '${Intl.plural(count, one: '1 file', other: '$formattedNumber files')} on this device have been backed up safely', + name: 'filesBackedUpFromDevice', + desc: + 'Text to tell user how many files have been backed up from this device', + args: [count, formattedNumber], + ); + } + + /// `You can still access {count, plural, one {it} other {them}} on ente as long as you have an active subscription` + String freeUpAccessPostDelete(int count) { + return Intl.message( + 'You can still access ${Intl.plural(count, one: 'it', other: 'them')} on ente as long as you have an active subscription', + name: 'freeUpAccessPostDelete', + desc: '', + args: [count], + ); + } + + /// `Free up {sizeInMBorGB}` + String freeUpAmount(Object sizeInMBorGB) { + return Intl.message( + 'Free up $sizeInMBorGB', + name: 'freeUpAmount', + desc: '', + args: [sizeInMBorGB], + ); + } + + /// `This email is already in use` + String get thisEmailIsAlreadyInUse { + return Intl.message( + 'This email is already in use', + name: 'thisEmailIsAlreadyInUse', + desc: '', + args: [], + ); + } + + /// `Incorrect code` + String get incorrectCode { + return Intl.message( + 'Incorrect code', + name: 'incorrectCode', + desc: '', + args: [], + ); + } + + /// `Authentication failed, please try again` + String get authenticationFailedPleaseTryAgain { + return Intl.message( + 'Authentication failed, please try again', + name: 'authenticationFailedPleaseTryAgain', + desc: '', + args: [], + ); + } + + /// `Verification failed, please try again` + String get verificationFailedPleaseTryAgain { + return Intl.message( + 'Verification failed, please try again', + name: 'verificationFailedPleaseTryAgain', + desc: '', + args: [], + ); + } + + /// `Authenticating...` + String get authenticating { + return Intl.message( + 'Authenticating...', + name: 'authenticating', + desc: '', + args: [], + ); + } + + /// `Authentication successful!` + String get authenticationSuccessful { + return Intl.message( + 'Authentication successful!', + name: 'authenticationSuccessful', + desc: '', + args: [], + ); + } + + /// `Incorrect recovery key` + String get incorrectRecoveryKey { + return Intl.message( + 'Incorrect recovery key', + name: 'incorrectRecoveryKey', + desc: '', + args: [], + ); + } + + /// `The recovery key you entered is incorrect` + String get theRecoveryKeyYouEnteredIsIncorrect { + return Intl.message( + 'The recovery key you entered is incorrect', + name: 'theRecoveryKeyYouEnteredIsIncorrect', + desc: '', + args: [], + ); + } + + /// `Two-factor authentication successfully reset` + String get twofactorAuthenticationSuccessfullyReset { + return Intl.message( + 'Two-factor authentication successfully reset', + name: 'twofactorAuthenticationSuccessfullyReset', + desc: '', + args: [], + ); + } + + /// `Please verify the code you have entered` + String get pleaseVerifyTheCodeYouHaveEntered { + return Intl.message( + 'Please verify the code you have entered', + name: 'pleaseVerifyTheCodeYouHaveEntered', + desc: '', + args: [], + ); + } + + /// `Please contact support if the problem persists` + String get pleaseContactSupportIfTheProblemPersists { + return Intl.message( + 'Please contact support if the problem persists', + name: 'pleaseContactSupportIfTheProblemPersists', + desc: '', + args: [], + ); + } + + /// `Two-factor authentication has been disabled` + String get twofactorAuthenticationHasBeenDisabled { + return Intl.message( + 'Two-factor authentication has been disabled', + name: 'twofactorAuthenticationHasBeenDisabled', + desc: '', + args: [], + ); + } + + /// `Sorry, the code you've entered is incorrect` + String get sorryTheCodeYouveEnteredIsIncorrect { + return Intl.message( + 'Sorry, the code you\'ve entered is incorrect', + name: 'sorryTheCodeYouveEnteredIsIncorrect', + desc: '', + args: [], + ); + } + + /// `Your verification code has expired` + String get yourVerificationCodeHasExpired { + return Intl.message( + 'Your verification code has expired', + name: 'yourVerificationCodeHasExpired', + desc: '', + args: [], + ); + } + + /// `Email changed to {newEmail}` + String emailChangedTo(Object newEmail) { + return Intl.message( + 'Email changed to $newEmail', + name: 'emailChangedTo', + desc: '', + args: [newEmail], + ); + } + + /// `Verifying...` + String get verifying { + return Intl.message( + 'Verifying...', + name: 'verifying', + desc: '', + args: [], + ); + } + + /// `Disabling two-factor authentication...` + String get disablingTwofactorAuthentication { + return Intl.message( + 'Disabling two-factor authentication...', + name: 'disablingTwofactorAuthentication', + desc: '', + args: [], + ); + } + + /// `All memories preserved` + String get allMemoriesPreserved { + return Intl.message( + 'All memories preserved', + name: 'allMemoriesPreserved', + desc: '', + args: [], + ); + } + + /// `Loading gallery...` + String get loadingGallery { + return Intl.message( + 'Loading gallery...', + name: 'loadingGallery', + desc: '', + args: [], + ); + } + + /// `Syncing...` + String get syncing { + return Intl.message( + 'Syncing...', + name: 'syncing', + desc: '', + args: [], + ); + } + + /// `Encrypting backup...` + String get encryptingBackup { + return Intl.message( + 'Encrypting backup...', + name: 'encryptingBackup', + desc: '', + args: [], + ); + } + + /// `Sync stopped` + String get syncStopped { + return Intl.message( + 'Sync stopped', + name: 'syncStopped', + desc: '', + args: [], + ); + } + + /// `{completed}/{total} memories preserved` + String syncProgress(int completed, int total) { + return Intl.message( + '$completed/$total memories preserved', + name: 'syncProgress', + desc: 'Text to tell user how many memories have been preserved', + args: [completed, total], + ); + } + + /// `Archiving...` + String get archiving { + return Intl.message( + 'Archiving...', + name: 'archiving', + desc: '', + args: [], + ); + } + + /// `Unarchiving...` + String get unarchiving { + return Intl.message( + 'Unarchiving...', + name: 'unarchiving', + desc: '', + args: [], + ); + } + + /// `Successfully archived` + String get successfullyArchived { + return Intl.message( + 'Successfully archived', + name: 'successfullyArchived', + desc: '', + args: [], + ); + } + + /// `Successfully unarchived` + String get successfullyUnarchived { + return Intl.message( + 'Successfully unarchived', + name: 'successfullyUnarchived', + desc: '', + args: [], + ); + } + + /// `Rename file` + String get renameFile { + return Intl.message( + 'Rename file', + name: 'renameFile', + desc: '', + args: [], + ); + } + + /// `Enter file name` + String get enterFileName { + return Intl.message( + 'Enter file name', + name: 'enterFileName', + desc: '', + args: [], + ); + } + + /// `Files deleted` + String get filesDeleted { + return Intl.message( + 'Files deleted', + name: 'filesDeleted', + desc: '', + args: [], + ); + } + + /// `Selected files are not on ente` + String get selectedFilesAreNotOnEnte { + return Intl.message( + 'Selected files are not on ente', + name: 'selectedFilesAreNotOnEnte', + desc: '', + args: [], + ); + } + + /// `This action cannot be undone` + String get thisActionCannotBeUndone { + return Intl.message( + 'This action cannot be undone', + name: 'thisActionCannotBeUndone', + desc: '', + args: [], + ); + } + + /// `Empty trash?` + String get emptyTrash { + return Intl.message( + 'Empty trash?', + name: 'emptyTrash', + desc: '', + args: [], + ); + } + + /// `All items in trash will be permanently deleted\n\nThis action cannot be undone` + String get permDeleteWarning { + return Intl.message( + 'All items in trash will be permanently deleted\n\nThis action cannot be undone', + name: 'permDeleteWarning', + desc: '', + args: [], + ); + } + + /// `Empty` + String get empty { + return Intl.message( + 'Empty', + name: 'empty', + desc: '', + args: [], + ); + } + + /// `Could not free up space` + String get couldNotFreeUpSpace { + return Intl.message( + 'Could not free up space', + name: 'couldNotFreeUpSpace', + desc: '', + args: [], + ); + } + + /// `Permanently delete from device?` + String get permanentlyDeleteFromDevice { + return Intl.message( + 'Permanently delete from device?', + name: 'permanentlyDeleteFromDevice', + desc: '', + args: [], + ); + } + + /// `Some of the files you are trying to delete are only available on your device and cannot be recovered if deleted` + String get someOfTheFilesYouAreTryingToDeleteAre { + return Intl.message( + 'Some of the files you are trying to delete are only available on your device and cannot be recovered if deleted', + name: 'someOfTheFilesYouAreTryingToDeleteAre', + desc: '', + args: [], + ); + } + + /// `They will be deleted from all albums.` + String get theyWillBeDeletedFromAllAlbums { + return Intl.message( + 'They will be deleted from all albums.', + name: 'theyWillBeDeletedFromAllAlbums', + desc: '', + args: [], + ); + } + + /// `Some items are in both ente and your device.` + String get someItemsAreInBothEnteAndYourDevice { + return Intl.message( + 'Some items are in both ente and your device.', + name: 'someItemsAreInBothEnteAndYourDevice', + desc: '', + args: [], + ); + } + + /// `Selected items will be deleted from all albums and moved to trash.` + String get selectedItemsWillBeDeletedFromAllAlbumsAndMoved { + return Intl.message( + 'Selected items will be deleted from all albums and moved to trash.', + name: 'selectedItemsWillBeDeletedFromAllAlbumsAndMoved', + desc: '', + args: [], + ); + } + + /// `These items will be deleted from your device.` + String get theseItemsWillBeDeletedFromYourDevice { + return Intl.message( + 'These items will be deleted from your device.', + name: 'theseItemsWillBeDeletedFromYourDevice', + desc: '', + args: [], + ); + } + + /// `It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.` + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome { + return Intl.message( + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.', + name: 'itLooksLikeSomethingWentWrongPleaseRetryAfterSome', + desc: '', + args: [], + ); + } + + /// `Error` + String get error { + return Intl.message( + 'Error', + name: 'error', + desc: '', + args: [], + ); + } + + /// `It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.` + String get tempErrorContactSupportIfPersists { + return Intl.message( + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.', + name: 'tempErrorContactSupportIfPersists', + desc: '', + args: [], + ); + } + + /// `Cached data` + String get cachedData { + return Intl.message( + 'Cached data', + name: 'cachedData', + desc: '', + args: [], + ); + } + + /// `Clear caches` + String get clearCaches { + return Intl.message( + 'Clear caches', + name: 'clearCaches', + desc: '', + args: [], + ); + } + + /// `Remote images` + String get remoteImages { + return Intl.message( + 'Remote images', + name: 'remoteImages', + desc: '', + args: [], + ); + } + + /// `Remote videos` + String get remoteVideos { + return Intl.message( + 'Remote videos', + name: 'remoteVideos', + desc: '', + args: [], + ); + } + + /// `Remote thumbnails` + String get remoteThumbnails { + return Intl.message( + 'Remote thumbnails', + name: 'remoteThumbnails', + desc: '', + args: [], + ); + } + + /// `Pending sync` + String get pendingSync { + return Intl.message( + 'Pending sync', + name: 'pendingSync', + desc: '', + args: [], + ); + } + + /// `Local gallery` + String get localGallery { + return Intl.message( + 'Local gallery', + name: 'localGallery', + desc: '', + args: [], + ); + } + + /// `Today's logs` + String get todaysLogs { + return Intl.message( + 'Today\'s logs', + name: 'todaysLogs', + desc: '', + args: [], + ); + } + + /// `View logs` + String get viewLogs { + return Intl.message( + 'View logs', + name: 'viewLogs', + desc: '', + args: [], + ); + } + + /// `This will send across logs to help us debug your issue. Please note that file names will be included to help track issues with specific files.` + String get logsDialogBody { + return Intl.message( + 'This will send across logs to help us debug your issue. Please note that file names will be included to help track issues with specific files.', + name: 'logsDialogBody', + desc: '', + args: [], + ); + } + + /// `Preparing logs...` + String get preparingLogs { + return Intl.message( + 'Preparing logs...', + name: 'preparingLogs', + desc: '', + args: [], + ); + } + + /// `Email your logs` + String get emailYourLogs { + return Intl.message( + 'Email your logs', + name: 'emailYourLogs', + desc: '', + args: [], + ); + } + + /// `Please send the logs to \n{toEmail}` + String pleaseSendTheLogsTo(Object toEmail) { + return Intl.message( + 'Please send the logs to \n$toEmail', + name: 'pleaseSendTheLogsTo', + desc: '', + args: [toEmail], + ); + } + + /// `Copy email address` + String get copyEmailAddress { + return Intl.message( + 'Copy email address', + name: 'copyEmailAddress', + desc: '', + args: [], + ); + } + + /// `Export logs` + String get exportLogs { + return Intl.message( + 'Export logs', + name: 'exportLogs', + desc: '', + args: [], + ); + } + + /// `Please email us at {toEmail}` + String pleaseEmailUsAt(Object toEmail) { + return Intl.message( + 'Please email us at $toEmail', + name: 'pleaseEmailUsAt', + desc: '', + args: [toEmail], + ); + } + + /// `Dismiss` + String get dismiss { + return Intl.message( + 'Dismiss', + name: 'dismiss', + desc: '', + args: [], + ); + } + + /// `Did you know?` + String get didYouKnow { + return Intl.message( + 'Did you know?', + name: 'didYouKnow', + desc: '', + args: [], + ); + } + + /// `Loading your photos...` + String get loadingMessage { + return Intl.message( + 'Loading your photos...', + name: 'loadingMessage', + desc: '', + args: [], + ); + } + + /// `You can share your subscription with your family` + String get loadMessage1 { + return Intl.message( + 'You can share your subscription with your family', + name: 'loadMessage1', + desc: '', + args: [], + ); + } + + /// `We have preserved over 10 million memories so far` + String get loadMessage2 { + return Intl.message( + 'We have preserved over 10 million memories so far', + name: 'loadMessage2', + desc: '', + args: [], + ); + } + + /// `We keep 3 copies of your data, one in an underground fallout shelter` + String get loadMessage3 { + return Intl.message( + 'We keep 3 copies of your data, one in an underground fallout shelter', + name: 'loadMessage3', + desc: '', + args: [], + ); + } + + /// `All our apps are open source` + String get loadMessage4 { + return Intl.message( + 'All our apps are open source', + name: 'loadMessage4', + desc: '', + args: [], + ); + } + + /// `Our source code and cryptography have been externally audited` + String get loadMessage5 { + return Intl.message( + 'Our source code and cryptography have been externally audited', + name: 'loadMessage5', + desc: '', + args: [], + ); + } + + /// `You can share links to your albums with your loved ones` + String get loadMessage6 { + return Intl.message( + 'You can share links to your albums with your loved ones', + name: 'loadMessage6', + desc: '', + args: [], + ); + } + + /// `Our mobile apps run in the background to encrypt and backup any new photos you click` + String get loadMessage7 { + return Intl.message( + 'Our mobile apps run in the background to encrypt and backup any new photos you click', + name: 'loadMessage7', + desc: '', + args: [], + ); + } + + /// `web.ente.io has a slick uploader` + String get loadMessage8 { + return Intl.message( + 'web.ente.io has a slick uploader', + name: 'loadMessage8', + desc: '', + args: [], + ); + } + + /// `We use Xchacha20Poly1305 to safely encrypt your data` + String get loadMessage9 { + return Intl.message( + 'We use Xchacha20Poly1305 to safely encrypt your data', + name: 'loadMessage9', + desc: '', + args: [], + ); + } + + /// `Language` + String get language { + return Intl.message( + 'Language', + name: 'language', + desc: '', + args: [], + ); + } + + /// `Select Language` + String get selectLanguage { + return Intl.message( + 'Select Language', + name: 'selectLanguage', + desc: '', + args: [], + ); + } + + /// `Location name` + String get locationName { + return Intl.message( + 'Location name', + name: 'locationName', + desc: '', + args: [], + ); + } + + /// `Add location` + String get addLocation { + return Intl.message( + 'Add location', + name: 'addLocation', + desc: '', + args: [], + ); + } + + /// `Group nearby photos` + String get groupNearbyPhotos { + return Intl.message( + 'Group nearby photos', + name: 'groupNearbyPhotos', + desc: '', + args: [], + ); + } + + /// `Location` + String get location { + return Intl.message( + 'Location', + name: 'location', + desc: '', + args: [], + ); + } + + /// `km` + String get kiloMeterUnit { + return Intl.message( + 'km', + name: 'kiloMeterUnit', + desc: '', + args: [], + ); + } + + /// `Add` + String get addLocationButton { + return Intl.message( + 'Add', + name: 'addLocationButton', + desc: '', + args: [], + ); + } + + /// `Radius` + String get radius { + return Intl.message( + 'Radius', + name: 'radius', + desc: '', + args: [], + ); + } + + /// `A location tag groups all photos that were taken within some radius of a photo` + String get locationTagFeatureDescription { + return Intl.message( + 'A location tag groups all photos that were taken within some radius of a photo', + name: 'locationTagFeatureDescription', + desc: '', + args: [], + ); + } + + /// `Up to 1000 memories shown in gallery` + String get galleryMemoryLimitInfo { + return Intl.message( + 'Up to 1000 memories shown in gallery', + name: 'galleryMemoryLimitInfo', + desc: '', + args: [], + ); + } + + /// `Save` + String get save { + return Intl.message( + 'Save', + name: 'save', + desc: '', + args: [], + ); + } + + /// `Center point` + String get centerPoint { + return Intl.message( + 'Center point', + name: 'centerPoint', + desc: '', + args: [], + ); + } + + /// `Pick center point` + String get pickCenterPoint { + return Intl.message( + 'Pick center point', + name: 'pickCenterPoint', + desc: '', + args: [], + ); + } + + /// `Use selected photo` + String get useSelectedPhoto { + return Intl.message( + 'Use selected photo', + name: 'useSelectedPhoto', + desc: '', + args: [], + ); + } + + /// `Edit` + String get edit { + return Intl.message( + 'Edit', + name: 'edit', + desc: '', + args: [], + ); + } + + /// `Delete location` + String get deleteLocation { + return Intl.message( + 'Delete location', + name: 'deleteLocation', + desc: '', + args: [], + ); + } +} + +class AppLocalizationDelegate extends LocalizationsDelegate { + const AppLocalizationDelegate(); + + List get supportedLocales { + return const [ + Locale.fromSubtags(languageCode: 'en'), + Locale.fromSubtags(languageCode: 'cs'), + Locale.fromSubtags(languageCode: 'de'), + Locale.fromSubtags(languageCode: 'es'), + Locale.fromSubtags(languageCode: 'fr'), + Locale.fromSubtags(languageCode: 'it'), + Locale.fromSubtags(languageCode: 'ko'), + Locale.fromSubtags(languageCode: 'nl'), + Locale.fromSubtags(languageCode: 'pl'), + Locale.fromSubtags(languageCode: 'pt'), + ]; + } + + @override + bool isSupported(Locale locale) => _isSupported(locale); + @override + Future load(Locale locale) => S.load(locale); + @override + bool shouldReload(AppLocalizationDelegate old) => false; + + bool _isSupported(Locale locale) { + for (var supportedLocale in supportedLocales) { + if (supportedLocale.languageCode == locale.languageCode) { + return true; + } + } + return false; + } +} diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index d8995989b..0a4caf6f7 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -85,7 +85,7 @@ "ackPasswordLostWarning": "I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.", "privacyPolicyTitle": "Privacy Policy", "termsOfServicesTitle": "Terms", - "signUpTerms" : "I agree to the terms of service and privacy policy", + "signUpTerms": "I agree to the terms of service and privacy policy", "logInLabel": "Log in", "loginTerms": "By clicking log in, I agree to the terms of service and privacy policy", "changeEmail": "Change email", @@ -720,7 +720,7 @@ "calculating": "Calculating...", "pleaseWaitDeletingAlbum": "Please wait, deleting album", "searchHintText": "Albums, months, days, years, ...", - "searchByExamples": "\u2022 Album names (e.g. \"Camera\")\n\u2022 Types of files (e.g. \"Videos\", \".gif\")\n\u2022 Years and months (e.g. \"2022\", \"January\")\n\u2022 Holidays (e.g. \"Christmas\")\n\u2022 Photo descriptions (e.g. “#fun”)", + "searchByExamples": "• Album names (e.g. \"Camera\")\n• Types of files (e.g. \"Videos\", \".gif\")\n• Years and months (e.g. \"2022\", \"January\")\n• Holidays (e.g. \"Christmas\")\n• Photo descriptions (e.g. “#fun”)", "youCanTrySearchingForADifferentQuery": "You can try searching for a different query.", "noResultsFound": "No results found", "addedBy": "Added by {emailOrName}", @@ -755,7 +755,7 @@ "@filesBackedUpInAlbum": { "description": "Text to tell user how many files have been backed up in the album", "placeholders": { - "count" :{ + "count": { "example": "1", "type": "int" }, @@ -770,7 +770,7 @@ "@filesBackedUpFromDevice": { "description": "Text to tell user how many files have been backed up from this device", "placeholders": { - "count" :{ + "count": { "example": "1", "type": "int" }, @@ -787,7 +787,7 @@ "freeUpAccessPostDelete": "You can still access {count, plural, one {it} other {them}} on ente as long as you have an active subscription", "@freeUpAccessPostDelete": { "placeholders": { - "count" :{ + "count": { "example": "1", "type": "int" } @@ -820,7 +820,7 @@ "@syncProgress": { "description": "Text to tell user how many memories have been preserved", "placeholders": { - "completed" :{ + "completed": { "type": "int" }, "total": { @@ -877,5 +877,22 @@ "loadMessage6": "You can share links to your albums with your loved ones", "loadMessage7": "Our mobile apps run in the background to encrypt and backup any new photos you click", "loadMessage8": "web.ente.io has a slick uploader", - "loadMessage9": "We use Xchacha20Poly1305 to safely encrypt your data" -} + "loadMessage9": "We use Xchacha20Poly1305 to safely encrypt your data", + "language": "Language", + "selectLanguage": "Select Language", + "locationName": "Location name", + "addLocation": "Add location", + "groupNearbyPhotos": "Group nearby photos", + "location": "Location", + "kiloMeterUnit": "km", + "addLocationButton": "Add", + "radius": "Radius", + "locationTagFeatureDescription": "A location tag groups all photos that were taken within some radius of a photo", + "galleryMemoryLimitInfo": "Up to 1000 memories shown in gallery", + "save": "Save", + "centerPoint": "Center point", + "pickCenterPoint": "Pick center point", + "useSelectedPhoto": "Use selected photo", + "edit": "Edit", + "deleteLocation": "Delete location" +} \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 881a937fd..d129bcaed 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -96,6 +96,7 @@ "pleaseTryAgain": "Veuillez réessayer", "recreatePasswordTitle": "Recréer le mot de passe", "useRecoveryKey": "Utiliser la clé de récupération", + "recreatePasswordBody": "L'appareil actuel n'est pas assez puissant pour vérifier votre mot de passe, mais nous pouvons régénérer d'une manière qui fonctionne avec tous les appareils.\n\nVeuillez vous connecter à l'aide de votre clé de récupération et régénérer votre mot de passe (vous pouvez réutiliser le même si vous le souhaitez).", "verifyPassword": "Vérifier le mot de passe", "recoveryKey": "Clé de récupération", "recoveryKeyOnForgotPassword": "Si vous oubliez votre mot de passe, la seule façon de récupérer vos données sera grâce à cette clé.", @@ -182,6 +183,78 @@ "linkNeverExpires": "Jamais", "expiredLinkInfo": "Ce lien a expiré. Veuillez sélectionner un nouveau délai d'expiration ou désactiver l'expiration du lien.", "setAPassword": "Définir un mot de passe", + "lockButtonLabel": "Verrouiller", + "enterPassword": "Saisissez le mot de passe", + "removeLink": "Supprimer le lien", + "manageLink": "Gérer le lien", + "linkExpiresOn": "Le lien expirera le {expiryTime}", + "albumUpdated": "Album mis à jour", + "maxDeviceLimitSpikeHandling": "Lorsqu'elle est définie au maximum ({maxValue}), la limite de l'appareil sera assouplie pour permettre des pointes temporaires d'un grand nombre de téléspectateurs.", + "@maxDeviceLimitSpikeHandling": { + "placeholders": { + "maxValue": { + "type": "int", + "example": "100" + } + } + }, + "never": "Jamais", + "custom": "Personnaliser", + "@custom": { + "description": "Label for setting custom value for link expiry" + }, + "after1Hour": "Après 1 heure", + "after1Day": "Après 1 jour", + "after1Week": "Après 1 semaine", + "after1Month": "Après 1 mois", + "after1Year": "Après 1 an", + "manageParticipants": "Gérer", + "collabLinkSectionDescription": "Créez un lien pour permettre aux gens d'ajouter et de voir des photos dans votre album partagé sans avoir besoin d'une application ente ou d'un compte. Idéal pour collecter des photos d'événement.", + "collectPhotos": "Récupérer les photos", + "collaborativeLink": "Lien collaboratif", + "shareWithNonenteUsers": "Partager avec des utilisateurs non-ente", + "createPublicLink": "Créer un lien public", + "sendLink": "Envoyer le lien", + "copyLink": "Copier le lien", + "linkHasExpired": "Le lien a expiré", + "publicLinkEnabled": "Lien public activé", + "shareALink": "Partager le lien", + "sharedAlbumSectionDescription": "Créez des albums partagés et collaboratifs avec d'autres utilisateurs de ente, y compris des utilisateurs sur des plans gratuits.", + "shareWithPeopleSectionTitle": "{numberOfPeople, plural, =0 {Partagez avec des personnes spécifiques} =1 {Partagé avec 1 personne} other {Partagé avec {numberOfPeople} des gens}}", + "@shareWithPeopleSectionTitle": { + "placeholders": { + "numberOfPeople": { + "type": "int", + "example": "2" + } + } + }, + "thisIsYourVerificationId": "Ceci est votre ID de vérification", + "someoneSharingAlbumsWithYouShouldSeeTheSameId": "Quelqu'un qui partage des albums avec vous devrait voir le même ID sur son appareil.", + "howToViewShareeVerificationID": "Demandez-leur d'appuyer longuement sur leur adresse e-mail sur l'écran des paramètres et de vérifier que les identifiants des deux appareils correspondent.", + "thisIsPersonVerificationId": "Ceci est l'ID de vérification de {email}", + "@thisIsPersonVerificationId": { + "placeholders": { + "email": { + "type": "String", + "example": "someone@ente.io" + } + } + }, + "verificationId": "ID de vérification", + "verifyEmailID": "Vérifier {email}", + "emailNoEnteAccount": "{email} n'a pas de compte ente.\n\nEnvoyez une invitation pour partager des photos.", + "shareMyVerificationID": "Voici mon ID de vérification : {verificationID} pour ente.io.", + "shareTextConfirmOthersVerificationID": "Hé, pouvez-vous confirmer qu'il s'agit de votre ID de vérification ente.io : {verificationID}", + "somethingWentWrong": "Un problème est survenu", + "sendInvite": "Envoyer Invitations", + "shareTextRecommendUsingEnte": "Téléchargez ente pour que nous puissions facilement partager des photos et des vidéos de qualité originale\n\nhttps://ente.io/#download", + "done": "Terminé", + "applyCodeTitle": "Utiliser le code", + "enterCodeDescription": "Entrez le code fourni par votre ami pour réclamer de l'espace de stockage gratuit pour vous deux", + "apply": "Appliquer", + "failedToApplyCode": "Impossible d'appliquer le code", + "enterReferralCode": "Entrez le code de parrainage", "codeAppliedPageTitle": "Code appliqué", "storageInGB": "{storageAmountInGB} Go", "claimed": "Réclamée", @@ -195,5 +268,460 @@ "shareTextReferralCode": "code de parrainage ente : {referralCode} \n\nAppliquez le dans Paramètres → Général → Références pour obtenir {referralStorageInGB} Go gratuitement après votre inscription à un plan payant\n\nhttps://ente.io", "claimFreeStorage": "Réclamer le stockage gratuit", "inviteYourFriends": "Invite tes ami(e)s", - "failedToFetchReferralDetails": "Impossible de récupérer les détails du parrainage. Veuillez réessayer plus tard." + "failedToFetchReferralDetails": "Impossible de récupérer les détails du parrainage. Veuillez réessayer plus tard.", + "referralStep1": "1. Donnez ce code à vos amis", + "referralStep2": "2. Ils s'inscrivent à une offre payante", + "referralStep3": "3. Vous recevez tous les deux {storageInGB} GB* gratuits", + "referralsAreCurrentlyPaused": "Les recommandations sont actuellement en pause", + "youCanAtMaxDoubleYourStorage": "* Vous pouvez au maximum doubler votre espace de stockage", + "claimedStorageSoFar": "{isFamilyMember, select, true {Votre famille a demandé {storageAmountInGb} Gb jusqu'à présent} false {Vous avez réclamé {storageAmountInGb} Gb jusqu'à présent} other {Vous avez réclamé {storageAmountInGb} Gbjusqu'à présent!}}", + "@claimedStorageSoFar": { + "placeholders": { + "isFamilyMember": { + "type": "String", + "example": "true" + }, + "storageAmountInGb": { + "type": "int", + "example": "10" + } + } + }, + "faq": "FAQ", + "oopsSomethingWentWrong": "Oups, une erreur est arrivée", + "peopleUsingYourCode": "Personnes utilisant votre code", + "eligible": "éligible", + "total": "total", + "codeUsedByYou": "Code utilisé par vous", + "freeStorageClaimed": "Stockage gratuit réclamé", + "freeStorageUsable": "Stockage gratuit utilisable", + "usableReferralStorageInfo": "Le stockage utilisable est limité par votre offre actuelle. Le stockage excédentaire deviendra automatiquement utilisable lorsque vous mettez à niveau votre offre.", + "removeFromAlbumTitle": "Retirer de l'album ?", + "removeFromAlbum": "Retirer de l'album", + "itemsWillBeRemovedFromAlbum": "Les éléments sélectionnés seront supprimés de cet album", + "removeShareItemsWarning": "Certains des objets que vous êtes en train de retirer ont été ajoutés par d'autres personnes, vous perdrez l'accès vers ces objets", + "addingToFavorites": "Ajout aux favoris...", + "removingFromFavorites": "Suppression des favoris…", + "sorryCouldNotAddToFavorites": "Désolé, impossible d'ajouter aux favoris !", + "sorryCouldNotRemoveFromFavorites": "Désolé, impossible de supprimer des favoris !", + "subscribeToEnableSharing": "Il semble que votre abonnement ait expiré. Veuillez vous abonner pour activer le partage.", + "subscribe": "S'abonner", + "canOnlyRemoveFilesOwnedByYou": "Vous ne pouvez supprimer que les fichiers que vous possédez", + "deleteSharedAlbum": "Supprimer l'album partagé ?", + "deleteAlbum": "Supprimer l'album", + "deleteAlbumDialog": "Supprimer aussi les photos (et vidéos) présentes dans cet album depuis tous les autres albums dont ils font partie ?", + "deleteSharedAlbumDialogBody": "L'album sera supprimé pour tout le monde\n\nVous perdrez l'accès aux photos partagées dans cet album qui est détenues par d'autres personnes", + "yesRemove": "Oui, supprimer", + "creatingLink": "Création du lien...", + "removeWithQuestionMark": "Enlever?", + "removeParticipantBody": "{userEmail} sera retiré de cet album partagé\n\nToutes les photos ajoutées par eux seront également retirées de l'album", + "keepPhotos": "Conserver les photos", + "deletePhotos": "Supprimer des photos", + "inviteToEnte": "Inviter à ente", + "removePublicLink": "Supprimer le lien public", + "disableLinkMessage": "Cela supprimera le lien public pour accéder à \"{albumName}\".", + "sharing": "Partage...", + "youCannotShareWithYourself": "Vous ne pouvez pas partager avec vous-même", + "archive": "Archiver", + "createAlbumActionHint": "Appuyez longuement pour sélectionner des photos et cliquez sur + pour créer un album", + "importing": "Importation en cours...", + "failedToLoadAlbums": "Impossible de charger les albums", + "hidden": "Masqué", + "authToViewYourHiddenFiles": "Veuillez vous authentifier pour voir vos fichiers cachés", + "trash": "Corbeille", + "uncategorized": "Aucune catégorie", + "videoSmallCase": "vidéo", + "photoSmallCase": "photo", + "singleFileDeleteHighlight": "Il sera supprimé de tous les albums.", + "singleFileInBothLocalAndRemote": "Ce {fileType} est à la fois dans ente et votre appareil.", + "singleFileInRemoteOnly": "Ce {fileType} sera supprimé de ente.", + "singleFileDeleteFromDevice": "Ce {fileType} sera supprimé de votre appareil.", + "deleteFromEnte": "Supprimer de ente", + "yesDelete": "Oui, supprimer", + "movedToTrash": "Déplacé dans la corbeille", + "deleteFromDevice": "Supprimer de l'appareil", + "deleteFromBoth": "Supprimer des deux", + "newAlbum": "Nouvel album", + "albums": "Albums", + "memoryCount": "{count, plural, one{{count} mémoire} other{{count} souvenirs}}", + "@memoryCount": { + "description": "The text to display the number of memories", + "type": "text", + "placeholders": { + "count": { + "example": "1", + "type": "int" + } + } + }, + "selectedPhotos": "{count} sélectionné", + "@selectedPhotos": { + "description": "Display the number of selected photos", + "type": "text", + "placeholders": { + "count": { + "example": "5", + "type": "int" + } + } + }, + "selectedPhotosWithYours": "{count} sélectionné ({yourCount} votre)", + "@selectedPhotosWithYours": { + "description": "Display the number of selected photos, including the number of selected photos owned by the user", + "type": "text", + "placeholders": { + "count": { + "example": "12", + "type": "int" + }, + "yourCount": { + "example": "2", + "type": "int" + } + } + }, + "advancedSettings": "Avancé", + "@advancedSettings": { + "description": "The text to display in the advanced settings section" + }, + "photoGridSize": "Taille de la grille photo", + "manageDeviceStorage": "Gérer le stockage de l'appareil", + "selectFoldersForBackup": "Sélectionner les dossiers à sauvegarder", + "selectedFoldersWillBeEncryptedAndBackedUp": "Les dossiers sélectionnés seront cryptés et sauvegardés", + "unselectAll": "Désélectionner tout", + "selectAll": "Tout sélectionner", + "skip": "Ignorer", + "updatingFolderSelection": "Mise à jour de la sélection du dossier...", + "itemCount": "{count, plural, one{{count} objet} other{{count} objets}}", + "yearsAgo": "{count, plural, one{{count} il y a un an} other{{count} il y a des années}}", + "backupSettings": "Paramètres de la sauvegarde", + "backupOverMobileData": "Sauvegarde sur données mobiles", + "backupVideos": "Sauvegarde des vidéos", + "disableAutoLock": "Désactiver le verrouillage automatique", + "deviceLockExplanation": "Désactiver le verrouillage de l'écran de l'appareil lorsque ente est au premier plan et il y a une sauvegarde en cours. Ce n'est normalement pas nécessaire, mais peut aider les gros téléchargements et les premières importations de grandes bibliothèques plus rapidement.", + "about": "À propos", + "weAreOpenSource": "Nous sommes open source !", + "privacy": "Confidentialité", + "terms": "Conditions", + "checkForUpdates": "Vérifier les mises à jour", + "checking": "Vérification...", + "youAreOnTheLatestVersion": "Vous êtes sur la dernière version", + "account": "Compte", + "manageSubscription": "Gérer l'abonnement", + "authToChangeYourEmail": "Veuillez vous authentifier pour modifier votre adresse e-mail", + "changePassword": "Modifier le mot de passe", + "authToChangeYourPassword": "Veuillez vous authentifier pour modifier votre mot de passe", + "exportYourData": "Exportez vos données", + "logout": "Déconnexion", + "authToInitiateAccountDeletion": "Veuillez vous authentifier pour débuter la suppression du compte", + "areYouSureYouWantToLogout": "Voulez-vous vraiment vous déconnecter ?", + "yesLogout": "Oui, se déconnecter", + "aNewVersionOfEnteIsAvailable": "Une nouvelle version de Wire est disponible.", + "update": "Mise à jour", + "installManually": "Installation manuelle", + "criticalUpdateAvailable": "Mise à jour critique disponible", + "updateAvailable": "Une mise à jour est disponible", + "downloading": "Téléchargement en cours...", + "theDownloadCouldNotBeCompleted": "Le téléchargement n'a pas pu être terminé", + "retry": "Réessayer", + "backedUpFolders": "Dossiers sauvegardés", + "backup": "Sauvegarde", + "freeUpDeviceSpace": "Libérer de l'espace sur l'appareil", + "allClear": "✨ Tout est effacé", + "noDeviceThatCanBeDeleted": "Vous n'avez pas de fichiers sur cet appareil qui peuvent être supprimés", + "removeDuplicates": "Supprimer les doublons", + "noDuplicates": "✨ Aucun doublon", + "youveNoDuplicateFilesThatCanBeCleared": "Vous n'avez aucun fichier dédupliqué pouvant être nettoyé", + "success": "Succès", + "rateUs": "Évaluez-nous", + "remindToEmptyDeviceTrash": "Également vide \"récemment supprimé\" de \"Paramètres\" -> \"Stockage\" pour réclamer l'espace libéré", + "youHaveSuccessfullyFreedUp": "Vous avez libéré {storageSaved} avec succès !", + "@youHaveSuccessfullyFreedUp": { + "description": "The text to display when the user has successfully freed up storage", + "type": "text", + "placeholders": { + "storageSaved": { + "example": "1.2 GB", + "type": "String" + } + } + }, + "remindToEmptyEnteTrash": "Vide aussi votre \"Corbeille\" pour réclamer l'espace libéré", + "sparkleSuccess": "✨ Succès", + "duplicateFileCountWithStorageSaved": "Vous avez nettoyé {count, plural, one{{count} fichier dupliqué} other{{count} fichiers dupliqués}}, sauvegarde ({storageSaved}!)", + "@duplicateFileCountWithStorageSaved": { + "description": "The text to display when the user has successfully cleaned up duplicate files", + "type": "text", + "placeholders": { + "count": { + "example": "1", + "type": "int" + }, + "storageSaved": { + "example": "1.2 GB", + "type": "String" + } + } + }, + "familyPlans": "Forfaits famille", + "referrals": "Parrainages", + "advanced": "Avancé", + "general": "Général", + "security": "Sécurité", + "authToViewYourRecoveryKey": "Veuillez vous authentifier pour afficher votre clé de récupération", + "twofactor": "Double authentification", + "authToConfigureTwofactorAuthentication": "Veuillez vous authentifier pour configurer l'authentification à deux facteurs", + "lockscreen": "Ecran de vérouillage", + "authToChangeLockscreenSetting": "Veuillez vous authentifier pour modifier les paramètres de l'écran de verrouillage", + "lockScreenEnablePreSteps": "Pour activer l'écran de verrouillage, veuillez configurer le code d'accès de l'appareil ou le verrouillage de l'écran dans les paramètres de votre système.", + "viewActiveSessions": "Afficher les sessions actives", + "authToViewYourActiveSessions": "Veuillez vous authentifier pour voir vos sessions actives", + "disableTwofactor": "Désactiver la double-authentification", + "confirm2FADisable": "Voulez-vous vraiment désactiver l'authentification à deux facteurs ?", + "no": "Non", + "yes": "Oui", + "social": "Réseaux Sociaux", + "rateUsOnStore": "Notez-nous sur {storeName}", + "blog": "Blog", + "merchandise": "Marchandise", + "twitter": "Twitter", + "mastodon": "Mastodon", + "matrix": "Matrix", + "discord": "Discord", + "reddit": "Reddit", + "yourStorageDetailsCouldNotBeFetched": "Vos informations de stockage n'ont pas pu être récupérées", + "reportABug": "Signaler un bug", + "reportBug": "Signaler un bug", + "suggestFeatures": "Suggérer des fonctionnalités", + "support": "Support", + "theme": "Thème", + "lightTheme": "Clair", + "darkTheme": "Sombre", + "systemTheme": "Système", + "freeTrial": "Essai gratuit", + "selectYourPlan": "Sélectionner votre offre", + "enteSubscriptionPitch": "ente conserve vos souvenirs, donc ils sont toujours disponibles pour vous, même si vous perdez votre appareil.", + "enteSubscriptionShareWithFamily": "Vous pouvez également ajouter votre famille à votre forfait.", + "currentUsageIs": "L'utilisation actuelle est ", + "@currentUsageIs": { + "description": "This text is followed by storage usaged", + "examples": { + "0": "Current usage is 1.2 GB" + }, + "type": "text" + }, + "faqs": "FAQ", + "renewsOn": "Renouvellement le {endDate}", + "subWillBeCancelledOn": "Votre abonnement sera annulé le {endDate}", + "subscription": "Abonnement", + "paymentDetails": "Détails de paiement", + "manageFamily": "Gérer la famille", + "contactToManageSubscription": "Veuillez nous contacter à support@ente.io pour gérer votre abonnement {provider}.", + "renewSubscription": "Renouveler l’abonnement", + "cancelSubscription": "Annuler l'abonnement", + "areYouSureYouWantToRenew": "Êtes-vous sûr de vouloir renouveler ?", + "yesRenew": "Oui, renouveler", + "areYouSureYouWantToCancel": "Es-tu sûre de vouloir annuler?", + "yesCancel": "Oui, annuler", + "failedToRenew": "Échec du renouvellement", + "failedToCancel": "Échec de l'annulation", + "twoMonthsFreeOnYearlyPlans": "2 mois gratuits sur les forfaits annuels", + "monthly": "Mensuel", + "@monthly": { + "description": "The text to display for monthly plans", + "type": "text" + }, + "yearly": "Annuel", + "@yearly": { + "description": "The text to display for yearly plans", + "type": "text" + }, + "confirmPlanChange": "Confirmer le changement de l'offre", + "areYouSureYouWantToChangeYourPlan": "Êtes-vous certains de vouloir changer d'offre ?", + "youCannotDowngradeToThisPlan": "Vous ne pouvez pas rétrograder vers cette offre", + "cancelOtherSubscription": "Veuillez d'abord annuler votre abonnement existant de {paymentProvider}", + "@cancelOtherSubscription": { + "description": "The text to display when the user has an existing subscription from a different payment provider", + "type": "text", + "placeholders": { + "paymentProvider": { + "example": "Apple", + "type": "String" + } + } + }, + "optionalAsShortAsYouLike": "Optionnel, aussi court que vous le souhaitez...", + "send": "Envoyer", + "askCancelReason": "Votre abonnement a été annulé. Souhaitez-vous partager la raison ?", + "thankYouForSubscribing": "Merci de vous être abonné !", + "yourPurchaseWasSuccessful": "Votre achat a été effectué avec succès", + "yourPlanWasSuccessfullyUpgraded": "Votre offre a été mise à jour avec succès", + "yourPlanWasSuccessfullyDowngraded": "Votre plan a été rétrogradé avec succès", + "yourSubscriptionWasUpdatedSuccessfully": "Votre abonnement a été mis à jour avec succès", + "googlePlayId": "Identifiant Google Play", + "appleId": "Apple ID", + "playstoreSubscription": "Abonnement au PlayStore", + "thankYou": "Merci", + "pleaseWaitForSometimeBeforeRetrying": "Veuillez attendre quelque temps avant de réessayer", + "usePublicLinksForPeopleNotOnEnte": "Utiliser des liens publics pour les personnes qui ne sont pas sur ente", + "allowPeopleToAddPhotos": "Autoriser les personnes à ajouter des photos", + "shareAnAlbumNow": "Partagez un album maintenant", + "collectEventPhotos": "Collecter des photos de l'événement", + "sessionExpired": "Session expirée", + "loggingOut": "Deconnexion...", + "name": "Nom", + "newest": "Le plus récent", + "lastUpdated": "Dernière mise à jour", + "deleteEmptyAlbums": "Supprimer les albums vides", + "deleteEmptyAlbumsWithQuestionMark": "Supprimer les albums vides ?", + "deleteAlbumsDialogBody": "Ceci supprimera tous les albums vides. Ceci est utile lorsque vous voulez réduire l'encombrement dans votre liste d'albums.", + "deleteProgress": "Suppression de {currentlyDeleting} / {totalCount}", + "permanentlyDelete": "Supprimer définitivement", + "canOnlyCreateLinkForFilesOwnedByYou": "Ne peut créer de lien que pour les fichiers que vous possédez", + "publicLinkCreated": "Lien public créé", + "youCanManageYourLinksInTheShareTab": "Vous pouvez gérer vos liens dans l'onglet Partage.", + "linkCopiedToClipboard": "Lien copié dans le presse-papiers", + "restore": "Restaurer", + "@restore": { + "description": "Display text for an action which triggers a restore of item from trash", + "type": "text" + }, + "moveToAlbum": "Déplacer vers l'album", + "unhide": "Dévoiler", + "unarchive": "Désarchiver", + "favorite": "Favori", + "removeFromFavorite": "Retirer des favoris", + "shareLink": "Partager le lien", + "addToEnte": "Ajouter à ente", + "addToAlbum": "Ajouter à l'album", + "delete": "Supprimer", + "hide": "Masquer", + "itemSelectedCount": "{count} sélectionné", + "@itemSelectedCount": { + "description": "Text to indicate number of items selected", + "placeholders": { + "count": { + "example": "1|2|3", + "type": "int" + } + } + }, + "share": "Partager", + "unhideToAlbum": "Afficher dans l'album", + "restoreToAlbum": "Restaurer vers l'album", + "moveItem": "{count, plural, one {Déplacez l'objet} other {Déplacez des objets}}", + "@moveItem": { + "description": "Page title while moving one or more items to an album" + }, + "addItem": "{count, plural, one {Ajoutez un objet} other {Ajoutez des objets}}", + "@addItem": { + "description": "Page title while adding one or more items to album" + }, + "createOrSelectAlbum": "Créer ou sélectionner un album", + "selectAlbum": "Sélectionner album", + "searchByAlbumNameHint": "Nom de l'album", + "albumTitle": "Titre de l'album", + "enterAlbumName": "Saisir un nom d'album", + "restoringFiles": "Restauration des fichiers...", + "movingFilesToAlbum": "Déplacement des fichiers vers l'album...", + "unhidingFilesToAlbum": "Démasquage des fichiers vers l'album", + "canNotUploadToAlbumsOwnedByOthers": "Impossible de télécharger dans les albums appartenant à d'autres personnes", + "uploadingFilesToAlbum": "Envoi des fichiers vers l'album...", + "addedSuccessfullyTo": "Ajouté avec succès à {albumName}", + "movedSuccessfullyTo": "Déplacé avec succès vers {albumName}", + "thisAlbumAlreadyHDACollaborativeLink": "Cet album a déjà un lien collaboratif", + "collaborativeLinkCreatedFor": "Lien collaboratif créé pour {albumName}", + "askYourLovedOnesToShare": "Demandez à vos proches de partager", + "sharedWith": "Partagé avec {emailIDs}", + "sharedByMe": "Partagé par moi", + "doubleYourStorage": "Doubler votre espace de stockage", + "referFriendsAnd2xYourPlan": "Parrainez des amis et 2x votre abonnement", + "shareAlbumHint": "Ouvrez un album et appuyez sur le bouton de partage en haut à droite pour le partager.", + "itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion": "Les éléments montrent le nombre de jours restants avant la suppression définitive", + "deleteAll": "Tout Supprimer", + "renameAlbum": "Renommer l'album", + "rename": "Renommer", + "leaveSharedAlbum": "Quitter l'album partagé?", + "leaveAlbum": "Quitter l'album", + "photosAddedByYouWillBeRemovedFromTheAlbum": "Les photos ajoutées par vous seront retirées de l'album", + "youveNoFilesInThisAlbumThatCanBeDeleted": "Vous n'avez pas de fichiers dans cet album qui peuvent être supprimés", + "ignoredFolderUploadReason": "Certains fichiers de cet album sont ignorés parce qu'ils avaient été précédemment supprimés de ente.", + "resetIgnoredFiles": "Réinitialiser les fichiers ignorés", + "exif": "EXIF", + "noResults": "Aucun résultat", + "weDontSupportEditingPhotosAndAlbumsThatYouDont": "Nous ne prenons pas en charge l'édition des photos et des albums que vous ne possédez pas encore", + "failedToFetchOriginalForEdit": "Impossible de récupérer l'original pour l'édition", + "close": "Fermer", + "setAs": "Définir comme", + "fileSavedToGallery": "Fichier enregistré dans la galerie", + "download": "Télécharger", + "pressAndHoldToPlayVideo": "Appuyez et maintenez enfoncé pour lire la vidéo", + "downloadFailed": "Échec du téléchargement", + "deduplicateFiles": "Déduplication de fichiers", + "deselectAll": "Tout déselectionner", + "reviewDeduplicateItems": "Veuillez vérifier et supprimer les éléments que vous croyez dupliqués.", + "clubByCaptureTime": "Durée du Club par capture", + "totalSize": "Taille totale", + "emailChangedTo": "L'e-mail a été changé en {newEmail}", + "verifying": "Validation en cours...", + "disablingTwofactorAuthentication": "Désactiver la double-authentification...", + "allMemoriesPreserved": "Tous les souvenirs conservés", + "loadingGallery": "Chargement de la galerie...", + "syncing": "En cours de synchronisation...", + "encryptingBackup": "Chiffrement de la sauvegarde...", + "syncStopped": "Synchronisation arrêtée ?", + "syncProgress": "{completed}/{total} souvenirs préservés", + "@syncProgress": { + "description": "Text to tell user how many memories have been preserved", + "placeholders": { + "completed": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "archiving": "Archivage en cours...", + "unarchiving": "Désarchivage en cours...", + "successfullyArchived": "Archivé avec succès", + "successfullyUnarchived": "Désarchivé avec succès", + "renameFile": "Renommer le fichier", + "enterFileName": "Entrez le nom du fichier", + "filesDeleted": "Fichiers supprimés", + "selectedFilesAreNotOnEnte": "Les fichiers sélectionnés ne sont pas sur ente", + "thisActionCannotBeUndone": "Cette action ne peut pas être annulée", + "emptyTrash": "Vider la corbeille ?", + "permDeleteWarning": "Tous les éléments de la corbeille seront définitivement supprimés\n\nCette action ne peut pas être annulée", + "empty": "Vide", + "couldNotFreeUpSpace": "Impossible de libérer de l'espace", + "permanentlyDeleteFromDevice": "Supprimer définitivement de l'appareil ?", + "someOfTheFilesYouAreTryingToDeleteAre": "Certains des fichiers que vous essayez de supprimer ne sont disponibles que sur votre appareil et ne peuvent pas être récupérés s'ils sont supprimés", + "theyWillBeDeletedFromAllAlbums": "Ils seront supprimés de tous les albums.", + "someItemsAreInBothEnteAndYourDevice": "Certains éléments sont à la fois dans ente et votre appareil.", + "selectedItemsWillBeDeletedFromAllAlbumsAndMoved": "Les éléments sélectionnés seront supprimés de tous les albums et déplacés dans la corbeille.", + "theseItemsWillBeDeletedFromYourDevice": "Ces éléments seront supprimés de votre appareil.", + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Il semble qu'une erreur s'est produite. Veuillez réessayer après un certain temps. Si l'erreur persiste, veuillez contacter notre équipe d'assistance.", + "error": "Erreur", + "tempErrorContactSupportIfPersists": "Il semble qu'une erreur s'est produite. Veuillez réessayer après un certain temps. Si l'erreur persiste, veuillez contacter notre équipe d'assistance.", + "cachedData": "Données mises en cache", + "clearCaches": "Nettoyer le cache", + "remoteImages": "Images distantes", + "remoteVideos": "Vidéos distantes", + "remoteThumbnails": "Miniatures distantes", + "pendingSync": "Synchronisation en attente", + "localGallery": "Galerie locale", + "todaysLogs": "Journaux du jour", + "viewLogs": "Afficher les journaux", + "logsDialogBody": "Cela enverra des logs pour nous aider à déboguer votre problème. Veuillez noter que les noms de fichiers seront inclus pour aider à suivre les problèmes avec des fichiers spécifiques.", + "preparingLogs": "Préparation des journaux...", + "emailYourLogs": "Envoyez vos logs par e-mail", + "pleaseSendTheLogsTo": "Envoyez les logs à {toEmail}", + "copyEmailAddress": "Copier l’adresse e-mail", + "exportLogs": "Exporter les logs", + "pleaseEmailUsAt": "Merci de nous envoyer un e-mail à {toEmail}", + "dismiss": "Rejeter", + "didYouKnow": "Le savais-tu ?", + "loadingMessage": "Chargement de vos photos...", + "language": "Langue" } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index 23f945e2b..b7fe0ce33 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -96,6 +96,7 @@ "pleaseTryAgain": "Probeer het nog eens", "recreatePasswordTitle": "Wachtwoord opnieuw instellen", "useRecoveryKey": "Herstelcode gebruiken", + "recreatePasswordBody": "Het huidige apparaat is niet krachtig genoeg om je wachtwoord te verifiëren, dus moeten we de code een keer opnieuw genereren op een manier die met alle apparaten werkt.\n\nLog in met behulp van uw herstelcode en genereer opnieuw uw wachtwoord (je kunt dezelfde indien gewenst opnieuw gebruiken).", "verifyPassword": "Bevestig wachtwoord", "recoveryKey": "Herstelsleutel", "recoveryKeyOnForgotPassword": "Als je je wachtwoord vergeet, kun je alleen met deze sleutel je gegevens herstellen.", @@ -132,6 +133,7 @@ "verifyingRecoveryKey": "Herstelsleutel verifiëren...", "recoveryKeyVerified": "Herstel sleutel geverifieerd", "recoveryKeySuccessBody": "Super! Je herstelsleutel is geldig. Bedankt voor het verifiëren.\n\nVergeet niet om je herstelsleutel veilig te bewaren.", + "invalidRecoveryKey": "De herstelsleutel die je hebt ingevoerd is niet geldig. Zorg ervoor dat deze 24 woorden bevat en controleer de spelling van elk van deze woorden.\n\nAls je een oudere herstelcode hebt ingevoerd, zorg ervoor dat deze 64 tekens lang is, en controleer ze allemaal.", "invalidKey": "Ongeldige sleutel", "tryAgain": "Probeer opnieuw", "viewRecoveryKey": "Toon herstelsleutel", @@ -216,6 +218,38 @@ "copyLink": "Kopieer link", "linkHasExpired": "Link is vervallen", "publicLinkEnabled": "Publieke link ingeschakeld", + "shareALink": "Deel een link", + "sharedAlbumSectionDescription": "Maak gedeelde en collaboratieve albums met andere ente gebruikers, inclusief gebruikers met gratis abonnementen.", + "shareWithPeopleSectionTitle": "{numberOfPeople, plural, =0 {Deel met specifieke mensen} =1 {Gedeeld met 1 persoon} other {Gedeeld met {numberOfPeople} mensen}}", + "@shareWithPeopleSectionTitle": { + "placeholders": { + "numberOfPeople": { + "type": "int", + "example": "2" + } + } + }, + "thisIsYourVerificationId": "Dit is uw verificatie-ID", + "someoneSharingAlbumsWithYouShouldSeeTheSameId": "Iemand die albums met je deelt zou hetzelfde ID op hun apparaat moeten zien.", + "howToViewShareeVerificationID": "Vraag hen om hun e-mailadres lang in te drukken op het instellingenscherm en te controleren dat de ID's op beide apparaten overeenkomen.", + "thisIsPersonVerificationId": "Dit is de verificatie-ID van {email}", + "@thisIsPersonVerificationId": { + "placeholders": { + "email": { + "type": "String", + "example": "someone@ente.io" + } + } + }, + "verificationId": "Verificatie ID", + "verifyEmailID": "Verifieer {email}", + "emailNoEnteAccount": "{email} heeft geen ente account.\n\nStuur ze een uitnodiging om foto's te delen.", + "shareMyVerificationID": "Hier is mijn verificatie-ID: {verificationID} voor ente.io.", + "shareTextConfirmOthersVerificationID": "Hey, kunt u bevestigen dat dit uw ente.io verificatie-ID is: {verificationID}", + "somethingWentWrong": "Er ging iets mis", + "sendInvite": "Stuur een uitnodiging", + "done": "Voltooid", + "applyCodeTitle": "Code toepassen", "removeShareItemsWarning": "Sommige van de items die je verwijdert zijn door andere mensen toegevoegd, en je verliest de toegang daartoe", "addingToFavorites": "Toevoegen aan favorieten...", "removingFromFavorites": "Verwijderen uit favorieten...", @@ -340,5 +374,7 @@ "updateAvailable": "Update beschikbaar", "downloading": "Downloaden...", "theDownloadCouldNotBeCompleted": "De download kon niet worden voltooid", - "retry": "Opnieuw" + "retry": "Opnieuw", + "noDuplicates": "✨ Geen duplicaten", + "sparkleSuccess": "✨ Succes" } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 9e26dfeeb..17ba8d272 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1 +1,65 @@ -{} \ No newline at end of file +{ + "enterYourEmailAddress": "Podaj swój adres e-mail", + "accountWelcomeBack": "Witaj ponownie!", + "email": "Adres e-mail", + "cancel": "Anuluj", + "verify": "Weryfikuj", + "invalidEmailAddress": "Nieprawidłowy adres e-mail", + "enterValidEmail": "Podaj poprawny adres e-mail.", + "deleteAccount": "Usuń konto", + "askDeleteReason": "Jaka jest przyczyna usunięcia konta?", + "deleteAccountFeedbackPrompt": "Przykro nam, że odchodzisz. Wyjaśnij nam, dlaczego nas opuszczasz, aby pomóc ulepszać nasze usługi.", + "feedback": "Informacja zwrotna", + "kindlyHelpUsWithThisInformation": "Pomóż nam z tą informacją", + "confirmDeletePrompt": "Tak, chcę trwale usunąć konto i wszystkie dane z nim powiązane.", + "confirmAccountDeletion": "Potwierdź usunięcie konta", + "deleteConfirmDialogBody": "Zamierzasz trwale usunąć swoje konto i wszystkie jego dane.\nTa akcja jest nieodwracalna.", + "deleteAccountPermanentlyButton": "Usuń konto na stałe", + "yourAccountHasBeenDeleted": "Twoje konto zostało usunięte", + "selectReason": "Wybierz powód", + "deleteReason1": "Brakuje kluczowej funkcji, której potrzebuję", + "deleteReason2": "Aplikacja lub określona funkcja nie \nzachowuje się tak, jak sądzę, że powinna", + "deleteReason3": "Znalazłem inną, lepszą usługę", + "deleteReason4": "Inna, niewymieniona wyżej przyczyna", + "sendEmail": "Wyślij e-mail", + "deleteRequestSLAText": "Twoje żądanie zostanie przetworzone w ciągu 72 godzin.", + "deleteEmailRequest": "Wyślij wiadomość e-mail na account-deletion@ente.io z zarejestrowanego adresu e-mail.", + "ok": "Ok", + "createAccount": "Stwórz konto", + "createNewAccount": "Stwórz nowe konto", + "password": "Hasło", + "confirmPassword": "Powtórz hasło", + "activeSessions": "Aktywne sesje", + "oops": "Ups", + "somethingWentWrongPleaseTryAgain": "Coś poszło nie tak, spróbuj ponownie", + "thisWillLogYouOutOfThisDevice": "To wyloguje Cię z tego urządzenia!", + "thisWillLogYouOutOfTheFollowingDevice": "To wyloguje Cię z tego urządzenia:", + "terminateSession": "Zakończyć sesję?", + "terminate": "Zakończ", + "thisDevice": "To urządzenie", + "recoverButton": "Odzyskaj", + "recoverySuccessful": "Odzyskano pomyślnie!", + "decrypting": "Odszyfrowywanie...", + "incorrectRecoveryKeyTitle": "Nieprawidłowy klucz odzyskiwania", + "incorrectRecoveryKeyBody": "Kod jest nieprawidłowy", + "forgotPassword": "Nie pamiętam hasła", + "enterYourRecoveryKey": "Wprowadź swój klucz odzyskiwania", + "noRecoveryKey": "Brak klucza odzyskiwania?", + "sorry": "Przepraszamy", + "noRecoveryKeyNoDecryption": "Ze względu na charakter naszego protokołu szyfrowania end-to-end, dane nie mogą być odszyfrowane bez hasła lub klucza odzyskiwania", + "verifyEmail": "Zweryfikuj adres e-mail", + "checkInboxAndSpamFolder": "Sprawdź swoją skrzynkę odbiorczą (i spam), aby zakończyć weryfikację", + "resetPasswordTitle": "Zresetuj hasło", + "weakStrength": "Słabe", + "strongStrength": "Silne", + "pleaseWait": "Proszę czekać...", + "continueLabel": "Kontynuuj", + "termsOfServicesTitle": "Regulamin", + "logInLabel": "Zaloguj się", + "enterYourPassword": "Wprowadź hasło", + "welcomeBack": "Witaj ponownie!", + "doThisLater": "Spróbuj później", + "enterCode": "Wprowadź kod", + "confirm": "Potwierdź", + "tryAgain": "Spróbuj ponownie" +} \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 9e26dfeeb..d1ad73787 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1 +1,112 @@ -{} \ No newline at end of file +{ + "enterYourEmailAddress": "Insira o seu endereço de email", + "accountWelcomeBack": "Bem-vindo de volta!", + "cancel": "Cancelar", + "verify": "Verificar", + "invalidEmailAddress": "Endereço de email invalido", + "enterValidEmail": "Por, favor insira um endereço de email válido.", + "deleteAccount": "Deletar conta", + "askDeleteReason": "Qual é o principal motivo para você excluir sua conta?", + "deleteAccountFeedbackPrompt": "Lamentamos ver você partir. Por favor, compartilhe seus comentários para nos ajudar a melhorar.", + "feedback": "Opinião", + "kindlyHelpUsWithThisInformation": "Ajude-nos com esta informação", + "confirmDeletePrompt": "Sim, desejo excluir permanentemente esta conta e todos os seus dados.", + "confirmAccountDeletion": "Confirmar exclusão da conta", + "deleteConfirmDialogBody": "Você está prestes a excluir permanentemente sua conta e todos os seus dados.\nEsta ação é irreversível.", + "deleteAccountPermanentlyButton": "Excluir conta permanentemente", + "yourAccountHasBeenDeleted": "Sua conta foi deletada", + "selectReason": "Selecione o motivo", + "deleteReason1": "Está faltando um recurso-chave que eu preciso", + "deleteReason2": "O aplicativo ou um determinado recurso não\nestá funcionando como eu acredito que deveria", + "deleteReason3": "Encontrei outro serviço que gosto mais", + "deleteReason4": "Meu motivo não está listado", + "sendEmail": "Enviar email", + "deleteRequestSLAText": "Sua solicitação será processada em até 72 horas.", + "deleteEmailRequest": "Por favor, envie um email para account-deletion@ente.io a partir do seu endereço de email registrado.", + "createAccount": "Criar uma conta", + "createNewAccount": "Criar nova conta", + "password": "Senha", + "confirmPassword": "Confirme sua senha", + "activeSessions": "Sessões ativas", + "oops": "Ops", + "somethingWentWrongPleaseTryAgain": "Algo deu errado. Por favor, tente outra vez", + "thisWillLogYouOutOfThisDevice": "Isso fará com que você saia deste dispositivo!", + "thisWillLogYouOutOfTheFollowingDevice": "Isso fará com que você saia do seguinte dispositivo:", + "terminateSession": "Encerrar sessão?", + "terminate": "Terminar", + "thisDevice": "Este aparelho", + "recoverButton": "Recuperar", + "recoverySuccessful": "Recuperação bem sucedida!", + "decrypting": "Descriptografando...", + "incorrectRecoveryKeyTitle": "Chave de recuperação incorreta", + "incorrectRecoveryKeyBody": "A chave de recuperação que você digitou está incorreta", + "forgotPassword": "Esqueceu sua senha", + "enterYourRecoveryKey": "Digite sua chave de recuperação", + "noRecoveryKey": "Nenhuma chave de recuperação?", + "sorry": "Desculpe", + "noRecoveryKeyNoDecryption": "Devido à natureza do nosso protocolo de criptografia de ponta a ponta, seus dados não podem ser descriptografados sem sua senha ou chave de recuperação", + "verifyEmail": "Verificar email", + "checkInboxAndSpamFolder": "Verifique sua caixa de entrada (e ‘spam’) para concluir a verificação", + "enterCode": "Coloque o código", + "scanCode": "Escanear código", + "codeCopiedToClipboard": "Código copiado para a área de transferência", + "copypasteThisCodentoYourAuthenticatorApp": "Copie e cole este código\npara seu aplicativo autenticador", + "tapToCopy": "toque para copiar", + "scanThisBarcodeWithnyourAuthenticatorApp": "Escaneie este código de barras com\nseu aplicativo autenticador", + "enterThe6digitCodeFromnyourAuthenticatorApp": "Digite o código de 6 dígitos de\nseu aplicativo autenticador", + "confirm": "Confirme", + "setupComplete": "Configuração concluída", + "saveYourRecoveryKeyIfYouHaventAlready": "Salve sua chave de recuperação, caso ainda não o tenha feito", + "thisCanBeUsedToRecoverYourAccountIfYou": "Isso pode ser usado para recuperar sua conta se você perder seu segundo fator", + "twofactorAuthenticationPageTitle": "Autenticação de dois fatores", + "lostDevice": "Dispositivo perdido?", + "verifyingRecoveryKey": "Verificando chave de recuperação...", + "recoveryKeyVerified": "Chave de recuperação verificada", + "recoveryKeySuccessBody": "Ótimo! Sua chave de recuperação é válida. Obrigado por verificar.\n\nLembre-se de manter o backup seguro de sua chave de recuperação.", + "invalidRecoveryKey": "A chave de recuperação que você digitou não é válida. Certifique-se de que contém 24 palavras e verifique a ortografia de cada uma.\n\nSe você inseriu um código de recuperação mais antigo, verifique se ele tem 64 caracteres e verifique cada um deles.", + "invalidKey": "Chave inválida", + "tryAgain": "Tente novamente", + "viewRecoveryKey": "Ver chave de recuperação", + "confirmRecoveryKey": "Confirme a chave de recuperação", + "recoveryKeyVerifyReason": "Sua chave de recuperação é a única maneira de recuperar suas fotos se você esquecer sua senha. Você pode encontrar sua chave de recuperação em Configurações > Conta.\n\nDigite sua chave de recuperação aqui para verificar se você a salvou corretamente.", + "confirmYourRecoveryKey": "Confirme sua chave de recuperação", + "addViewer": "Adicionar visualizador", + "addCollaborator": "Adicionar colaborador", + "addANewEmail": "Adicionar um novo email", + "orPickAnExistingOne": "Ou escolha um existente", + "collaboratorsCanAddPhotosAndVideosToTheSharedAlbum": "Os colaboradores podem adicionar fotos e vídeos ao álbum compartilhado.", + "enterEmail": "Digite o email", + "albumOwner": "Proprietário", + "@albumOwner": { + "description": "Role of the album owner" + }, + "you": "Você", + "collaborator": "Colaborador", + "addMore": "Adicione mais", + "@addMore": { + "description": "Button text to add more collaborators/viewers" + }, + "viewer": "Visualizador", + "remove": "Remover", + "removeParticipant": "Remover participante", + "@removeParticipant": { + "description": "menuSectionTitle for removing a participant" + }, + "manage": "Gerenciar", + "addedAs": "Adicionado como", + "changePermissions": "Alterar permissões?", + "yesConvertToViewer": "Sim, converter para visualizador", + "cannotAddMorePhotosAfterBecomingViewer": "{user} Não poderá adicionar mais fotos a este álbum\n\nEles ainda poderão remover as fotos existentes adicionadas por eles", + "allowAddingPhotos": "Permitir adicionar fotos", + "@allowAddingPhotos": { + "description": "Switch button to enable uploading photos to a public link" + }, + "allowAddPhotosDescription": "Permita que as pessoas com o link também adicionem fotos ao álbum compartilhado.", + "passwordLock": "Bloqueio de senha", + "disableDownloadWarningTitle": "Observe", + "disableDownloadWarningBody": "Os espectadores ainda podem tirar screenshots ou salvar uma cópia de suas fotos usando ferramentas externas", + "allowDownloads": "Permitir transferências", + "linkDeviceLimit": "Limite do dispositivo", + "linkExpiry": "Expiração do link", + "linkExpired": "Expirado" +} \ No newline at end of file diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 8b800dfe1..3b3287191 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -1,5 +1,7 @@ +import "package:flutter/foundation.dart"; import "package:flutter/widgets.dart"; import "package:photos/generated/l10n.dart"; +import "package:shared_preferences/shared_preferences.dart"; extension AppLocalizationsX on BuildContext { S get l10n => S.of(this); @@ -8,9 +10,11 @@ extension AppLocalizationsX on BuildContext { // list of locales which are enabled for auth app. // Add more language to the list only when at least 90% of the strings are // translated in the corresponding language. -const List appSupportedLocales = [ - Locale('en'), -]; +const List appSupportedLocales = kDebugMode + ? [Locale('en'), Locale('fr'), Locale("nl")] + : [ + Locale('en'), + ]; Locale localResolutionCallBack(locales, supportedLocales) { for (Locale locale in locales) { @@ -21,3 +25,21 @@ Locale localResolutionCallBack(locales, supportedLocales) { // if device language is not supported by the app, use en as default return const Locale('en'); } + +Future getLocale() async { + final String? savedLocale = + (await SharedPreferences.getInstance()).getString('locale'); + if (savedLocale != null && + appSupportedLocales.contains(Locale(savedLocale))) { + return Locale(savedLocale); + } + return const Locale('en'); +} + +Future setLocale(Locale locale) async { + if (!appSupportedLocales.contains(locale)) { + throw Exception('Locale $locale is not supported by the app'); + } + await (await SharedPreferences.getInstance()) + .setString('locale', locale.languageCode); +} diff --git a/lib/main.dart b/lib/main.dart index 510ab1698..cd73a77f9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,13 +17,16 @@ import 'package:photos/core/errors.dart'; import 'package:photos/core/network/network.dart'; import 'package:photos/db/upload_locks_db.dart'; import 'package:photos/ente_theme_data.dart'; +import "package:photos/l10n/l10n.dart"; import 'package:photos/services/app_lifecycle_service.dart'; import 'package:photos/services/billing_service.dart'; import 'package:photos/services/collections_service.dart'; +import "package:photos/services/entity_service.dart"; import 'package:photos/services/favorites_service.dart'; import 'package:photos/services/feature_flag_service.dart'; import 'package:photos/services/local_file_update_service.dart'; import 'package:photos/services/local_sync_service.dart'; +import "package:photos/services/location_service.dart"; import 'package:photos/services/memories_service.dart'; import 'package:photos/services/notification_service.dart'; import "package:photos/services/object_detection/object_detection_service.dart"; @@ -67,13 +70,15 @@ Future _runInForeground(AdaptiveThemeMode? savedThemeMode) async { return await _runWithLogs(() async { _logger.info("Starting app in foreground"); await _init(false, via: 'mainMethod'); + final Locale locale = await getLocale(); unawaited(_scheduleFGSync('appStart in FG')); runApp( AppLock( builder: (args) => - EnteApp(_runBackgroundTask, _killBGTask, savedThemeMode), + EnteApp(_runBackgroundTask, _killBGTask, locale, savedThemeMode), lockScreen: const LockScreen(), enabled: Configuration.instance.shouldShowLockScreen(), + locale: locale, lightTheme: lightThemeData, darkTheme: darkThemeData, backgroundLockLatency: kBackgroundLockLatency, @@ -153,6 +158,9 @@ Future _init(bool isBackground, {String via = ''}) async { await NetworkClient.instance.init(); await Configuration.instance.init(); await UserService.instance.init(); + await EntityService.instance.init(); + LocationService.instance.init(preferences); + await UserRemoteFlagService.instance.init(); await UpdateService.instance.init(); BillingService.instance.init(); diff --git a/lib/models/api/entity/data.dart b/lib/models/api/entity/data.dart new file mode 100644 index 000000000..b46b3bdb7 --- /dev/null +++ b/lib/models/api/entity/data.dart @@ -0,0 +1,55 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +@immutable +class EntityData { + final String id; + + // encryptedData will be null for diff items when item is deleted + final String? encryptedData; + final String? header; + final bool isDeleted; + final int createdAt; + final int updatedAt; + final int userID; + + const EntityData( + this.id, + this.userID, + this.encryptedData, + this.header, + this.isDeleted, + this.createdAt, + this.updatedAt, + ); + + Map toMap() { + return { + 'id': id, + 'userID': userID, + 'encryptedData': encryptedData, + 'header': header, + 'isDeleted': isDeleted, + 'createdAt': createdAt, + 'updatedAt': updatedAt, + }; + } + + factory EntityData.fromMap(Map map) { + return EntityData( + map['id'], + map['userID'], + map['encryptedData'], + map['header'], + map['isDeleted']!, + map['createdAt']!, + map['updatedAt']!, + ); + } + + String toJson() => json.encode(toMap()); + + factory EntityData.fromJson(String source) => + EntityData.fromMap(json.decode(source)); +} diff --git a/lib/models/api/entity/key.dart b/lib/models/api/entity/key.dart new file mode 100644 index 000000000..58e53e042 --- /dev/null +++ b/lib/models/api/entity/key.dart @@ -0,0 +1,46 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import "package:photos/models/api/entity/type.dart"; + +@immutable +class EntityKey { + final int userID; + final String encryptedKey; + final EntityType type; + final String header; + final int createdAt; + + const EntityKey( + this.userID, + this.encryptedKey, + this.header, + this.createdAt, + this.type, + ); + + Map toMap() { + return { + 'userID': userID, + 'type': type.typeToString(), + 'encryptedKey': encryptedKey, + 'header': header, + 'createdAt': createdAt, + }; + } + + factory EntityKey.fromMap(Map map) { + return EntityKey( + map['userID']?.toInt() ?? 0, + map['encryptedKey']!, + map['header']!, + map['createdAt']?.toInt() ?? 0, + typeFromString(map['type']!), + ); + } + + String toJson() => json.encode(toMap()); + + factory EntityKey.fromJson(String source) => + EntityKey.fromMap(json.decode(source)); +} diff --git a/lib/models/api/entity/type.dart b/lib/models/api/entity/type.dart new file mode 100644 index 000000000..3631792de --- /dev/null +++ b/lib/models/api/entity/type.dart @@ -0,0 +1,26 @@ +import "package:flutter/foundation.dart"; + +enum EntityType { + location, + unknown, +} + +EntityType typeFromString(String type) { + switch (type) { + case "location": + return EntityType.location; + } + debugPrint("unexpected collection type $type"); + return EntityType.unknown; +} + +extension EntityTypeExtn on EntityType { + String typeToString() { + switch (this) { + case EntityType.location: + return "location"; + case EntityType.unknown: + return "unknown"; + } + } +} diff --git a/lib/models/file.dart b/lib/models/file.dart index 6035d1daa..8c0861ecc 100644 --- a/lib/models/file.dart +++ b/lib/models/file.dart @@ -8,7 +8,7 @@ import 'package:photos/core/configuration.dart'; import 'package:photos/core/constants.dart'; import 'package:photos/models/ente_file.dart'; import 'package:photos/models/file_type.dart'; -import 'package:photos/models/location.dart'; +import 'package:photos/models/location/location.dart'; import 'package:photos/models/magic_metadata.dart'; import 'package:photos/services/feature_flag_service.dart'; import 'package:photos/utils/date_time_util.dart'; @@ -72,7 +72,8 @@ class File extends EnteFile { file.localID = asset.id; file.title = asset.title; file.deviceFolder = pathName; - file.location = Location(asset.latitude, asset.longitude); + file.location = + Location(latitude: asset.latitude, longitude: asset.longitude); file.fileType = _fileTypeFromAsset(asset); file.creationTime = parseFileCreationTime(file.title, asset); file.modificationTime = asset.modifiedDateTime.microsecondsSinceEpoch; @@ -147,7 +148,7 @@ class File extends EnteFile { if (latitude == null || longitude == null) { location = null; } else { - location = Location(latitude, longitude); + location = Location(latitude: latitude, longitude: longitude); } fileType = getFileType(metadata["fileType"] ?? -1); fileSubType = metadata["subType"] ?? -1; diff --git a/lib/models/gallery_type.dart b/lib/models/gallery_type.dart index d93e42faf..ad29bf7c6 100644 --- a/lib/models/gallery_type.dart +++ b/lib/models/gallery_type.dart @@ -9,7 +9,8 @@ enum GalleryType { // indicator for gallery view of collections shared with the user sharedCollection, ownedCollection, - searchResults + searchResults, + locationTag, } extension GalleyTypeExtension on GalleryType { @@ -21,6 +22,7 @@ extension GalleyTypeExtension on GalleryType { case GalleryType.ownedCollection: case GalleryType.searchResults: case GalleryType.favorite: + case GalleryType.locationTag: return true; case GalleryType.hidden: @@ -45,6 +47,7 @@ extension GalleyTypeExtension on GalleryType { case GalleryType.homepage: case GalleryType.trash: case GalleryType.sharedCollection: + case GalleryType.locationTag: return false; } } @@ -59,6 +62,7 @@ extension GalleyTypeExtension on GalleryType { case GalleryType.favorite: case GalleryType.localFolder: case GalleryType.uncategorized: + case GalleryType.locationTag: return true; case GalleryType.trash: case GalleryType.archive: @@ -78,6 +82,7 @@ extension GalleyTypeExtension on GalleryType { case GalleryType.archive: case GalleryType.hidden: case GalleryType.localFolder: + case GalleryType.locationTag: return true; case GalleryType.trash: case GalleryType.sharedCollection: @@ -93,6 +98,7 @@ extension GalleyTypeExtension on GalleryType { case GalleryType.favorite: case GalleryType.archive: case GalleryType.uncategorized: + case GalleryType.locationTag: return true; case GalleryType.hidden: case GalleryType.localFolder: @@ -115,6 +121,7 @@ extension GalleyTypeExtension on GalleryType { case GalleryType.archive: case GalleryType.localFolder: case GalleryType.trash: + case GalleryType.locationTag: return false; } } @@ -133,6 +140,7 @@ extension GalleyTypeExtension on GalleryType { case GalleryType.localFolder: case GalleryType.trash: case GalleryType.sharedCollection: + case GalleryType.locationTag: return false; } } @@ -148,6 +156,7 @@ extension GalleyTypeExtension on GalleryType { case GalleryType.searchResults: case GalleryType.archive: case GalleryType.uncategorized: + case GalleryType.locationTag: return true; case GalleryType.hidden: @@ -169,6 +178,7 @@ extension GalleyTypeExtension on GalleryType { case GalleryType.homepage: case GalleryType.searchResults: case GalleryType.uncategorized: + case GalleryType.locationTag: return true; case GalleryType.hidden: diff --git a/lib/models/local_entity_data.dart b/lib/models/local_entity_data.dart new file mode 100644 index 000000000..9066e16fd --- /dev/null +++ b/lib/models/local_entity_data.dart @@ -0,0 +1,48 @@ +import "package:equatable/equatable.dart"; +import "package:photos/models/api/entity/type.dart"; + +class LocalEntityData { + final String id; + final EntityType type; + final String data; + final int ownerID; + final int updatedAt; + + LocalEntityData({ + required this.id, + required this.type, + required this.data, + required this.ownerID, + required this.updatedAt, + }); + + Map toJson() { + return { + "id": id, + "type": type.typeToString(), + "data": data, + "ownerID": ownerID, + "updatedAt": updatedAt, + }; + } + + factory LocalEntityData.fromJson(Map json) { + return LocalEntityData( + id: json["id"], + type: typeFromString(json["type"]), + data: json["data"], + ownerID: json["ownerID"] as int, + updatedAt: json["updatedAt"] as int, + ); + } +} + +class LocalEntity extends Equatable { + final T item; + final String id; + + const LocalEntity(this.item, this.id); + + @override + List get props => [item, id]; +} diff --git a/lib/models/location.dart b/lib/models/location.dart deleted file mode 100644 index e81964322..000000000 --- a/lib/models/location.dart +++ /dev/null @@ -1,9 +0,0 @@ -class Location { - final double? latitude; - final double? longitude; - - Location(this.latitude, this.longitude); - - @override - String toString() => 'Location(latitude: $latitude, longitude: $longitude)'; -} diff --git a/lib/models/location/location.dart b/lib/models/location/location.dart new file mode 100644 index 000000000..1349aba44 --- /dev/null +++ b/lib/models/location/location.dart @@ -0,0 +1,16 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'location.freezed.dart'; + +part 'location.g.dart'; + +@freezed +class Location with _$Location { + const factory Location({ + required double? latitude, + required double? longitude, + }) = _Location; + + factory Location.fromJson(Map json) => + _$LocationFromJson(json); +} diff --git a/lib/models/location/location.freezed.dart b/lib/models/location/location.freezed.dart new file mode 100644 index 000000000..e3cc1a19d --- /dev/null +++ b/lib/models/location/location.freezed.dart @@ -0,0 +1,168 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'location.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +Location _$LocationFromJson(Map json) { + return _Location.fromJson(json); +} + +/// @nodoc +mixin _$Location { + double? get latitude => throw _privateConstructorUsedError; + double? get longitude => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $LocationCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LocationCopyWith<$Res> { + factory $LocationCopyWith(Location value, $Res Function(Location) then) = + _$LocationCopyWithImpl<$Res, Location>; + @useResult + $Res call({double? latitude, double? longitude}); +} + +/// @nodoc +class _$LocationCopyWithImpl<$Res, $Val extends Location> + implements $LocationCopyWith<$Res> { + _$LocationCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? latitude = freezed, + Object? longitude = freezed, + }) { + return _then(_value.copyWith( + latitude: freezed == latitude + ? _value.latitude + : latitude // ignore: cast_nullable_to_non_nullable + as double?, + longitude: freezed == longitude + ? _value.longitude + : longitude // ignore: cast_nullable_to_non_nullable + as double?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_LocationCopyWith<$Res> implements $LocationCopyWith<$Res> { + factory _$$_LocationCopyWith( + _$_Location value, $Res Function(_$_Location) then) = + __$$_LocationCopyWithImpl<$Res>; + @override + @useResult + $Res call({double? latitude, double? longitude}); +} + +/// @nodoc +class __$$_LocationCopyWithImpl<$Res> + extends _$LocationCopyWithImpl<$Res, _$_Location> + implements _$$_LocationCopyWith<$Res> { + __$$_LocationCopyWithImpl( + _$_Location _value, $Res Function(_$_Location) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? latitude = freezed, + Object? longitude = freezed, + }) { + return _then(_$_Location( + latitude: freezed == latitude + ? _value.latitude + : latitude // ignore: cast_nullable_to_non_nullable + as double?, + longitude: freezed == longitude + ? _value.longitude + : longitude // ignore: cast_nullable_to_non_nullable + as double?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_Location implements _Location { + const _$_Location({required this.latitude, required this.longitude}); + + factory _$_Location.fromJson(Map json) => + _$$_LocationFromJson(json); + + @override + final double? latitude; + @override + final double? longitude; + + @override + String toString() { + return 'Location(latitude: $latitude, longitude: $longitude)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_Location && + (identical(other.latitude, latitude) || + other.latitude == latitude) && + (identical(other.longitude, longitude) || + other.longitude == longitude)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, latitude, longitude); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_LocationCopyWith<_$_Location> get copyWith => + __$$_LocationCopyWithImpl<_$_Location>(this, _$identity); + + @override + Map toJson() { + return _$$_LocationToJson( + this, + ); + } +} + +abstract class _Location implements Location { + const factory _Location( + {required final double? latitude, + required final double? longitude}) = _$_Location; + + factory _Location.fromJson(Map json) = _$_Location.fromJson; + + @override + double? get latitude; + @override + double? get longitude; + @override + @JsonKey(ignore: true) + _$$_LocationCopyWith<_$_Location> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/location/location.g.dart b/lib/models/location/location.g.dart new file mode 100644 index 000000000..fe91798f9 --- /dev/null +++ b/lib/models/location/location.g.dart @@ -0,0 +1,18 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'location.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_Location _$$_LocationFromJson(Map json) => _$_Location( + latitude: (json['latitude'] as num?)?.toDouble(), + longitude: (json['longitude'] as num?)?.toDouble(), + ); + +Map _$$_LocationToJson(_$_Location instance) => + { + 'latitude': instance.latitude, + 'longitude': instance.longitude, + }; diff --git a/lib/models/location_tag/location_tag.dart b/lib/models/location_tag/location_tag.dart new file mode 100644 index 000000000..1901013d9 --- /dev/null +++ b/lib/models/location_tag/location_tag.dart @@ -0,0 +1,25 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import "package:photos/core/constants.dart"; +import 'package:photos/models/location/location.dart'; + +part 'location_tag.freezed.dart'; +part 'location_tag.g.dart'; + +@freezed +class LocationTag with _$LocationTag { + const LocationTag._(); + const factory LocationTag({ + required String name, + required int radius, + required double aSquare, + required double bSquare, + required Location centerPoint, + }) = _LocationTag; + + factory LocationTag.fromJson(Map json) => + _$LocationTagFromJson(json); + + int get radiusIndex { + return radiusValues.indexOf(radius); + } +} diff --git a/lib/models/location_tag/location_tag.freezed.dart b/lib/models/location_tag/location_tag.freezed.dart new file mode 100644 index 000000000..88a88b483 --- /dev/null +++ b/lib/models/location_tag/location_tag.freezed.dart @@ -0,0 +1,252 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'location_tag.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +LocationTag _$LocationTagFromJson(Map json) { + return _LocationTag.fromJson(json); +} + +/// @nodoc +mixin _$LocationTag { + String get name => throw _privateConstructorUsedError; + int get radius => throw _privateConstructorUsedError; + double get aSquare => throw _privateConstructorUsedError; + double get bSquare => throw _privateConstructorUsedError; + Location get centerPoint => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $LocationTagCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LocationTagCopyWith<$Res> { + factory $LocationTagCopyWith( + LocationTag value, $Res Function(LocationTag) then) = + _$LocationTagCopyWithImpl<$Res, LocationTag>; + @useResult + $Res call( + {String name, + int radius, + double aSquare, + double bSquare, + Location centerPoint}); + + $LocationCopyWith<$Res> get centerPoint; +} + +/// @nodoc +class _$LocationTagCopyWithImpl<$Res, $Val extends LocationTag> + implements $LocationTagCopyWith<$Res> { + _$LocationTagCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? radius = null, + Object? aSquare = null, + Object? bSquare = null, + Object? centerPoint = null, + }) { + return _then(_value.copyWith( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + radius: null == radius + ? _value.radius + : radius // ignore: cast_nullable_to_non_nullable + as int, + aSquare: null == aSquare + ? _value.aSquare + : aSquare // ignore: cast_nullable_to_non_nullable + as double, + bSquare: null == bSquare + ? _value.bSquare + : bSquare // ignore: cast_nullable_to_non_nullable + as double, + centerPoint: null == centerPoint + ? _value.centerPoint + : centerPoint // ignore: cast_nullable_to_non_nullable + as Location, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $LocationCopyWith<$Res> get centerPoint { + return $LocationCopyWith<$Res>(_value.centerPoint, (value) { + return _then(_value.copyWith(centerPoint: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$_LocationTagCopyWith<$Res> + implements $LocationTagCopyWith<$Res> { + factory _$$_LocationTagCopyWith( + _$_LocationTag value, $Res Function(_$_LocationTag) then) = + __$$_LocationTagCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String name, + int radius, + double aSquare, + double bSquare, + Location centerPoint}); + + @override + $LocationCopyWith<$Res> get centerPoint; +} + +/// @nodoc +class __$$_LocationTagCopyWithImpl<$Res> + extends _$LocationTagCopyWithImpl<$Res, _$_LocationTag> + implements _$$_LocationTagCopyWith<$Res> { + __$$_LocationTagCopyWithImpl( + _$_LocationTag _value, $Res Function(_$_LocationTag) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? radius = null, + Object? aSquare = null, + Object? bSquare = null, + Object? centerPoint = null, + }) { + return _then(_$_LocationTag( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + radius: null == radius + ? _value.radius + : radius // ignore: cast_nullable_to_non_nullable + as int, + aSquare: null == aSquare + ? _value.aSquare + : aSquare // ignore: cast_nullable_to_non_nullable + as double, + bSquare: null == bSquare + ? _value.bSquare + : bSquare // ignore: cast_nullable_to_non_nullable + as double, + centerPoint: null == centerPoint + ? _value.centerPoint + : centerPoint // ignore: cast_nullable_to_non_nullable + as Location, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_LocationTag extends _LocationTag { + const _$_LocationTag( + {required this.name, + required this.radius, + required this.aSquare, + required this.bSquare, + required this.centerPoint}) + : super._(); + + factory _$_LocationTag.fromJson(Map json) => + _$$_LocationTagFromJson(json); + + @override + final String name; + @override + final int radius; + @override + final double aSquare; + @override + final double bSquare; + @override + final Location centerPoint; + + @override + String toString() { + return 'LocationTag(name: $name, radius: $radius, aSquare: $aSquare, bSquare: $bSquare, centerPoint: $centerPoint)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_LocationTag && + (identical(other.name, name) || other.name == name) && + (identical(other.radius, radius) || other.radius == radius) && + (identical(other.aSquare, aSquare) || other.aSquare == aSquare) && + (identical(other.bSquare, bSquare) || other.bSquare == bSquare) && + (identical(other.centerPoint, centerPoint) || + other.centerPoint == centerPoint)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, name, radius, aSquare, bSquare, centerPoint); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_LocationTagCopyWith<_$_LocationTag> get copyWith => + __$$_LocationTagCopyWithImpl<_$_LocationTag>(this, _$identity); + + @override + Map toJson() { + return _$$_LocationTagToJson( + this, + ); + } +} + +abstract class _LocationTag extends LocationTag { + const factory _LocationTag( + {required final String name, + required final int radius, + required final double aSquare, + required final double bSquare, + required final Location centerPoint}) = _$_LocationTag; + const _LocationTag._() : super._(); + + factory _LocationTag.fromJson(Map json) = + _$_LocationTag.fromJson; + + @override + String get name; + @override + int get radius; + @override + double get aSquare; + @override + double get bSquare; + @override + Location get centerPoint; + @override + @JsonKey(ignore: true) + _$$_LocationTagCopyWith<_$_LocationTag> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/location_tag/location_tag.g.dart b/lib/models/location_tag/location_tag.g.dart new file mode 100644 index 000000000..29bc59b35 --- /dev/null +++ b/lib/models/location_tag/location_tag.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'location_tag.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_LocationTag _$$_LocationTagFromJson(Map json) => + _$_LocationTag( + name: json['name'] as String, + radius: json['radius'] as int, + aSquare: (json['aSquare'] as num).toDouble(), + bSquare: (json['bSquare'] as num).toDouble(), + centerPoint: + Location.fromJson(json['centerPoint'] as Map), + ); + +Map _$$_LocationTagToJson(_$_LocationTag instance) => + { + 'name': instance.name, + 'radius': instance.radius, + 'aSquare': instance.aSquare, + 'bSquare': instance.bSquare, + 'centerPoint': instance.centerPoint, + }; diff --git a/lib/models/search/generic_search_result.dart b/lib/models/search/generic_search_result.dart index 8d96bd085..9bb35e327 100644 --- a/lib/models/search/generic_search_result.dart +++ b/lib/models/search/generic_search_result.dart @@ -1,3 +1,4 @@ +import "package:flutter/cupertino.dart"; import 'package:photos/models/file.dart'; import 'package:photos/models/search/search_result.dart'; @@ -5,8 +6,9 @@ class GenericSearchResult extends SearchResult { final String _name; final List _files; final ResultType _type; + final Function(BuildContext context)? onResultTap; - GenericSearchResult(this._type, this._name, this._files); + GenericSearchResult(this._type, this._name, this._files, {this.onResultTap}); @override String name() { diff --git a/lib/models/search/search_result.dart b/lib/models/search/search_result.dart index 559ad7d24..94063b0d2 100644 --- a/lib/models/search/search_result.dart +++ b/lib/models/search/search_result.dart @@ -23,5 +23,5 @@ enum ResultType { fileType, fileExtension, fileCaption, - event + event, } diff --git a/lib/models/typedefs.dart b/lib/models/typedefs.dart index 7a42a1fe5..bd53d57ff 100644 --- a/lib/models/typedefs.dart +++ b/lib/models/typedefs.dart @@ -1,7 +1,11 @@ import 'dart:async'; +import "package:photos/models/location/location.dart"; + typedef FutureVoidCallback = Future Function(); typedef BoolCallBack = bool Function(); typedef FutureVoidCallbackParamStr = Future Function(String); typedef VoidCallbackParamStr = void Function(String); typedef FutureOrVoidCallback = FutureOr Function(); +typedef VoidCallbackParamInt = void Function(int); +typedef VoidCallbackParamLocation = void Function(Location); diff --git a/lib/services/entity_service.dart b/lib/services/entity_service.dart new file mode 100644 index 000000000..a89b44f58 --- /dev/null +++ b/lib/services/entity_service.dart @@ -0,0 +1,189 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:math'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_sodium/flutter_sodium.dart'; +import 'package:logging/logging.dart'; +import "package:photos/core/configuration.dart"; +import "package:photos/core/network/network.dart"; +import "package:photos/db/entities_db.dart"; +import "package:photos/db/files_db.dart"; +import "package:photos/gateways/entity_gw.dart"; +import "package:photos/models/api/entity/data.dart"; +import "package:photos/models/api/entity/key.dart"; +import "package:photos/models/api/entity/type.dart"; +import "package:photos/models/local_entity_data.dart"; +import "package:photos/utils/crypto_util.dart"; +import 'package:shared_preferences/shared_preferences.dart'; + +class EntityService { + static const int fetchLimit = 500; + final _logger = Logger((EntityService).toString()); + final _config = Configuration.instance; + late SharedPreferences _prefs; + late EntityGateway _gateway; + late FilesDB _db; + + EntityService._privateConstructor(); + + static final EntityService instance = EntityService._privateConstructor(); + + Future init() async { + _prefs = await SharedPreferences.getInstance(); + _db = FilesDB.instance; + _gateway = EntityGateway(NetworkClient.instance.enteDio); + } + + String _getEntityKeyPrefix(EntityType type) { + return "entity_key_" + type.typeToString(); + } + + String _getEntityHeaderPrefix(EntityType type) { + return "entity_key_header_" + type.typeToString(); + } + + String _getEntityLastSyncTimePrefix(EntityType type) { + return "entity_last_sync_time_" + type.typeToString(); + } + + Future> getEntities(EntityType type) async { + return await _db.getEntities(type); + } + + Future addOrUpdate( + EntityType type, + String plainText, { + String? id, + }) async { + final key = await getOrCreateEntityKey(type); + final encryptedKeyData = await CryptoUtil.encryptChaCha( + utf8.encode(plainText) as Uint8List, + key, + ); + final String encryptedData = + Sodium.bin2base64(encryptedKeyData.encryptedData!); + final String header = Sodium.bin2base64(encryptedKeyData.header!); + debugPrint("Adding entity of type: " + type.typeToString()); + final EntityData data = id == null + ? await _gateway.createEntity(type, encryptedData, header) + : await _gateway.updateEntity(type, id, encryptedData, header); + final LocalEntityData localData = LocalEntityData( + id: data.id, + type: type, + data: plainText, + ownerID: data.userID, + updatedAt: data.updatedAt, + ); + await _db.upsertEntities([localData]); + syncEntities().ignore(); + return localData; + } + + Future deleteEntry(String id) async { + await _gateway.deleteEntity(id); + await _db.deleteEntities([id]); + } + + Future syncEntities() async { + try { + await _remoteToLocalSync(EntityType.location); + } catch (e) { + _logger.severe("Failed to sync entities", e); + } + } + + Future _remoteToLocalSync(EntityType type) async { + final int lastSyncTime = + _prefs.getInt(_getEntityLastSyncTimePrefix(type)) ?? 0; + final List result = await _gateway.getDiff( + type, + lastSyncTime, + limit: fetchLimit, + ); + if (result.isEmpty) { + debugPrint("No $type entries to sync"); + return; + } + final bool hasMoreItems = result.length == fetchLimit; + _logger.info("${result.length} entries of type $type fetched"); + final maxSyncTime = result.map((e) => e.updatedAt).reduce(max); + final List deletedIDs = + result.where((element) => element.isDeleted).map((e) => e.id).toList(); + if (deletedIDs.isNotEmpty) { + _logger.info("${deletedIDs.length} entries of type $type deleted"); + await _db.deleteEntities(deletedIDs); + } + result.removeWhere((element) => element.isDeleted); + if (result.isNotEmpty) { + final entityKey = await getOrCreateEntityKey(type); + final List entities = []; + for (EntityData e in result) { + try { + final decryptedValue = await CryptoUtil.decryptChaCha( + Sodium.base642bin(e.encryptedData!), + entityKey, + Sodium.base642bin(e.header!), + ); + final String plainText = utf8.decode(decryptedValue); + entities.add( + LocalEntityData( + id: e.id, + type: type, + data: plainText, + ownerID: e.userID, + updatedAt: e.updatedAt, + ), + ); + } catch (e, s) { + _logger.severe("Failed to decrypted data for key $type", e, s); + } + } + if (entities.isNotEmpty) { + await _db.upsertEntities(entities); + } + } + _prefs.setInt(_getEntityLastSyncTimePrefix(type), maxSyncTime); + if (hasMoreItems) { + _logger.info("Diff limit reached, pulling again"); + await _remoteToLocalSync(type); + } + } + + Future getOrCreateEntityKey(EntityType type) async { + late String encryptedKey; + late String header; + try { + if (_prefs.containsKey(_getEntityKeyPrefix(type)) && + _prefs.containsKey(_getEntityHeaderPrefix(type))) { + encryptedKey = _prefs.getString(_getEntityKeyPrefix(type))!; + header = _prefs.getString(_getEntityHeaderPrefix(type))!; + } else { + final EntityKey response = await _gateway.getKey(type); + encryptedKey = response.encryptedKey; + header = response.header; + await _prefs.setString(_getEntityKeyPrefix(type), encryptedKey); + _prefs.setString(_getEntityHeaderPrefix(type), header); + } + final entityKey = CryptoUtil.decryptSync( + Sodium.base642bin(encryptedKey), + _config.getKey()!, + Sodium.base642bin(header), + ); + return entityKey; + } on EntityKeyNotFound { + _logger.info("EntityKeyNotFound generating key for type $type"); + final key = CryptoUtil.generateKey(); + final encryptedKeyData = CryptoUtil.encryptSync(key, _config.getKey()!); + encryptedKey = Sodium.bin2base64(encryptedKeyData.encryptedData!); + header = Sodium.bin2base64(encryptedKeyData.nonce!); + await _gateway.createKey(type, encryptedKey, header); + await _prefs.setString(_getEntityKeyPrefix(type), encryptedKey); + await _prefs.setString(_getEntityHeaderPrefix(type), header); + return key; + } catch (e, s) { + _logger.severe("Failed to getOrCreateKey for type $type", e, s); + rethrow; + } + } +} diff --git a/lib/services/files_service.dart b/lib/services/files_service.dart index 136e3e7c2..41544594a 100644 --- a/lib/services/files_service.dart +++ b/lib/services/files_service.dart @@ -6,8 +6,10 @@ import 'package:photos/core/network/network.dart'; import 'package:photos/db/files_db.dart'; import 'package:photos/extensions/list.dart'; import 'package:photos/models/file.dart'; +import "package:photos/models/file_load_result.dart"; import 'package:photos/models/magic_metadata.dart'; import 'package:photos/services/file_magic_service.dart'; +import "package:photos/services/ignored_files_service.dart"; import 'package:photos/utils/date_time_util.dart'; class FilesService { @@ -94,6 +96,15 @@ class FilesService { ); return timeResult?.microsecondsSinceEpoch; } + + Future removeIgnoredFiles(Future result) async { + final ignoredIDs = await IgnoredFilesService.instance.ignoredIDs; + (await result).files.removeWhere( + (f) => + f.uploadedFileID == null && + IgnoredFilesService.instance.shouldSkipUpload(ignoredIDs, f), + ); + } } enum EditTimeSource { diff --git a/lib/services/location_service.dart b/lib/services/location_service.dart new file mode 100644 index 000000000..652b24605 --- /dev/null +++ b/lib/services/location_service.dart @@ -0,0 +1,216 @@ +import "dart:convert"; +import "dart:math"; + +import "package:logging/logging.dart"; +import "package:photos/core/constants.dart"; +import "package:photos/core/event_bus.dart"; +import "package:photos/events/location_tag_updated_event.dart"; +import "package:photos/models/api/entity/type.dart"; +import "package:photos/models/local_entity_data.dart"; +import "package:photos/models/location/location.dart"; +import 'package:photos/models/location_tag/location_tag.dart'; +import "package:photos/services/entity_service.dart"; +import "package:shared_preferences/shared_preferences.dart"; + +class LocationService { + late SharedPreferences prefs; + final Logger _logger = Logger((LocationService).toString()); + + LocationService._privateConstructor(); + + static final LocationService instance = LocationService._privateConstructor(); + + void init(SharedPreferences preferences) { + prefs = preferences; + } + + Future>> _getStoredLocationTags() async { + final data = await EntityService.instance.getEntities(EntityType.location); + return data.map( + (e) => LocalEntity(LocationTag.fromJson(json.decode(e.data)), e.id), + ); + } + + Future>> getLocationTags() { + return _getStoredLocationTags(); + } + + Future addLocation( + String location, + Location centerPoint, + int radius, + ) async { + //The area enclosed by the location tag will be a circle on a 3D spherical + //globe and an ellipse on a 2D Mercator projection (2D map) + //a & b are the semi-major and semi-minor axes of the ellipse + //Converting the unit from kilometers to degrees for a and b as that is + //the unit on the caritesian plane + + try { + final a = + (radius * _scaleFactor(centerPoint.latitude!)) / kilometersPerDegree; + final b = radius / kilometersPerDegree; + final locationTag = LocationTag( + name: location, + radius: radius, + aSquare: a * a, + bSquare: b * b, + centerPoint: centerPoint, + ); + await EntityService.instance + .addOrUpdate(EntityType.location, json.encode(locationTag.toJson())); + Bus.instance.fire(LocationTagUpdatedEvent(LocTagEventType.add)); + } catch (e, s) { + _logger.severe("Failed to add location tag", e, s); + } + } + + ///The area bounded by the location tag becomes more elliptical with increase + ///in the magnitude of the latitude on the caritesian plane. When latitude is + ///0 degrees, the ellipse is a circle with a = b = r. When latitude incrases, + ///the major axis (a) has to be scaled by the secant of the latitude. + double _scaleFactor(double lat) { + return 1 / cos(lat * (pi / 180)); + } + + Future>> enclosingLocationTags( + Location fileCoordinates, + ) async { + try { + final result = List>.of([]); + final locationTagEntities = await getLocationTags(); + for (LocalEntity locationTagEntity in locationTagEntities) { + final locationTag = locationTagEntity.item; + final x = fileCoordinates.latitude! - locationTag.centerPoint.latitude!; + final y = + fileCoordinates.longitude! - locationTag.centerPoint.longitude!; + if ((x * x) / (locationTag.aSquare) + (y * y) / (locationTag.bSquare) <= + 1) { + result.add( + locationTagEntity, + ); + } + } + return result; + } catch (e, s) { + _logger.severe("Failed to get enclosing location tags", e, s); + rethrow; + } + } + + bool isFileInsideLocationTag( + Location centerPoint, + Location fileCoordinates, + int radius, + ) { + final a = + (radius * _scaleFactor(centerPoint.latitude!)) / kilometersPerDegree; + final b = radius / kilometersPerDegree; + final x = centerPoint.latitude! - fileCoordinates.latitude!; + final y = centerPoint.longitude! - fileCoordinates.longitude!; + if ((x * x) / (a * a) + (y * y) / (b * b) <= 1) { + return true; + } + return false; + } + + String convertLocationToDMS(Location centerPoint) { + final lat = centerPoint.latitude!; + final long = centerPoint.longitude!; + final latRef = lat >= 0 ? "N" : "S"; + final longRef = long >= 0 ? "E" : "W"; + final latDMS = convertCoordinateToDMS(lat.abs()); + final longDMS = convertCoordinateToDMS(long.abs()); + return "${latDMS[0]}°${latDMS[1]}'${latDMS[2]}\"$latRef, ${longDMS[0]}°${longDMS[1]}'${longDMS[2]}\"$longRef"; + } + + List convertCoordinateToDMS(double coordinate) { + final degrees = coordinate.floor(); + final minutes = ((coordinate - degrees) * 60).floor(); + final seconds = ((coordinate - degrees - minutes / 60) * 3600).floor(); + return [degrees, minutes, seconds]; + } + + ///Will only update if there is a change in the locationTag's properties + Future updateLocationTag({ + required LocalEntity locationTagEntity, + int? newRadius, + Location? newCenterPoint, + String? newName, + }) async { + try { + final radius = newRadius ?? locationTagEntity.item.radius; + final centerPoint = newCenterPoint ?? locationTagEntity.item.centerPoint; + final name = newName ?? locationTagEntity.item.name; + + final locationTag = locationTagEntity.item; + //Exit if there is no change in locationTag's properties + if (radius == locationTag.radius && + centerPoint == locationTag.centerPoint && + name == locationTag.name) { + return; + } + final a = + (radius * _scaleFactor(centerPoint.latitude!)) / kilometersPerDegree; + final b = radius / kilometersPerDegree; + final updatedLoationTag = locationTagEntity.item.copyWith( + centerPoint: centerPoint, + aSquare: a * a, + bSquare: b * b, + radius: radius, + name: name, + ); + + await EntityService.instance.addOrUpdate( + EntityType.location, + json.encode(updatedLoationTag.toJson()), + id: locationTagEntity.id, + ); + Bus.instance.fire( + LocationTagUpdatedEvent( + LocTagEventType.update, + updatedLocTagEntities: [ + LocalEntity(updatedLoationTag, locationTagEntity.id) + ], + ), + ); + } catch (e, s) { + _logger.severe("Failed to update location tag", e, s); + rethrow; + } + } + + Future deleteLocationTag(String locTagEntityId) async { + try { + await EntityService.instance.deleteEntry( + locTagEntityId, + ); + Bus.instance.fire( + LocationTagUpdatedEvent( + LocTagEventType.delete, + ), + ); + } catch (e, s) { + _logger.severe("Failed to delete location tag", e, s); + rethrow; + } + } +} + +class GPSData { + final String latRef; + final List lat; + final String longRef; + final List long; + + GPSData(this.latRef, this.lat, this.longRef, this.long); + + Location toLocationObj() { + final latSign = latRef == "N" ? 1 : -1; + final longSign = longRef == "E" ? 1 : -1; + return Location( + latitude: latSign * lat[0] + lat[1] / 60 + lat[2] / 3600, + longitude: longSign * long[0] + long[1] / 60 + long[2] / 3600, + ); + } +} diff --git a/lib/services/search_service.dart b/lib/services/search_service.dart index 8ace0cc33..d4d21e221 100644 --- a/lib/services/search_service.dart +++ b/lib/services/search_service.dart @@ -9,13 +9,17 @@ import 'package:photos/models/collection.dart'; import 'package:photos/models/collection_items.dart'; import 'package:photos/models/file.dart'; import 'package:photos/models/file_type.dart'; -import 'package:photos/models/location.dart'; +import "package:photos/models/local_entity_data.dart"; +import "package:photos/models/location_tag/location_tag.dart"; import 'package:photos/models/search/album_search_result.dart'; import 'package:photos/models/search/generic_search_result.dart'; -import 'package:photos/models/search/location_api_response.dart'; import 'package:photos/models/search/search_result.dart'; import 'package:photos/services/collections_service.dart'; +import "package:photos/services/location_service.dart"; +import "package:photos/states/location_screen_state.dart"; +import "package:photos/ui/viewer/location/location_screen.dart"; import 'package:photos/utils/date_time_util.dart'; +import "package:photos/utils/navigation_util.dart"; import 'package:tuple/tuple.dart'; class SearchService { @@ -53,46 +57,6 @@ class SearchService { _cachedFilesFuture = null; } - Future> getLocationSearchResults( - String query, - ) async { - final List searchResults = []; - try { - final List allFiles = await _getAllFiles(); - // This code used an deprecated API earlier. We've retained the - // scaffolding for when we implement a client side location search, and - // meanwhile have replaced the API response.data with an empty map here. - final matchedLocationSearchResults = LocationApiResponse.fromMap({}); - - for (var locationData in matchedLocationSearchResults.results) { - final List filesInLocation = []; - - for (var file in allFiles) { - if (_isValidLocation(file.location) && - _isLocationWithinBounds(file.location!, locationData)) { - filesInLocation.add(file); - } - } - filesInLocation.sort( - (first, second) => - second.creationTime!.compareTo(first.creationTime!), - ); - if (filesInLocation.isNotEmpty) { - searchResults.add( - GenericSearchResult( - ResultType.location, - locationData.place, - filesInLocation, - ), - ); - } - } - } catch (e) { - _logger.severe(e); - } - return searchResults; - } - // getFilteredCollectionsWithThumbnail removes deleted or archived or // collections which don't have a file from search result Future> getCollectionSearchResults( @@ -263,6 +227,61 @@ class SearchService { return searchResults; } + Future> getLocationResults( + String query, + ) async { + final locationTagEntities = + (await LocationService.instance.getLocationTags()); + final Map, List> result = {}; + + final List searchResults = []; + + for (LocalEntity tag in locationTagEntities) { + if (tag.item.name.toLowerCase().contains(query.toLowerCase())) { + result[tag] = []; + } + } + if (result.isEmpty) { + return searchResults; + } + final allFiles = await _getAllFiles(); + for (File file in allFiles) { + if (file.hasLocation) { + for (LocalEntity tag in result.keys) { + if (LocationService.instance.isFileInsideLocationTag( + tag.item.centerPoint, + file.location!, + tag.item.radius, + )) { + result[tag]!.add(file); + } + } + } + } + for (MapEntry, List> entry + in result.entries) { + if (entry.value.isNotEmpty) { + searchResults.add( + GenericSearchResult( + ResultType.location, + entry.key.item.name, + entry.value, + onResultTap: (ctx) { + routeToPage( + ctx, + LocationScreenStateProvider( + entry.key, + const LocationScreen(), + ), + ); + }, + ), + ); + } + } + return searchResults; + } + Future> getMonthSearchResults(String query) async { final List searchResults = []; for (var month in _getMatchingMonths(query)) { @@ -363,25 +382,6 @@ class SearchService { return durationsOfMonthInEveryYear; } - bool _isValidLocation(Location? location) { - return location != null && - location.latitude != null && - location.latitude != 0 && - location.longitude != null && - location.longitude != 0; - } - - bool _isLocationWithinBounds( - Location location, - LocationDataFromResponse locationData, - ) { - //format returned by the api is [lng,lat,lng,lat] where indexes 0 & 1 are southwest and 2 & 3 northeast - return location.longitude! > locationData.bbox[0] && - location.latitude! > locationData.bbox[1] && - location.longitude! < locationData.bbox[2] && - location.latitude! < locationData.bbox[3]; - } - List> _getPossibleEventDate(String query) { final List> possibleEvents = []; if (query.trim().isEmpty) { diff --git a/lib/services/update_service.dart b/lib/services/update_service.dart index ce989aa2e..486e4493d 100644 --- a/lib/services/update_service.dart +++ b/lib/services/update_service.dart @@ -16,7 +16,7 @@ class UpdateService { static final UpdateService instance = UpdateService._privateConstructor(); static const kUpdateAvailableShownTimeKey = "update_available_shown_time_key"; static const changeLogVersionKey = "update_change_log_key"; - static const currentChangeLogVersion = 7; + static const currentChangeLogVersion = 8; LatestVersionInfo? _latestVersion; final _logger = Logger("UpdateService"); diff --git a/lib/states/location_screen_state.dart b/lib/states/location_screen_state.dart new file mode 100644 index 000000000..fe9e284ae --- /dev/null +++ b/lib/states/location_screen_state.dart @@ -0,0 +1,77 @@ +import "dart:async"; + +import "package:flutter/material.dart"; +import "package:photos/core/event_bus.dart"; +import "package:photos/events/location_tag_updated_event.dart"; +import "package:photos/models/local_entity_data.dart"; +import 'package:photos/models/location_tag/location_tag.dart'; + +class LocationScreenStateProvider extends StatefulWidget { + final LocalEntity locationTagEntity; + final Widget child; + const LocationScreenStateProvider( + this.locationTagEntity, + this.child, { + super.key, + }); + + @override + State createState() => + _LocationScreenStateProviderState(); +} + +class _LocationScreenStateProviderState + extends State { + late LocalEntity _locationTagEntity; + late final StreamSubscription _locTagUpdateListener; + @override + void initState() { + _locationTagEntity = widget.locationTagEntity; + _locTagUpdateListener = + Bus.instance.on().listen((event) { + if (event.type == LocTagEventType.update) { + setState(() { + _locationTagEntity = event.updatedLocTagEntities!.first; + }); + } + }); + super.initState(); + } + + @override + dispose() { + _locTagUpdateListener.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return InheritedLocationScreenState( + _locationTagEntity, + child: widget.child, + ); + } +} + +class InheritedLocationScreenState extends InheritedWidget { + final LocalEntity locationTagEntity; + const InheritedLocationScreenState( + this.locationTagEntity, { + super.key, + required super.child, + }); + + //This is used to show loading state when memory count is beign computed and to + //show count after computation. + static final memoryCountNotifier = ValueNotifier(null); + + static InheritedLocationScreenState of(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType()!; + } + + @override + bool updateShouldNotify(covariant InheritedLocationScreenState oldWidget) { + return oldWidget.locationTagEntity != locationTagEntity; + } +} diff --git a/lib/states/location_state.dart b/lib/states/location_state.dart new file mode 100644 index 000000000..c2c525c7a --- /dev/null +++ b/lib/states/location_state.dart @@ -0,0 +1,132 @@ +import "dart:async"; + +import "package:flutter/material.dart"; +import "package:photos/core/constants.dart"; +import "package:photos/core/event_bus.dart"; +import "package:photos/events/location_tag_updated_event.dart"; +import "package:photos/models/local_entity_data.dart"; +import "package:photos/models/location/location.dart"; +import "package:photos/models/location_tag/location_tag.dart"; +import "package:photos/models/typedefs.dart"; +import "package:photos/utils/debouncer.dart"; + +class LocationTagStateProvider extends StatefulWidget { + final LocalEntity? locationTagEntity; + final Location? centerPoint; + final Widget child; + const LocationTagStateProvider( + this.child, { + this.centerPoint, + this.locationTagEntity, + super.key, + }); + + @override + State createState() => + _LocationTagStateProviderState(); +} + +class _LocationTagStateProviderState extends State { + int _selectedRaduisIndex = defaultRadiusValueIndex; + late Location? _centerPoint; + late LocalEntity? _locationTagEntity; + final Debouncer _selectedRadiusDebouncer = + Debouncer(const Duration(milliseconds: 300)); + late final StreamSubscription _locTagEntityListener; + @override + void initState() { + _locationTagEntity = widget.locationTagEntity; + _centerPoint = widget.centerPoint; + assert(_centerPoint != null || _locationTagEntity != null); + _centerPoint = _locationTagEntity?.item.centerPoint ?? _centerPoint!; + _selectedRaduisIndex = + _locationTagEntity?.item.radiusIndex ?? defaultRadiusValueIndex; + _locTagEntityListener = + Bus.instance.on().listen((event) { + _locationTagUpdateListener(event); + }); + super.initState(); + } + + @override + void dispose() { + _locTagEntityListener.cancel(); + super.dispose(); + } + + void _locationTagUpdateListener(LocationTagUpdatedEvent event) { + if (event.type == LocTagEventType.update) { + if (event.updatedLocTagEntities!.first.id == _locationTagEntity!.id) { + //Update state when locationTag is updated. + setState(() { + final updatedLocTagEntity = event.updatedLocTagEntities!.first; + _selectedRaduisIndex = updatedLocTagEntity.item.radiusIndex; + _centerPoint = updatedLocTagEntity.item.centerPoint; + _locationTagEntity = updatedLocTagEntity; + }); + } + } + } + + void _updateSelectedIndex(int index) { + _selectedRadiusDebouncer.cancelDebounce(); + _selectedRadiusDebouncer.run(() async { + if (mounted) { + setState(() { + _selectedRaduisIndex = index; + }); + } + }); + } + + void _updateCenterPoint(Location centerPoint) { + if (mounted) { + setState(() { + _centerPoint = centerPoint; + }); + } + } + + @override + Widget build(BuildContext context) { + return InheritedLocationTagData( + _selectedRaduisIndex, + _centerPoint!, + _updateSelectedIndex, + _locationTagEntity, + _updateCenterPoint, + child: widget.child, + ); + } +} + +///This InheritedWidget's state is used in add & edit location sheets +class InheritedLocationTagData extends InheritedWidget { + final int selectedRadiusIndex; + final Location centerPoint; + //locationTag is null when we are creating a new location tag in add location sheet + final LocalEntity? locationTagEntity; + final VoidCallbackParamInt updateSelectedIndex; + final VoidCallbackParamLocation updateCenterPoint; + const InheritedLocationTagData( + this.selectedRadiusIndex, + this.centerPoint, + this.updateSelectedIndex, + this.locationTagEntity, + this.updateCenterPoint, { + required super.child, + super.key, + }); + + static InheritedLocationTagData of(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType()!; + } + + @override + bool updateShouldNotify(InheritedLocationTagData oldWidget) { + return oldWidget.selectedRadiusIndex != selectedRadiusIndex || + oldWidget.centerPoint != centerPoint || + oldWidget.locationTagEntity != locationTagEntity; + } +} diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index fcf2e9f62..1cc2d151b 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -216,6 +216,7 @@ const Color tabIconDark = Color.fromRGBO(255, 255, 255, 0.80); // Fixed Colors const Color fixedStrokeMutedWhite = Color.fromRGBO(255, 255, 255, 0.50); +const Color strokeSolidMutedLight = Color.fromRGBO(147, 147, 147, 1); const Color _primary700 = Color.fromRGBO(0, 179, 60, 1); const Color _primary500 = Color.fromRGBO(29, 185, 84, 1); diff --git a/lib/ui/actions/file/file_actions.dart b/lib/ui/actions/file/file_actions.dart index d5f14ed3d..e88c917fd 100644 --- a/lib/ui/actions/file/file_actions.dart +++ b/lib/ui/actions/file/file_actions.dart @@ -131,7 +131,7 @@ Future showSingleFileDeleteSheet( } } -Future showInfoSheet(BuildContext context, File file) async { +Future showDetailsSheet(BuildContext context, File file) async { final colorScheme = getEnteColorScheme(context); return showBarModalBottomSheet( topControl: const SizedBox.shrink(), diff --git a/lib/ui/collection_action_sheet.dart b/lib/ui/collection_action_sheet.dart index 9e0d044d7..cae6b2a7a 100644 --- a/lib/ui/collection_action_sheet.dart +++ b/lib/ui/collection_action_sheet.dart @@ -159,8 +159,9 @@ class _CollectionActionSheetState extends State { _searchQuery = value; }); }, - cancellable: true, - shouldUnfocusOnCancelOrSubmit: true, + isClearable: true, + shouldUnfocusOnClearOrSubmit: true, + borderRadius: 2, ), ), _getCollectionItems(filesCount), diff --git a/lib/ui/components/buttons/chip_button_widget.dart b/lib/ui/components/buttons/chip_button_widget.dart index 20c492737..27d9d2b19 100644 --- a/lib/ui/components/buttons/chip_button_widget.dart +++ b/lib/ui/components/buttons/chip_button_widget.dart @@ -37,14 +37,16 @@ class ChipButtonWidget extends StatelessWidget { size: 17, ) : const SizedBox.shrink(), - const SizedBox(width: 4), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Text( - label ?? "", - style: getEnteTextTheme(context).smallBold, - ), - ) + if (label != null && leadingIcon != null) + const SizedBox(width: 4), + if (label != null) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text( + label!, + style: getEnteTextTheme(context).smallBold, + ), + ) ], ), ), diff --git a/lib/ui/components/divider_widget.dart b/lib/ui/components/divider_widget.dart index de30ea04f..1e8b3a882 100644 --- a/lib/ui/components/divider_widget.dart +++ b/lib/ui/components/divider_widget.dart @@ -28,17 +28,23 @@ class DividerWidget extends StatelessWidget { : getEnteColorScheme(context).strokeFaint; if (dividerType == DividerType.solid) { - return Container( - color: getEnteColorScheme(context).strokeFaint, - width: double.infinity, - height: 1, + return Padding( + padding: padding ?? EdgeInsets.zero, + child: Container( + color: getEnteColorScheme(context).strokeFaint, + width: double.infinity, + height: 1, + ), ); } if (dividerType == DividerType.bottomBar) { - return Container( - color: dividerColor, - width: double.infinity, - height: 1, + return Padding( + padding: padding ?? EdgeInsets.zero, + child: Container( + color: dividerColor, + width: double.infinity, + height: 1, + ), ); } diff --git a/lib/ui/components/info_item_widget.dart b/lib/ui/components/info_item_widget.dart index 603899523..eb5e84328 100644 --- a/lib/ui/components/info_item_widget.dart +++ b/lib/ui/components/info_item_widget.dart @@ -10,12 +10,14 @@ class InfoItemWidget extends StatelessWidget { final String? title; final Future> subtitleSection; final bool hasChipButtons; + final VoidCallback? onTap; const InfoItemWidget({ required this.leadingIcon, this.editOnTap, this.title, required this.subtitleSection, this.hasChipButtons = false, + this.onTap, super.key, }); @@ -83,10 +85,17 @@ class InfoItemWidget extends StatelessWidget { Flexible( child: Padding( padding: const EdgeInsets.fromLTRB(12, 3.5, 16, 3.5), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: children, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: onTap, + child: SizedBox( + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ), + ), ), ), ), diff --git a/lib/ui/components/text_input_widget.dart b/lib/ui/components/text_input_widget.dart index 53ae63521..1f1b0d5c3 100644 --- a/lib/ui/components/text_input_widget.dart +++ b/lib/ui/components/text_input_widget.dart @@ -16,10 +16,15 @@ class TextInputWidget extends StatefulWidget { final Alignment? alignMessage; final bool? autoFocus; final int? maxLength; + final double borderRadius; ///TextInputWidget will listen to this notifier and executes onSubmit when ///notified. final ValueNotifier? submitNotifier; + + ///TextInputWidget will listen to this notifier and clears and unfocuses the + ///textFiled when notified. + final ValueNotifier? cancelNotifier; final bool alwaysShowSuccessState; final bool showOnlyLoadingState; final FutureVoidCallbackParamStr? onSubmit; @@ -28,8 +33,14 @@ class TextInputWidget extends StatefulWidget { final bool shouldSurfaceExecutionStates; final TextCapitalization? textCapitalization; final bool isPasswordInput; - final bool cancellable; - final bool shouldUnfocusOnCancelOrSubmit; + + ///Clear comes in the form of a suffix icon. It is unrelated to onCancel. + final bool isClearable; + final bool shouldUnfocusOnClearOrSubmit; + final FocusNode? focusNode; + final VoidCallback? onCancel; + final TextEditingController? textEditingController; + final ValueNotifier? isEmptyNotifier; const TextInputWidget({ this.onSubmit, this.onChange, @@ -42,14 +53,20 @@ class TextInputWidget extends StatefulWidget { this.autoFocus, this.maxLength, this.submitNotifier, + this.cancelNotifier, this.alwaysShowSuccessState = false, this.showOnlyLoadingState = false, this.popNavAfterSubmission = false, this.shouldSurfaceExecutionStates = true, this.textCapitalization = TextCapitalization.none, this.isPasswordInput = false, - this.cancellable = false, - this.shouldUnfocusOnCancelOrSubmit = false, + this.isClearable = false, + this.shouldUnfocusOnClearOrSubmit = false, + this.borderRadius = 8, + this.focusNode, + this.onCancel, + this.textEditingController, + this.isEmptyNotifier, super.key, }); @@ -59,7 +76,7 @@ class TextInputWidget extends StatefulWidget { class _TextInputWidgetState extends State { ExecutionState executionState = ExecutionState.idle; - final _textController = TextEditingController(); + late final TextEditingController _textController; final _debouncer = Debouncer(const Duration(milliseconds: 300)); late final ValueNotifier _obscureTextNotifier; @@ -70,6 +87,8 @@ class _TextInputWidgetState extends State { @override void initState() { widget.submitNotifier?.addListener(_onSubmit); + widget.cancelNotifier?.addListener(_onCancel); + _textController = widget.textEditingController ?? TextEditingController(); if (widget.initialValue != null) { _textController.value = TextEditingValue( @@ -84,14 +103,22 @@ class _TextInputWidgetState extends State { } _obscureTextNotifier = ValueNotifier(widget.isPasswordInput); _obscureTextNotifier.addListener(_safeRefresh); + + if (widget.isEmptyNotifier != null) { + _textController.addListener(() { + widget.isEmptyNotifier!.value = _textController.text.isEmpty; + }); + } super.initState(); } @override void dispose() { widget.submitNotifier?.removeListener(_onSubmit); + widget.cancelNotifier?.removeListener(_onCancel); _obscureTextNotifier.dispose(); _textController.dispose(); + widget.isEmptyNotifier?.dispose(); super.dispose(); } @@ -113,12 +140,13 @@ class _TextInputWidgetState extends State { } textInputChildren.add( ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(8)), + borderRadius: BorderRadius.all(Radius.circular(widget.borderRadius)), child: Material( child: TextFormField( textCapitalization: widget.textCapitalization!, autofocus: widget.autoFocus ?? false, controller: _textController, + focusNode: widget.focusNode, inputFormatters: widget.maxLength != null ? [LengthLimitingTextInputFormatter(50)] : null, @@ -155,9 +183,9 @@ class _TextInputWidgetState extends State { obscureTextNotifier: _obscureTextNotifier, isPasswordInput: widget.isPasswordInput, textController: _textController, - isCancellable: widget.cancellable, - shouldUnfocusOnCancelOrSubmit: - widget.shouldUnfocusOnCancelOrSubmit, + isClearable: widget.isClearable, + shouldUnfocusOnClearOrSubmit: + widget.shouldUnfocusOnClearOrSubmit, ), ), ), @@ -224,7 +252,7 @@ class _TextInputWidgetState extends State { }); }), ); - if (widget.shouldUnfocusOnCancelOrSubmit) { + if (widget.shouldUnfocusOnClearOrSubmit) { FocusScope.of(context).unfocus(); } try { @@ -303,6 +331,15 @@ class _TextInputWidgetState extends State { } } + void _onCancel() { + if (widget.onCancel != null) { + widget.onCancel!(); + } else { + _textController.clear(); + FocusScope.of(context).unfocus(); + } + } + void _popNavigatorStack(BuildContext context, {Exception? e}) { Navigator.of(context).canPop() ? Navigator.of(context).pop(e) : null; } @@ -315,8 +352,8 @@ class SuffixIconWidget extends StatelessWidget { final TextEditingController textController; final ValueNotifier? obscureTextNotifier; final bool isPasswordInput; - final bool isCancellable; - final bool shouldUnfocusOnCancelOrSubmit; + final bool isClearable; + final bool shouldUnfocusOnClearOrSubmit; const SuffixIconWidget({ required this.executionState, @@ -324,8 +361,8 @@ class SuffixIconWidget extends StatelessWidget { required this.textController, this.obscureTextNotifier, this.isPasswordInput = false, - this.isCancellable = false, - this.shouldUnfocusOnCancelOrSubmit = false, + this.isClearable = false, + this.shouldUnfocusOnClearOrSubmit = false, super.key, }); @@ -335,11 +372,11 @@ class SuffixIconWidget extends StatelessWidget { final colorScheme = getEnteColorScheme(context); if (executionState == ExecutionState.idle || !shouldSurfaceExecutionStates) { - if (isCancellable) { + if (isClearable) { trailingWidget = GestureDetector( onTap: () { textController.clear(); - if (shouldUnfocusOnCancelOrSubmit) { + if (shouldUnfocusOnClearOrSubmit) { FocusScope.of(context).unfocus(); } }, diff --git a/lib/ui/components/title_bar_widget.dart b/lib/ui/components/title_bar_widget.dart index 25a2cf2d6..46fbd228d 100644 --- a/lib/ui/components/title_bar_widget.dart +++ b/lib/ui/components/title_bar_widget.dart @@ -13,6 +13,7 @@ class TitleBarWidget extends StatelessWidget { final bool isFlexibleSpaceDisabled; final bool isOnTopOfScreen; final Color? backgroundColor; + final bool isSliver; const TitleBarWidget({ this.leading, this.title, @@ -24,103 +25,96 @@ class TitleBarWidget extends StatelessWidget { this.isFlexibleSpaceDisabled = false, this.isOnTopOfScreen = true, this.backgroundColor, + this.isSliver = true, super.key, }); @override Widget build(BuildContext context) { const toolbarHeight = 48.0; - final textTheme = getEnteTextTheme(context); - final colorTheme = getEnteColorScheme(context); - return SliverAppBar( - backgroundColor: backgroundColor, - primary: isOnTopOfScreen ? true : false, - toolbarHeight: toolbarHeight, - leadingWidth: 48, - automaticallyImplyLeading: false, - pinned: true, - expandedHeight: isFlexibleSpaceDisabled ? toolbarHeight : 102, - centerTitle: false, - titleSpacing: 4, - title: Padding( - padding: EdgeInsets.only(left: isTitleH2WithoutLeading ? 16 : 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - title == null - ? const SizedBox.shrink() - : Text( - title!, - style: isTitleH2WithoutLeading - ? textTheme.h2Bold - : textTheme.largeBold, - ), - caption == null || isTitleH2WithoutLeading - ? const SizedBox.shrink() - : Text( - caption!, - style: textTheme.mini.copyWith(color: colorTheme.textMuted), - ) - ], + if (isSliver) { + return SliverAppBar( + backgroundColor: backgroundColor, + primary: isOnTopOfScreen ? true : false, + toolbarHeight: toolbarHeight, + leadingWidth: 48, + automaticallyImplyLeading: false, + pinned: true, + expandedHeight: isFlexibleSpaceDisabled ? toolbarHeight : 102, + centerTitle: false, + titleSpacing: 4, + title: TitleWidget( + title: title, + caption: caption, + isTitleH2WithoutLeading: isTitleH2WithoutLeading, ), - ), - actions: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Row( - children: _actionsWithPaddingInBetween(), - ), - ), - ], - leading: isTitleH2WithoutLeading - ? null - : leading ?? - IconButtonWidget( - icon: Icons.arrow_back_outlined, - iconButtonType: IconButtonType.primary, - onTap: () { - Navigator.pop(context); - }, - ), - flexibleSpace: isFlexibleSpaceDisabled - ? null - : FlexibleSpaceBar( - background: SafeArea( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: toolbarHeight), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 4, - horizontal: 16, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - flexibleSpaceTitle == null - ? const SizedBox.shrink() - : flexibleSpaceTitle!, - flexibleSpaceCaption == null - ? const SizedBox.shrink() - : Text( - flexibleSpaceCaption!, - style: textTheme.small.copyWith( - color: colorTheme.textMuted, - ), - overflow: TextOverflow.ellipsis, - maxLines: 1, - ) - ], - ), - ), - ], - ), - ), + actions: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Row( + children: _actionsWithPaddingInBetween(), ), - ); + ), + ], + leading: isTitleH2WithoutLeading + ? null + : leading ?? + IconButtonWidget( + icon: Icons.arrow_back_outlined, + iconButtonType: IconButtonType.primary, + onTap: () { + Navigator.pop(context); + }, + ), + flexibleSpace: isFlexibleSpaceDisabled + ? null + : FlexibleSpaceBarWidget( + flexibleSpaceTitle, + flexibleSpaceCaption, + toolbarHeight, + ), + ); + } else { + return AppBar( + backgroundColor: backgroundColor, + primary: isOnTopOfScreen ? true : false, + toolbarHeight: toolbarHeight, + leadingWidth: 48, + automaticallyImplyLeading: false, + centerTitle: false, + titleSpacing: 4, + title: TitleWidget( + title: title, + caption: caption, + isTitleH2WithoutLeading: isTitleH2WithoutLeading, + ), + actions: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Row( + children: _actionsWithPaddingInBetween(), + ), + ), + ], + leading: isTitleH2WithoutLeading + ? null + : leading ?? + IconButtonWidget( + icon: Icons.arrow_back_outlined, + iconButtonType: IconButtonType.primary, + onTap: () { + Navigator.pop(context); + }, + ), + flexibleSpace: isFlexibleSpaceDisabled + ? null + : FlexibleSpaceBarWidget( + flexibleSpaceTitle, + flexibleSpaceCaption, + toolbarHeight, + ), + ); + } } _actionsWithPaddingInBetween() { @@ -150,3 +144,89 @@ class TitleBarWidget extends StatelessWidget { return actions; } } + +class TitleWidget extends StatelessWidget { + final String? title; + final String? caption; + final bool isTitleH2WithoutLeading; + const TitleWidget( + {this.title, + this.caption, + required this.isTitleH2WithoutLeading, + super.key}); + + @override + Widget build(BuildContext context) { + final textTheme = getEnteTextTheme(context); + return Padding( + padding: EdgeInsets.only(left: isTitleH2WithoutLeading ? 16 : 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + title == null + ? const SizedBox.shrink() + : Text( + title!, + style: isTitleH2WithoutLeading + ? textTheme.h2Bold + : textTheme.largeBold, + ), + caption == null || isTitleH2WithoutLeading + ? const SizedBox.shrink() + : Text( + caption!, + style: textTheme.miniMuted, + ) + ], + ), + ); + } +} + +class FlexibleSpaceBarWidget extends StatelessWidget { + final Widget? flexibleSpaceTitle; + final String? flexibleSpaceCaption; + final double toolbarHeight; + const FlexibleSpaceBarWidget( + this.flexibleSpaceTitle, this.flexibleSpaceCaption, this.toolbarHeight, + {super.key}); + + @override + Widget build(BuildContext context) { + final textTheme = getEnteTextTheme(context); + return FlexibleSpaceBar( + background: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: toolbarHeight), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 4, + horizontal: 16, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + flexibleSpaceTitle == null + ? const SizedBox.shrink() + : flexibleSpaceTitle!, + flexibleSpaceCaption == null + ? const SizedBox.shrink() + : Text( + flexibleSpaceCaption!, + style: textTheme.smallMuted, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ) + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/ui/home/landing_page_widget.dart b/lib/ui/home/landing_page_widget.dart index 2eb20a181..495efea41 100644 --- a/lib/ui/home/landing_page_widget.dart +++ b/lib/ui/home/landing_page_widget.dart @@ -1,10 +1,13 @@ import 'dart:io'; import 'package:dots_indicator/dots_indicator.dart'; +import "package:flutter/foundation.dart"; import 'package:flutter/material.dart'; +import "package:photos/app.dart"; import 'package:photos/core/configuration.dart'; import 'package:photos/ente_theme_data.dart'; import "package:photos/generated/l10n.dart"; +import "package:photos/l10n/l10n.dart"; import 'package:photos/services/update_service.dart'; import 'package:photos/ui/account/email_entry_page.dart'; import 'package:photos/ui/account/login_page.dart'; @@ -15,6 +18,8 @@ import 'package:photos/ui/components/buttons/button_widget.dart'; import 'package:photos/ui/components/dialog_widget.dart'; import 'package:photos/ui/components/models/button_type.dart'; import 'package:photos/ui/payment/subscription.dart'; +import "package:photos/ui/settings/language_picker.dart"; +import "package:photos/utils/navigation_util.dart"; class LandingPageWidget extends StatefulWidget { const LandingPageWidget({Key? key}) : super(key: key); @@ -42,6 +47,31 @@ class _LandingPageWidgetState extends State { child: SingleChildScrollView( child: Column( children: [ + kDebugMode + ? GestureDetector( + child: const Align( + alignment: Alignment.topRight, + child: Text("Lang"), + ), + onTap: () async { + final locale = await getLocale(); + routeToPage( + context, + LanguageSelectorPage( + appSupportedLocales, + (locale) async { + await setLocale(locale); + EnteApp.setLocale(context, locale); + S.delegate.load(locale); + }, + locale, + ), + ).then((value) { + setState(() {}); + }); + }, + ) + : const SizedBox(), const Padding(padding: EdgeInsets.all(12)), const Text( "ente", diff --git a/lib/ui/home/memories_widget.dart b/lib/ui/home/memories_widget.dart index 5e23e3ea0..a06e5bcab 100644 --- a/lib/ui/home/memories_widget.dart +++ b/lib/ui/home/memories_widget.dart @@ -369,7 +369,7 @@ class _FullScreenMemoryState extends State { color: Colors.white, //same for both themes ), onPressed: () { - showInfoSheet(context, file); + showDetailsSheet(context, file); }, ), IconButton( diff --git a/lib/ui/home/status_bar_widget.dart b/lib/ui/home/status_bar_widget.dart index f04662588..9b1b3a10a 100644 --- a/lib/ui/home/status_bar_widget.dart +++ b/lib/ui/home/status_bar_widget.dart @@ -101,7 +101,7 @@ class _StatusBarWidgetState extends State { _showErrorBanner ? HeaderErrorWidget(error: _syncError) : const SizedBox.shrink(), - !UserRemoteFlagService.instance.shouldShowRecoveryVerification() + UserRemoteFlagService.instance.shouldShowRecoveryVerification() ? Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12), diff --git a/lib/ui/huge_listview/huge_listview.dart b/lib/ui/huge_listview/huge_listview.dart index ce651d297..056039e1c 100644 --- a/lib/ui/huge_listview/huge_listview.dart +++ b/lib/ui/huge_listview/huge_listview.dart @@ -60,6 +60,8 @@ class HugeListView extends StatefulWidget { final EdgeInsetsGeometry? thumbPadding; + final bool disableScroll; + const HugeListView({ Key? key, this.controller, @@ -77,6 +79,7 @@ class HugeListView extends StatefulWidget { this.bottomSafeArea = 120.0, this.isDraggableScrollbarEnabled = true, this.thumbPadding, + this.disableScroll = false, }) : super(key: key); @override @@ -160,6 +163,9 @@ class HugeListViewState extends State> { isEnabled: widget.isDraggableScrollbarEnabled, padding: widget.thumbPadding, child: ScrollablePositionedList.builder( + physics: widget.disableScroll + ? const NeverScrollableScrollPhysics() + : null, itemScrollController: widget.controller, itemPositionsListener: listener, initialScrollIndex: widget.startIndex, diff --git a/lib/ui/huge_listview/lazy_loading_gallery.dart b/lib/ui/huge_listview/lazy_loading_gallery.dart index d1e012f8d..a3a2edd89 100644 --- a/lib/ui/huge_listview/lazy_loading_gallery.dart +++ b/lib/ui/huge_listview/lazy_loading_gallery.dart @@ -32,11 +32,13 @@ class LazyLoadingGallery extends StatefulWidget { final Stream? reloadEvent; final Set removalEventTypes; final GalleryLoader asyncLoader; - final SelectedFiles selectedFiles; + final SelectedFiles? selectedFiles; final String tag; final String? logTag; final Stream currentIndexStream; final int photoGirdSize; + final bool areFilesCollatedByDay; + final bool limitSelectionToOne; LazyLoadingGallery( this.files, this.index, @@ -45,9 +47,11 @@ class LazyLoadingGallery extends StatefulWidget { this.asyncLoader, this.selectedFiles, this.tag, - this.currentIndexStream, { + this.currentIndexStream, + this.areFilesCollatedByDay, { this.logTag = "", this.photoGirdSize = photoGridSizeDefault, + this.limitSelectionToOne = false, Key? key, }) : super(key: key ?? UniqueKey()); @@ -62,7 +66,7 @@ class _LazyLoadingGalleryState extends State { late Logger _logger; late List _files; - late StreamSubscription _reloadEventSubscription; + late StreamSubscription? _reloadEventSubscription; late StreamSubscription _currentIndexSubscription; bool? _shouldRender; final ValueNotifier _toggleSelectAllFromDay = ValueNotifier(false); @@ -72,7 +76,7 @@ class _LazyLoadingGalleryState extends State { @override void initState() { //this is for removing the 'select all from day' icon on unselecting all files with 'cancel' - widget.selectedFiles.addListener(_selectedFilesListener); + widget.selectedFiles?.addListener(_selectedFilesListener); super.initState(); _init(); } @@ -81,7 +85,7 @@ class _LazyLoadingGalleryState extends State { _logger = Logger("LazyLoading_${widget.logTag}"); _shouldRender = true; _files = widget.files; - _reloadEventSubscription = widget.reloadEvent!.listen((e) => _onReload(e)); + _reloadEventSubscription = widget.reloadEvent?.listen((e) => _onReload(e)); _currentIndexSubscription = widget.currentIndexStream.listen((currentIndex) { @@ -162,9 +166,9 @@ class _LazyLoadingGalleryState extends State { @override void dispose() { - _reloadEventSubscription.cancel(); + _reloadEventSubscription?.cancel(); _currentIndexSubscription.cancel(); - widget.selectedFiles.removeListener(_selectedFilesListener); + widget.selectedFiles?.removeListener(_selectedFilesListener); _toggleSelectAllFromDay.dispose(); _showSelectAllButton.dispose(); _areAllFromDaySelected.dispose(); @@ -175,7 +179,7 @@ class _LazyLoadingGalleryState extends State { void didUpdateWidget(LazyLoadingGallery oldWidget) { super.didUpdateWidget(oldWidget); if (!listEquals(_files, widget.files)) { - _reloadEventSubscription.cancel(); + _reloadEventSubscription?.cancel(); _init(); } } @@ -190,47 +194,50 @@ class _LazyLoadingGalleryState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - getDayWidget( - context, - _files[0].creationTime!, - widget.photoGirdSize, - ), - ValueListenableBuilder( - valueListenable: _showSelectAllButton, - builder: (context, dynamic value, _) { - return !value - ? const SizedBox.shrink() - : GestureDetector( - behavior: HitTestBehavior.translucent, - child: SizedBox( - width: 48, - height: 44, - child: ValueListenableBuilder( - valueListenable: _areAllFromDaySelected, - builder: (context, dynamic value, _) { - return value - ? const Icon( - Icons.check_circle, - size: 18, - ) - : Icon( - Icons.check_circle_outlined, - color: getEnteColorScheme(context) - .strokeMuted, - size: 18, - ); - }, - ), - ), - onTap: () { - //this value has no significance - //changing only to notify the listeners - _toggleSelectAllFromDay.value = - !_toggleSelectAllFromDay.value; - }, - ); - }, - ) + if (widget.areFilesCollatedByDay) + getDayWidget( + context, + _files[0].creationTime!, + widget.photoGirdSize, + ), + widget.limitSelectionToOne + ? const SizedBox.shrink() + : ValueListenableBuilder( + valueListenable: _showSelectAllButton, + builder: (context, dynamic value, _) { + return !value + ? const SizedBox.shrink() + : GestureDetector( + behavior: HitTestBehavior.translucent, + child: SizedBox( + width: 48, + height: 44, + child: ValueListenableBuilder( + valueListenable: _areAllFromDaySelected, + builder: (context, dynamic value, _) { + return value + ? const Icon( + Icons.check_circle, + size: 18, + ) + : Icon( + Icons.check_circle_outlined, + color: getEnteColorScheme(context) + .strokeMuted, + size: 18, + ); + }, + ), + ), + onTap: () { + //this value has no significance + //changing only to notify the listeners + _toggleSelectAllFromDay.value = + !_toggleSelectAllFromDay.value; + }, + ); + }, + ) ], ), _shouldRender! @@ -261,6 +268,7 @@ class _LazyLoadingGalleryState extends State { _toggleSelectAllFromDay, _areAllFromDaySelected, widget.photoGirdSize, + limitSelectionToOne: widget.limitSelectionToOne, ), ); } @@ -271,7 +279,7 @@ class _LazyLoadingGalleryState extends State { } void _selectedFilesListener() { - if (widget.selectedFiles.files.isEmpty) { + if (widget.selectedFiles!.files.isEmpty) { _showSelectAllButton.value = false; } else { _showSelectAllButton.value = true; @@ -283,12 +291,13 @@ class LazyLoadingGridView extends StatefulWidget { final String tag; final List filesInDay; final GalleryLoader asyncLoader; - final SelectedFiles selectedFiles; + final SelectedFiles? selectedFiles; final bool shouldRender; final bool shouldRecycle; final ValueNotifier toggleSelectAllFromDay; final ValueNotifier areAllFilesSelected; final int? photoGridSize; + final bool limitSelectionToOne; LazyLoadingGridView( this.tag, @@ -300,6 +309,7 @@ class LazyLoadingGridView extends StatefulWidget { this.toggleSelectAllFromDay, this.areAllFilesSelected, this.photoGridSize, { + this.limitSelectionToOne = false, Key? key, }) : super(key: key ?? UniqueKey()); @@ -316,7 +326,7 @@ class _LazyLoadingGridViewState extends State { void initState() { _shouldRender = widget.shouldRender; _currentUserID = Configuration.instance.getUserID(); - widget.selectedFiles.addListener(_selectedFilesListener); + widget.selectedFiles?.addListener(_selectedFilesListener); _clearSelectionsEvent = Bus.instance.on().listen((event) { if (mounted) { @@ -329,7 +339,7 @@ class _LazyLoadingGridViewState extends State { @override void dispose() { - widget.selectedFiles.removeListener(_selectedFilesListener); + widget.selectedFiles?.removeListener(_selectedFilesListener); _clearSelectionsEvent.cancel(); widget.toggleSelectAllFromDay .removeListener(_toggleSelectAllFromDayListener); @@ -403,12 +413,12 @@ class _LazyLoadingGridViewState extends State { mainAxisSpacing: 2, crossAxisCount: widget.photoGridSize!, ), - padding: const EdgeInsets.all(0), + padding: const EdgeInsets.symmetric(vertical: (galleryGridSpacing / 2)), ); } Widget _buildFile(BuildContext context, File file) { - final isFileSelected = widget.selectedFiles.isFileSelected(file); + final isFileSelected = widget.selectedFiles?.isFileSelected(file) ?? false; Color selectionColor = Colors.white; if (isFileSelected && file.isUploaded && @@ -421,25 +431,15 @@ class _LazyLoadingGridViewState extends State { selectionColor = avatarColors[(randomID).remainder(avatarColors.length)]; } return GestureDetector( - onTap: () async { - if (widget.selectedFiles.files.isNotEmpty) { - _selectFile(file); - } else { - if (AppLifecycleService.instance.mediaExtensionAction.action == - IntentAction.pick) { - final ioFile = await getFile(file); - MediaExtension().setResult("file://${ioFile!.path}"); - } else { - _routeToDetailPage(file, context); - } - } + onTap: () { + widget.limitSelectionToOne + ? _onTapWithSelectionLimit(file) + : _onTapNoSelectionLimit(file); }, onLongPress: () { - if (AppLifecycleService.instance.mediaExtensionAction.action == - IntentAction.main) { - HapticFeedback.lightImpact(); - _selectFile(file); - } + widget.limitSelectionToOne + ? _onLongPressWithSelectionLimit(file) + : _onLongPressNoSelectionLimit(file); }, child: ClipRRect( borderRadius: BorderRadius.circular(1), @@ -485,8 +485,50 @@ class _LazyLoadingGridViewState extends State { ); } - void _selectFile(File file) { - widget.selectedFiles.toggleSelection(file); + void _toggleFileSelection(File file) { + widget.selectedFiles!.toggleSelection(file); + } + + void _onTapNoSelectionLimit(File file) async { + if (widget.selectedFiles?.files.isNotEmpty ?? false) { + _toggleFileSelection(file); + } else { + if (AppLifecycleService.instance.mediaExtensionAction.action == + IntentAction.pick) { + final ioFile = await getFile(file); + MediaExtension().setResult("file://${ioFile!.path}"); + } else { + _routeToDetailPage(file, context); + } + } + } + + void _onTapWithSelectionLimit(File file) { + if (widget.selectedFiles!.files.isNotEmpty && + widget.selectedFiles!.files.first != file) { + widget.selectedFiles!.clearAll(); + } + _toggleFileSelection(file); + } + + void _onLongPressNoSelectionLimit(File file) { + if (widget.selectedFiles!.files.isNotEmpty) { + _routeToDetailPage(file, context); + } else if (AppLifecycleService.instance.mediaExtensionAction.action == + IntentAction.main) { + HapticFeedback.lightImpact(); + _toggleFileSelection(file); + } + } + + Future _onLongPressWithSelectionLimit(File file) async { + if (AppLifecycleService.instance.mediaExtensionAction.action == + IntentAction.pick) { + final ioFile = await getFile(file); + MediaExtension().setResult("file://${ioFile!.path}"); + } else { + _routeToDetailPage(file, context); + } } void _routeToDetailPage(File file, BuildContext context) { @@ -502,14 +544,14 @@ class _LazyLoadingGridViewState extends State { } void _selectedFilesListener() { - if (widget.selectedFiles.files.containsAll(widget.filesInDay.toSet())) { + if (widget.selectedFiles!.files.containsAll(widget.filesInDay.toSet())) { widget.areAllFilesSelected.value = true; } else { widget.areAllFilesSelected.value = false; } bool shouldRefresh = false; for (final file in widget.filesInDay) { - if (widget.selectedFiles.isPartOfLastSelected(file)) { + if (widget.selectedFiles!.isPartOfLastSelected(file)) { shouldRefresh = true; } } @@ -519,12 +561,12 @@ class _LazyLoadingGridViewState extends State { } void _toggleSelectAllFromDayListener() { - if (widget.selectedFiles.files.containsAll(widget.filesInDay.toSet())) { + if (widget.selectedFiles!.files.containsAll(widget.filesInDay.toSet())) { setState(() { - widget.selectedFiles.unSelectAll(widget.filesInDay.toSet()); + widget.selectedFiles!.unSelectAll(widget.filesInDay.toSet()); }); } else { - widget.selectedFiles.selectAll(widget.filesInDay.toSet()); + widget.selectedFiles!.selectAll(widget.filesInDay.toSet()); } } } diff --git a/lib/ui/notification/update/change_log_page.dart b/lib/ui/notification/update/change_log_page.dart index a548324fc..e2efbc5ec 100644 --- a/lib/ui/notification/update/change_log_page.dart +++ b/lib/ui/notification/update/change_log_page.dart @@ -1,5 +1,3 @@ -import "dart:io"; - import 'package:flutter/material.dart'; import "package:photos/generated/l10n.dart"; import 'package:photos/services/update_service.dart'; @@ -104,41 +102,27 @@ class _ChangeLogPageState extends State { Widget _getChangeLog() { final scrollController = ScrollController(); final List items = []; - items.add( - ChangeLogEntry( - "Referrals ✨", - "You can now double your storage by referring your friends and family. Both you and your loved ones will get 10 GB of storage when " - "they upgrade to a paid plan.\n\nGo to Settings -> General -> " - "Referrals to get started!", - ), - ); - if (Platform.isAndroid) { - items.add( - ChangeLogEntry( - "Pick Files", - "While sharing photos and videos through other apps, ente will now " - "be an option to pick files from. This means you can now easily" - " attach files backed up to ente.\n\nConsider this the first " - "step towards making ente your default gallery app!", - ), - ); - } items.add( ChangeLogEntry( - "Verification ID", - "Security of your end-to-end encryption with those you are sharing your " - "albums with can now be verified, with the help of Verification IDs." - "\n\nPlease click on the Verify button on the album sharing page to learn more.", + "Security Audit", + "It gives us immense joy to announce that ente's source code has been" + " successfully audited by Cure53, in collaboration with Symbolic " + "Software.\n\nTogether they have certified that ente's " + "architecture is sound and that our implementation across all " + "clients is cryptographically accurate.\n\nYou can find more " + "details at ente.io/blog.", ), ); items.add( ChangeLogEntry( - "Prettier Pixels", - "This release is also packed with a bunch of user interface improvements suggested by our community." - "\n\nWe have added more actions to your Memories section, introduced archived albums to your Archived section, improved the experience of the Trash screen and sprinkled a few more improvements here and there.", - isFeature: false, + "Location tags", + "This release includes a fresh, beautiful, privacy-friendly way to " + "search through your photos by location!\n\nTag a photo with a " + "location, define a radius, and ente will automatically cluster " + "all photos clicked within that area.\n\nOpen a photo, and click on" + " the Info button to get started!", ), ); diff --git a/lib/ui/settings/general_section_widget.dart b/lib/ui/settings/general_section_widget.dart index 0f5740b55..c006612fa 100644 --- a/lib/ui/settings/general_section_widget.dart +++ b/lib/ui/settings/general_section_widget.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; +import "package:photos/app.dart"; import "package:photos/generated/l10n.dart"; +import "package:photos/l10n/l10n.dart"; import 'package:photos/services/billing_service.dart'; +import "package:photos/services/feature_flag_service.dart"; import 'package:photos/services/user_service.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/advanced_settings_screen.dart'; @@ -9,6 +12,7 @@ import 'package:photos/ui/components/expandable_menu_item_widget.dart'; import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import "package:photos/ui/growth/referral_screen.dart"; import 'package:photos/ui/settings/common_settings.dart'; +import "package:photos/ui/settings/language_picker.dart"; import 'package:photos/utils/navigation_util.dart'; class GeneralSectionWidget extends StatelessWidget { @@ -24,21 +28,10 @@ class GeneralSectionWidget extends StatelessWidget { } Widget _getSectionOptions(BuildContext context) { + final bool showLanguageChangeOption = + FeatureFlagService.instance.isInternalUserOrDebugBuild(); return Column( children: [ - sectionOptionSpacing, - MenuItemWidget( - captionedTextWidget: CaptionedTextWidget( - title: S.of(context).familyPlans, - ), - pressedColor: getEnteColorScheme(context).fillFaint, - trailingIcon: Icons.chevron_right_outlined, - trailingIconIsMuted: true, - showOnlyLoadingState: true, - onTap: () async { - await _onFamilyPlansTapped(context); - }, - ), sectionOptionSpacing, MenuItemWidget( captionedTextWidget: CaptionedTextWidget( @@ -56,6 +49,46 @@ class GeneralSectionWidget extends StatelessWidget { }, ), sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: S.of(context).familyPlans, + ), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + showOnlyLoadingState: true, + onTap: () async { + await _onFamilyPlansTapped(context); + }, + ), + sectionOptionSpacing, + showLanguageChangeOption + ? MenuItemWidget( + captionedTextWidget: + CaptionedTextWidget(title: S.of(context).language), + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: Icons.chevron_right_outlined, + trailingIconIsMuted: true, + onTap: () async { + final locale = await getLocale(); + routeToPage( + context, + LanguageSelectorPage( + appSupportedLocales, + (locale) async { + await setLocale(locale); + EnteApp.setLocale(context, locale); + S.load(locale); + }, + locale, + ), + ); + }, + ) + : const SizedBox.shrink(), + showLanguageChangeOption + ? sectionOptionSpacing + : const SizedBox.shrink(), MenuItemWidget( captionedTextWidget: CaptionedTextWidget( title: S.of(context).advanced, diff --git a/lib/ui/settings/language_picker.dart b/lib/ui/settings/language_picker.dart new file mode 100644 index 000000000..92ec3e878 --- /dev/null +++ b/lib/ui/settings/language_picker.dart @@ -0,0 +1,173 @@ +import "package:flutter/foundation.dart"; +import "package:flutter/material.dart"; +import "package:photos/l10n/l10n.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/components/captioned_text_widget.dart"; +import "package:photos/ui/components/divider_widget.dart"; +import "package:photos/ui/components/menu_item_widget/menu_item_widget.dart"; +import "package:photos/ui/components/title_bar_title_widget.dart"; +import "package:photos/ui/components/title_bar_widget.dart"; +import "package:photos/utils/separators_util.dart"; + +class LanguageSelectorPage extends StatelessWidget { + final List supportedLocales; + final ValueChanged onLocaleChanged; + final Locale currentLocale; + + const LanguageSelectorPage( + this.supportedLocales, + this.onLocaleChanged, + this.currentLocale, { + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + primary: false, + slivers: [ + TitleBarWidget( + flexibleSpaceTitle: TitleBarTitleWidget( + title: context.l10n.selectLanguage, + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 20, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ClipRRect( + borderRadius: + const BorderRadius.all(Radius.circular(8)), + child: ItemsWidget( + supportedLocales, + onLocaleChanged, + currentLocale, + ), + ), + // MenuSectionDescriptionWidget( + // content: context.l10n.maxDeviceLimitSpikeHandling(50), + // ) + ], + ), + ); + }, + childCount: 1, + ), + ), + const SliverPadding(padding: EdgeInsets.symmetric(vertical: 12)), + ], + ), + ); + } +} + +class ItemsWidget extends StatefulWidget { + final List supportedLocales; + final ValueChanged onLocaleChanged; + final Locale currentLocale; + + const ItemsWidget( + this.supportedLocales, + this.onLocaleChanged, + this.currentLocale, { + Key? key, + }) : super(key: key); + + @override + State createState() => _ItemsWidgetState(); +} + +class _ItemsWidgetState extends State { + late Locale currentLocale; + List items = []; + + @override + void initState() { + currentLocale = widget.currentLocale; + super.initState(); + } + + @override + Widget build(BuildContext context) { + items.clear(); + for (Locale locale in widget.supportedLocales) { + items.add( + _menuItemForPicker(locale), + ); + } + items = addSeparators( + items, + DividerWidget( + dividerType: DividerType.menuNoIcon, + bgColor: getEnteColorScheme(context).fillFaint, + ), + ); + return Column( + mainAxisSize: MainAxisSize.min, + children: items, + ); + } + + String _getLanguageName(Locale locale) { + switch (locale.languageCode) { + case 'en': + return 'English'; + case 'es': + return 'Español'; + case 'fr': + return 'Français'; + case 'de': + return 'Deutsch'; + case 'it': + return 'Italiano'; + case 'nl': + return 'Nederlands'; + case 'pt': + return 'Português'; + case 'ru': + return 'Русский'; + case 'tr': + return 'Türkçe'; + case 'fi': + return 'Suomi'; + case 'zh': + return '中文'; + case 'ja': + return '日本語'; + case 'ko': + return '한국어'; + case 'ar': + return 'العربية'; + default: + return locale.languageCode; + } + } + + Widget _menuItemForPicker(Locale locale) { + return MenuItemWidget( + key: ValueKey(locale.toString()), + menuItemColor: getEnteColorScheme(context).fillFaint, + captionedTextWidget: CaptionedTextWidget( + title: _getLanguageName(locale) + (kDebugMode ? ' ($locale)' : ''), + ), + trailingIcon: currentLocale == locale ? Icons.check : null, + alignCaptionedTextToLeft: true, + isTopBorderRadiusRemoved: true, + isBottomBorderRadiusRemoved: true, + showOnlyLoadingState: true, + onTap: () async { + widget.onLocaleChanged(locale); + currentLocale = locale; + setState(() {}); + }, + ); + } +} diff --git a/lib/ui/tools/app_lock.dart b/lib/ui/tools/app_lock.dart index a6dee5bf7..f8d6401ac 100644 --- a/lib/ui/tools/app_lock.dart +++ b/lib/ui/tools/app_lock.dart @@ -35,6 +35,7 @@ class AppLock extends StatefulWidget { final ThemeData? darkTheme; final ThemeData? lightTheme; final ThemeMode savedThemeMode; + final Locale locale; const AppLock({ Key? key, @@ -42,6 +43,7 @@ class AppLock extends StatefulWidget { required this.lockScreen, required this.savedThemeMode, this.enabled = true, + this.locale = const Locale("en", "US"), this.backgroundLockLatency = const Duration(seconds: 0), this.darkTheme, this.lightTheme, @@ -110,6 +112,7 @@ class _AppLockState extends State with WidgetsBindingObserver { themeMode: widget.savedThemeMode, theme: widget.lightTheme, darkTheme: widget.darkTheme, + locale: widget.locale, supportedLocales: appSupportedLocales, localeListResolutionCallback: localResolutionCallBack, localizationsDelegates: const [ diff --git a/lib/ui/tools/editor/image_editor_page.dart b/lib/ui/tools/editor/image_editor_page.dart index 3a888177a..493f691c1 100644 --- a/lib/ui/tools/editor/image_editor_page.dart +++ b/lib/ui/tools/editor/image_editor_page.dart @@ -13,7 +13,7 @@ import 'package:photos/core/event_bus.dart'; import 'package:photos/db/files_db.dart'; import 'package:photos/events/local_photos_updated_event.dart'; import 'package:photos/models/file.dart' as ente; -import 'package:photos/models/location.dart'; +import 'package:photos/models/location/location.dart'; import 'package:photos/services/sync_service.dart'; import 'package:photos/ui/common/loading_widget.dart'; import 'package:photos/ui/components/action_sheet_widget.dart'; @@ -362,7 +362,10 @@ class _ImageEditorPageState extends State { final assetEntity = await widget.originalFile.getAsset; if (assetEntity != null) { final latLong = await assetEntity.latlngAsync(); - newFile.location = Location(latLong.latitude, latLong.longitude); + newFile.location = Location( + latitude: latLong.latitude, + longitude: latLong.longitude, + ); } } newFile.generatedID = await FilesDB.instance.insert(newFile); diff --git a/lib/ui/viewer/file/fading_bottom_bar.dart b/lib/ui/viewer/file/fading_bottom_bar.dart index 3bf0febd0..27f338e8f 100644 --- a/lib/ui/viewer/file/fading_bottom_bar.dart +++ b/lib/ui/viewer/file/fading_bottom_bar.dart @@ -76,7 +76,7 @@ class FadingBottomBarState extends State { color: Colors.white, ), onPressed: () async { - await _displayInfo(widget.file); + await _displayDetails(widget.file); safeRefresh(); //to instantly show the new caption if keypad is closed after pressing 'done' - here the caption will be updated before the bottom sheet is closed await Future.delayed( const Duration(milliseconds: 500), @@ -268,7 +268,7 @@ class FadingBottomBarState extends State { ); } - Future _displayInfo(File file) async { - await showInfoSheet(context, file); + Future _displayDetails(File file) async { + await showDetailsSheet(context, file); } } diff --git a/lib/ui/viewer/file/file_caption_widget.dart b/lib/ui/viewer/file/file_caption_widget.dart index 6f6fbd093..7841a92c1 100644 --- a/lib/ui/viewer/file/file_caption_widget.dart +++ b/lib/ui/viewer/file/file_caption_widget.dart @@ -66,7 +66,7 @@ class _FileCaptionWidgetState extends State { final _focusNode = FocusNode(); String? editedCaption; String hintText = fileCaptionDefaultHint; - Widget? keyboardTopButtoms; + Widget? keyboardTopButtons; @override void initState() { @@ -172,12 +172,12 @@ class _FileCaptionWidgetState extends State { editedCaption = caption; } final bool hasFocus = _focusNode.hasFocus; - keyboardTopButtoms ??= KeyboardTopButton( + keyboardTopButtons ??= KeyboardTopButton( onDoneTap: onDoneTap, onCancelTap: onCancelTap, ); if (hasFocus) { - KeyboardOverlay.showOverlay(context, keyboardTopButtoms!); + KeyboardOverlay.showOverlay(context, keyboardTopButtons!); } else { KeyboardOverlay.removeOverlay(); } diff --git a/lib/ui/viewer/file/file_details_widget.dart b/lib/ui/viewer/file/file_details_widget.dart index 12c9a4b58..b8333cc39 100644 --- a/lib/ui/viewer/file/file_details_widget.dart +++ b/lib/ui/viewer/file/file_details_widget.dart @@ -16,6 +16,7 @@ import 'package:photos/ui/viewer/file_details/backed_up_time_item_widget.dart'; import "package:photos/ui/viewer/file_details/creation_time_item_widget.dart"; import 'package:photos/ui/viewer/file_details/exif_item_widgets.dart'; import "package:photos/ui/viewer/file_details/file_properties_item_widget.dart"; +import "package:photos/ui/viewer/file_details/location_tags_widget.dart"; import "package:photos/ui/viewer/file_details/objects_item_widget.dart"; import "package:photos/utils/exif_util.dart"; @@ -39,12 +40,17 @@ class _FileDetailsWidgetState extends State { "takenOnDevice": null, "exposureTime": null, "ISO": null, - "megaPixels": null + "megaPixels": null, + "lat": null, + "long": null, + "latRef": null, + "longRef": null, }; bool _isImage = false; late int _currentUserID; bool showExifListTile = false; + bool hasLocationData = false; @override void initState() { @@ -52,6 +58,12 @@ class _FileDetailsWidgetState extends State { _currentUserID = Configuration.instance.getUserID()!; _isImage = widget.file.fileType == FileType.image || widget.file.fileType == FileType.livePhoto; + _exifNotifier.addListener(() { + if (_exifNotifier.value != null) { + _generateExifForLocation(_exifNotifier.value!); + hasLocationData = _hasLocationData(); + } + }); if (_isImage) { _exifNotifier.addListener(() { if (_exifNotifier.value != null) { @@ -63,10 +75,10 @@ class _FileDetailsWidgetState extends State { _exifData["exposureTime"] != null || _exifData["ISO"] != null; }); - getExif(widget.file).then((exif) { - _exifNotifier.value = exif; - }); } + getExif(widget.file).then((exif) { + _exifNotifier.value = exif; + }); super.initState(); } @@ -125,6 +137,25 @@ class _FileDetailsWidgetState extends State { }, ), ); + if (FeatureFlagService.instance.isInternalUserOrDebugBuild()) { + fileDetailsTiles.addAll([ + ValueListenableBuilder( + valueListenable: _exifNotifier, + builder: (context, _, __) { + return hasLocationData + ? Column( + children: [ + LocationTagsWidget( + widget.file.location!, + ), + const FileDetailsDivider(), + ], + ) + : const SizedBox.shrink(); + }, + ) + ]); + } if (_isImage) { fileDetailsTiles.addAll([ ValueListenableBuilder( @@ -200,6 +231,38 @@ class _FileDetailsWidgetState extends State { ); } + bool _hasLocationData() { + final fileLocation = widget.file.location; + final hasLocation = (fileLocation != null && + fileLocation.latitude != null && + fileLocation.longitude != null) && + (fileLocation.latitude != 0 || fileLocation.longitude != 0); + return hasLocation; + } + + void _generateExifForLocation(Map exif) { + if (exif["GPS GPSLatitude"] != null) { + _exifData["lat"] = exif["GPS GPSLatitude"]! + .values + .toList() + .map((e) => ((e as Ratio).numerator / e.denominator)) + .toList(); + } + if (exif["GPS GPSLongitude"] != null) { + _exifData["long"] = exif["GPS GPSLongitude"]! + .values + .toList() + .map((e) => ((e as Ratio).numerator / e.denominator)) + .toList(); + } + if (exif["GPS GPSLatitudeRef"] != null) { + _exifData["latRef"] = exif["GPS GPSLatitudeRef"].toString(); + } + if (exif["GPS GPSLongitudeRef"] != null) { + _exifData["longRef"] = exif["GPS GPSLongitudeRef"].toString(); + } + } + _generateExifForDetails(Map exif) { if (exif["EXIF FocalLength"] != null) { _exifData["focalLength"] = diff --git a/lib/ui/viewer/file_details/location_tags_widget.dart b/lib/ui/viewer/file_details/location_tags_widget.dart new file mode 100644 index 000000000..2fbc5b7de --- /dev/null +++ b/lib/ui/viewer/file_details/location_tags_widget.dart @@ -0,0 +1,121 @@ +import "dart:async"; + +import "package:flutter/material.dart"; +import "package:photos/core/event_bus.dart"; +import "package:photos/events/location_tag_updated_event.dart"; +import "package:photos/generated/l10n.dart"; +import "package:photos/models/location/location.dart"; +import "package:photos/services/location_service.dart"; +import "package:photos/states/location_screen_state.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/components/buttons/chip_button_widget.dart"; +import "package:photos/ui/components/info_item_widget.dart"; +import 'package:photos/ui/viewer/location/add_location_sheet.dart'; +import "package:photos/ui/viewer/location/location_screen.dart"; +import "package:photos/utils/navigation_util.dart"; + +class LocationTagsWidget extends StatefulWidget { + final Location centerPoint; + const LocationTagsWidget(this.centerPoint, {super.key}); + + @override + State createState() => _LocationTagsWidgetState(); +} + +class _LocationTagsWidgetState extends State { + String? title; + IconData? leadingIcon; + bool? hasChipButtons; + late Future> locationTagChips; + late StreamSubscription _locTagUpdateListener; + VoidCallback? onTap; + @override + void initState() { + locationTagChips = _getLocationTags(); + _locTagUpdateListener = + Bus.instance.on().listen((event) { + locationTagChips = _getLocationTags(); + }); + super.initState(); + } + + @override + void dispose() { + _locTagUpdateListener.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + switchInCurve: Curves.easeInOutExpo, + switchOutCurve: Curves.easeInOutExpo, + child: InfoItemWidget( + key: ValueKey(title), + leadingIcon: leadingIcon ?? Icons.pin_drop_outlined, + title: title, + subtitleSection: locationTagChips, + hasChipButtons: hasChipButtons ?? true, + onTap: onTap, + ), + ); + } + + Future> _getLocationTags() async { + final locationTags = await LocationService.instance + .enclosingLocationTags(widget.centerPoint); + if (locationTags.isEmpty) { + if (mounted) { + setState(() { + title = S.of(context).addLocation; + leadingIcon = Icons.add_location_alt_outlined; + hasChipButtons = false; + onTap = () => showAddLocationSheet( + context, + widget.centerPoint, + ); + }); + } + return [ + Text( + S.of(context).groupNearbyPhotos, + style: getEnteTextTheme(context).smallMuted, + ) + ]; + } else { + if (mounted) { + setState(() { + title = S.of(context).location; + leadingIcon = Icons.pin_drop_outlined; + hasChipButtons = true; + onTap = null; + }); + } + final result = locationTags + .map( + (locationTagEntity) => ChipButtonWidget( + locationTagEntity.item.name, + onTap: () { + routeToPage( + context, + LocationScreenStateProvider( + locationTagEntity, + const LocationScreen(), + ), + ); + }, + ), + ) + .toList(); + result.add( + ChipButtonWidget( + null, + leadingIcon: Icons.add_outlined, + onTap: () => showAddLocationSheet(context, widget.centerPoint), + ), + ); + return result; + } + } +} diff --git a/lib/ui/viewer/gallery/gallery.dart b/lib/ui/viewer/gallery/gallery.dart index 9fe53a590..40e8206d4 100644 --- a/lib/ui/viewer/gallery/gallery.dart +++ b/lib/ui/viewer/gallery/gallery.dart @@ -33,18 +33,22 @@ class Gallery extends StatefulWidget { final Stream? reloadEvent; final List>? forceReloadEvents; final Set removalEventTypes; - final SelectedFiles selectedFiles; + final SelectedFiles? selectedFiles; final String tagPrefix; final Widget? header; final Widget? footer; final Widget emptyState; final String? albumName; final double scrollBottomSafeArea; + final bool shouldCollateFilesByDay; + final Widget loadingWidget; + final bool disableScroll; + final bool limitSelectionToOne; const Gallery({ required this.asyncLoader, - required this.selectedFiles, required this.tagPrefix, + this.selectedFiles, this.initialFiles, this.reloadEvent, this.forceReloadEvents, @@ -54,6 +58,10 @@ class Gallery extends StatefulWidget { this.emptyState = const EmptyState(), this.scrollBottomSafeArea = 120.0, this.albumName = '', + this.shouldCollateFilesByDay = true, + this.loadingWidget = const EnteLoadingWidget(), + this.disableScroll = false, + this.limitSelectionToOne = false, Key? key, }) : super(key: key); @@ -168,7 +176,8 @@ class _GalleryState extends State { // Collates files and returns `true` if it resulted in a gallery reload bool _onFilesLoaded(List files) { - final updatedCollatedFiles = _collateFiles(files); + final updatedCollatedFiles = + widget.shouldCollateFilesByDay ? _collateFiles(files) : [files]; if (_collatedFiles.length != updatedCollatedFiles.length || _collatedFiles.isEmpty) { if (mounted) { @@ -198,7 +207,7 @@ class _GalleryState extends State { Widget build(BuildContext context) { _logger.finest("Building Gallery ${widget.tagPrefix}"); if (!_hasLoadedFiles) { - return const EnteLoadingWidget(); + return widget.loadingWidget; } _photoGridSize = LocalSettings.instance.getPhotoGridSize(); return _getListView(); @@ -211,6 +220,7 @@ class _GalleryState extends State { startIndex: 0, totalCount: _collatedFiles.length, isDraggableScrollbarEnabled: _collatedFiles.length > 10, + disableScroll: widget.disableScroll, waitBuilder: (_) { return const EnteLoadingWidget(); }, @@ -246,8 +256,10 @@ class _GalleryState extends State { .on() .where((event) => event.tag == widget.tagPrefix) .map((event) => event.index), + widget.shouldCollateFilesByDay, logTag: _logTag, photoGirdSize: _photoGridSize, + limitSelectionToOne: widget.limitSelectionToOne, ); if (widget.header != null && index == 0) { gallery = Column(children: [widget.header!, gallery]); diff --git a/lib/ui/viewer/location/add_location_sheet.dart b/lib/ui/viewer/location/add_location_sheet.dart new file mode 100644 index 000000000..10357c9f8 --- /dev/null +++ b/lib/ui/viewer/location/add_location_sheet.dart @@ -0,0 +1,267 @@ +import 'package:flutter/material.dart'; +import "package:modal_bottom_sheet/modal_bottom_sheet.dart"; +import "package:photos/core/constants.dart"; +import "package:photos/generated/l10n.dart"; +import "package:photos/models/location/location.dart"; +import "package:photos/services/location_service.dart"; +import 'package:photos/states/location_state.dart'; +import "package:photos/theme/colors.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/common/loading_widget.dart"; +import "package:photos/ui/components/bottom_of_title_bar_widget.dart"; +import "package:photos/ui/components/buttons/button_widget.dart"; +import "package:photos/ui/components/divider_widget.dart"; +import "package:photos/ui/components/keyboard/keybiard_oveylay.dart"; +import "package:photos/ui/components/keyboard/keyboard_top_button.dart"; +import "package:photos/ui/components/models/button_type.dart"; +import "package:photos/ui/components/text_input_widget.dart"; +import "package:photos/ui/components/title_bar_title_widget.dart"; +import 'package:photos/ui/viewer/location/dynamic_location_gallery_widget.dart'; +import "package:photos/ui/viewer/location/radius_picker_widget.dart"; + +showAddLocationSheet( + BuildContext context, + Location coordinates, +) { + showBarModalBottomSheet( + context: context, + builder: (context) { + return LocationTagStateProvider( + centerPoint: coordinates, + const AddLocationSheet(), + ); + }, + shape: const RoundedRectangleBorder( + side: BorderSide(width: 0), + borderRadius: BorderRadius.vertical( + top: Radius.circular(5), + ), + ), + topControl: const SizedBox.shrink(), + backgroundColor: getEnteColorScheme(context).backgroundElevated, + barrierColor: backdropFaintDark, + ); +} + +class AddLocationSheet extends StatefulWidget { + const AddLocationSheet({super.key}); + + @override + State createState() => _AddLocationSheetState(); +} + +class _AddLocationSheetState extends State { + //The value of these notifiers has no significance. + //When memoriesCountNotifier is null, we show the loading widget in the + //memories count section which also means the gallery is loading. + final ValueNotifier _memoriesCountNotifier = ValueNotifier(null); + final ValueNotifier _submitNotifer = ValueNotifier(false); + final ValueNotifier _cancelNotifier = ValueNotifier(false); + final ValueNotifier _selectedRadiusIndexNotifier = + ValueNotifier(defaultRadiusValueIndex); + final _focusNode = FocusNode(); + final _textEditingController = TextEditingController(); + final _isEmptyNotifier = ValueNotifier(true); + Widget? _keyboardTopButtons; + + @override + void initState() { + _focusNode.addListener(_focusNodeListener); + _selectedRadiusIndexNotifier.addListener(_selectedRadiusIndexListener); + super.initState(); + } + + @override + void dispose() { + _focusNode.removeListener(_focusNodeListener); + _submitNotifer.dispose(); + _cancelNotifier.dispose(); + _selectedRadiusIndexNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final textTheme = getEnteTextTheme(context); + final colorScheme = getEnteColorScheme(context); + return Padding( + padding: const EdgeInsets.fromLTRB(0, 32, 0, 8), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: BottomOfTitleBarWidget( + title: TitleBarTitleWidget(title: S.of(context).addLocation), + ), + ), + Expanded( + child: SingleChildScrollView( + physics: const BouncingScrollPhysics( + decelerationRate: ScrollDecelerationRate.fast, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: TextInputWidget( + hintText: S.of(context).locationName, + borderRadius: 2, + focusNode: _focusNode, + submitNotifier: _submitNotifer, + cancelNotifier: _cancelNotifier, + popNavAfterSubmission: false, + shouldUnfocusOnClearOrSubmit: true, + alwaysShowSuccessState: true, + textCapitalization: TextCapitalization.words, + textEditingController: _textEditingController, + isEmptyNotifier: _isEmptyNotifier, + ), + ), + const SizedBox(width: 8), + ValueListenableBuilder( + valueListenable: _isEmptyNotifier, + builder: (context, bool value, _) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + switchInCurve: Curves.easeInOut, + switchOutCurve: Curves.easeInOut, + child: ButtonWidget( + key: ValueKey(value), + buttonType: ButtonType.secondary, + buttonSize: ButtonSize.small, + labelText: S.of(context).addLocationButton, + isDisabled: value, + onTap: () async { + _focusNode.unfocus(); + await _addLocationTag(); + }, + ), + ); + }, + ) + ], + ), + const SizedBox(height: 24), + RadiusPickerWidget( + _selectedRadiusIndexNotifier, + ), + const SizedBox(height: 20), + Text( + S.of(context).locationTagFeatureDescription, + style: textTheme.smallMuted, + ), + ], + ), + ), + const DividerWidget( + dividerType: DividerType.solid, + padding: EdgeInsets.only(top: 24, bottom: 20), + ), + SizedBox( + width: double.infinity, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: ValueListenableBuilder( + valueListenable: _memoriesCountNotifier, + builder: (context, int? value, _) { + Widget widget; + if (value == null) { + widget = RepaintBoundary( + child: EnteLoadingWidget( + size: 14, + color: colorScheme.strokeMuted, + alignment: Alignment.centerLeft, + padding: 3, + ), + ); + } else { + widget = Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + S.of(context).memoryCount(value), + style: textTheme.body, + ), + if (value > 1000) + Padding( + padding: const EdgeInsets.only(top: 2), + child: Text( + S.of(context).galleryMemoryLimitInfo, + style: textTheme.miniMuted, + ), + ), + ], + ); + } + return Align( + alignment: Alignment.centerLeft, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + switchInCurve: Curves.easeInOutExpo, + switchOutCurve: Curves.easeInOutExpo, + child: widget, + ), + ); + }, + ), + ), + ), + const SizedBox(height: 24), + DynamicLocationGalleryWidget( + _memoriesCountNotifier, + "Add_location", + ), + ], + ), + ), + ), + ], + ), + ); + } + + Future _addLocationTag() async { + final locationData = InheritedLocationTagData.of(context); + final coordinates = locationData.centerPoint; + final radius = radiusValues[locationData.selectedRadiusIndex]; + await LocationService.instance.addLocation( + _textEditingController.text.trim(), + coordinates, + radius, + ); + Navigator.pop(context); + } + + void _focusNodeListener() { + final bool hasFocus = _focusNode.hasFocus; + _keyboardTopButtons ??= KeyboardTopButton( + onDoneTap: () { + _submitNotifer.value = !_submitNotifer.value; + }, + onCancelTap: () { + _cancelNotifier.value = !_cancelNotifier.value; + }, + ); + if (hasFocus) { + KeyboardOverlay.showOverlay(context, _keyboardTopButtons!); + } else { + KeyboardOverlay.removeOverlay(); + } + } + + void _selectedRadiusIndexListener() { + InheritedLocationTagData.of( + context, + ).updateSelectedIndex( + _selectedRadiusIndexNotifier.value, + ); + _memoriesCountNotifier.value = null; + } +} diff --git a/lib/ui/viewer/location/dynamic_location_gallery_widget.dart b/lib/ui/viewer/location/dynamic_location_gallery_widget.dart new file mode 100644 index 000000000..d49e0f625 --- /dev/null +++ b/lib/ui/viewer/location/dynamic_location_gallery_widget.dart @@ -0,0 +1,144 @@ +import "dart:developer" as dev; +import "dart:math"; + +import "package:flutter/material.dart"; +import "package:photos/core/constants.dart"; +import "package:photos/db/files_db.dart"; +import "package:photos/models/file.dart"; +import "package:photos/models/file_load_result.dart"; +import "package:photos/services/collections_service.dart"; +import "package:photos/services/files_service.dart"; +import "package:photos/services/location_service.dart"; +import 'package:photos/states/location_state.dart'; +import "package:photos/ui/viewer/gallery/gallery.dart"; +import "package:photos/utils/local_settings.dart"; + +///This gallery will get rebuilt with the updated radius when +///InheritedLocationTagData notifies a change in radius. +class DynamicLocationGalleryWidget extends StatefulWidget { + final ValueNotifier memoriesCountNotifier; + final String tagPrefix; + const DynamicLocationGalleryWidget( + this.memoriesCountNotifier, + this.tagPrefix, { + super.key, + }); + + @override + State createState() => + _DynamicLocationGalleryWidgetState(); +} + +class _DynamicLocationGalleryWidgetState + extends State { + late final Future fileLoadResult; + late Future removeIgnoredFiles; + double heightOfGallery = 0; + + @override + void initState() { + final collectionsToHide = + CollectionsService.instance.collectionsHiddenFromTimeline(); + fileLoadResult = + FilesDB.instance.fetchAllUploadedAndSharedFilesWithLocation( + galleryLoadStartTime, + galleryLoadEndTime, + limit: null, + asc: false, + ignoredCollectionIDs: collectionsToHide, + ); + removeIgnoredFiles = + FilesService.instance.removeIgnoredFiles(fileLoadResult); + super.initState(); + } + + @override + Widget build(BuildContext context) { + const galleryFilesLimit = 1000; + final selectedRadius = _selectedRadius(); + Future filterFiles() async { + final FileLoadResult result = await fileLoadResult; + //wait for ignored files to be removed after init + await removeIgnoredFiles; + final stopWatch = Stopwatch()..start(); + final copyOfFiles = List.from(result.files); + copyOfFiles.removeWhere((f) { + return !LocationService.instance.isFileInsideLocationTag( + InheritedLocationTagData.of(context).centerPoint, + f.location!, + selectedRadius, + ); + }); + dev.log( + "Time taken to get all files in a location tag: ${stopWatch.elapsedMilliseconds} ms", + ); + stopWatch.stop(); + widget.memoriesCountNotifier.value = copyOfFiles.length; + final limitedResults = copyOfFiles.take(galleryFilesLimit).toList(); + + return Future.value( + FileLoadResult( + limitedResults, + result.hasMore, + ), + ); + } + + return FutureBuilder( + //Only rebuild Gallery if the center point or radius changes + key: ValueKey( + "${InheritedLocationTagData.of(context).centerPoint}$selectedRadius", + ), + builder: (context, snapshot) { + if (snapshot.hasData) { + return SizedBox( + height: _galleryHeight( + min( + (widget.memoriesCountNotifier.value ?? 0), + galleryFilesLimit, + ), + ), + child: Gallery( + loadingWidget: const SizedBox.shrink(), + disableScroll: true, + asyncLoader: ( + creationStartTime, + creationEndTime, { + limit, + asc, + }) async { + return snapshot.data as FileLoadResult; + }, + tagPrefix: widget.tagPrefix, + shouldCollateFilesByDay: false, + ), + ); + } else { + return const SizedBox.shrink(); + } + }, + future: filterFiles(), + ); + } + + int _selectedRadius() { + return radiusValues[ + InheritedLocationTagData.of(context).selectedRadiusIndex]; + } + + double _galleryHeight(int fileCount) { + final photoGridSize = LocalSettings.instance.getPhotoGridSize(); + final totalWhiteSpaceBetweenPhotos = + galleryGridSpacing * (photoGridSize - 1); + + final thumbnailHeight = + ((MediaQuery.of(context).size.width - totalWhiteSpaceBetweenPhotos) / + photoGridSize); + + final numberOfRows = (fileCount / photoGridSize).ceil(); + + final galleryHeight = (thumbnailHeight * numberOfRows) + + (galleryGridSpacing * (numberOfRows - 1)); + return galleryHeight + 120; + } +} diff --git a/lib/ui/viewer/location/edit_center_point_tile_widget.dart b/lib/ui/viewer/location/edit_center_point_tile_widget.dart new file mode 100644 index 000000000..7611a9659 --- /dev/null +++ b/lib/ui/viewer/location/edit_center_point_tile_widget.dart @@ -0,0 +1,70 @@ +import "package:flutter/material.dart"; +import "package:photos/generated/l10n.dart"; +import "package:photos/models/file.dart"; +import "package:photos/services/location_service.dart"; +import "package:photos/states/location_state.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/viewer/location/pick_center_point_widget.dart"; + +class EditCenterPointTileWidget extends StatelessWidget { + const EditCenterPointTileWidget({super.key}); + + @override + Widget build(BuildContext context) { + final textTheme = getEnteTextTheme(context); + final colorScheme = getEnteColorScheme(context); + return Row( + children: [ + Container( + width: 48, + height: 48, + color: colorScheme.fillFaint, + child: Icon( + Icons.location_on_outlined, + color: colorScheme.strokeFaint, + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.fromLTRB(12, 4.5, 16, 4.5), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + S.of(context).centerPoint, + style: textTheme.body, + ), + const SizedBox(height: 4), + Text( + LocationService.instance.convertLocationToDMS( + InheritedLocationTagData.of(context) + .locationTagEntity! + .item + .centerPoint, + ), + style: textTheme.miniMuted, + ), + ], + ), + ), + ), + IconButton( + onPressed: () async { + final File? centerPointFile = await showPickCenterPointSheet( + context, + InheritedLocationTagData.of(context).locationTagEntity!, + ); + if (centerPointFile != null) { + InheritedLocationTagData.of(context) + .updateCenterPoint(centerPointFile.location!); + } + }, + icon: const Icon(Icons.edit), + color: getEnteColorScheme(context).strokeMuted, + ), + ], + ); + } +} diff --git a/lib/ui/viewer/location/edit_location_sheet.dart b/lib/ui/viewer/location/edit_location_sheet.dart new file mode 100644 index 000000000..d65d32663 --- /dev/null +++ b/lib/ui/viewer/location/edit_location_sheet.dart @@ -0,0 +1,275 @@ +import 'package:flutter/material.dart'; +import "package:modal_bottom_sheet/modal_bottom_sheet.dart"; +import "package:photos/core/constants.dart"; +import "package:photos/generated/l10n.dart"; +import "package:photos/models/local_entity_data.dart"; +import "package:photos/models/location_tag/location_tag.dart"; +import "package:photos/services/location_service.dart"; +import "package:photos/states/location_state.dart"; +import "package:photos/theme/colors.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/common/loading_widget.dart"; +import "package:photos/ui/components/bottom_of_title_bar_widget.dart"; +import "package:photos/ui/components/buttons/button_widget.dart"; +import "package:photos/ui/components/divider_widget.dart"; +import "package:photos/ui/components/keyboard/keybiard_oveylay.dart"; +import "package:photos/ui/components/keyboard/keyboard_top_button.dart"; +import "package:photos/ui/components/models/button_type.dart"; +import "package:photos/ui/components/text_input_widget.dart"; +import "package:photos/ui/components/title_bar_title_widget.dart"; +import 'package:photos/ui/viewer/location/dynamic_location_gallery_widget.dart'; +import "package:photos/ui/viewer/location/edit_center_point_tile_widget.dart"; +import "package:photos/ui/viewer/location/radius_picker_widget.dart"; + +showEditLocationSheet( + BuildContext context, + LocalEntity locationTagEntity, +) { + showBarModalBottomSheet( + context: context, + builder: (context) { + return LocationTagStateProvider( + locationTagEntity: locationTagEntity, + const EditLocationSheet(), + ); + }, + shape: const RoundedRectangleBorder( + side: BorderSide(width: 0), + borderRadius: BorderRadius.vertical( + top: Radius.circular(5), + ), + ), + topControl: const SizedBox.shrink(), + backgroundColor: getEnteColorScheme(context).backgroundElevated, + barrierColor: backdropFaintDark, + ); +} + +class EditLocationSheet extends StatefulWidget { + const EditLocationSheet({ + super.key, + }); + + @override + State createState() => _EditLocationSheetState(); +} + +class _EditLocationSheetState extends State { + //The value of these notifiers has no significance. + //When memoriesCountNotifier is null, we show the loading widget in the + //memories count section which also means the gallery is loading. + final ValueNotifier _memoriesCountNotifier = ValueNotifier(null); + final ValueNotifier _submitNotifer = ValueNotifier(false); + final ValueNotifier _cancelNotifier = ValueNotifier(false); + final ValueNotifier _selectedRadiusIndexNotifier = + ValueNotifier(defaultRadiusValueIndex); + final _focusNode = FocusNode(); + final _textEditingController = TextEditingController(); + final _isEmptyNotifier = ValueNotifier(false); + Widget? _keyboardTopButtons; + + @override + void initState() { + _focusNode.addListener(_focusNodeListener); + _selectedRadiusIndexNotifier.addListener(_selectedRadiusIndexListener); + super.initState(); + } + + @override + void dispose() { + _focusNode.removeListener(_focusNodeListener); + _submitNotifer.dispose(); + _cancelNotifier.dispose(); + _selectedRadiusIndexNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final textTheme = getEnteTextTheme(context); + final colorScheme = getEnteColorScheme(context); + final locationName = + InheritedLocationTagData.of(context).locationTagEntity!.item.name; + return Padding( + padding: const EdgeInsets.fromLTRB(0, 32, 0, 8), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 16), + child: BottomOfTitleBarWidget( + title: TitleBarTitleWidget(title: "Edit location"), + ), + ), + Expanded( + child: SingleChildScrollView( + physics: const BouncingScrollPhysics( + decelerationRate: ScrollDecelerationRate.fast, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: TextInputWidget( + hintText: S.of(context).locationName, + borderRadius: 2, + focusNode: _focusNode, + submitNotifier: _submitNotifer, + cancelNotifier: _cancelNotifier, + popNavAfterSubmission: false, + shouldUnfocusOnClearOrSubmit: true, + alwaysShowSuccessState: true, + initialValue: locationName, + onCancel: () { + _focusNode.unfocus(); + _textEditingController.value = + TextEditingValue(text: locationName); + }, + textEditingController: _textEditingController, + isEmptyNotifier: _isEmptyNotifier, + ), + ), + const SizedBox(width: 8), + ValueListenableBuilder( + valueListenable: _isEmptyNotifier, + builder: (context, bool value, _) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + switchInCurve: Curves.easeInOut, + switchOutCurve: Curves.easeInOut, + child: ButtonWidget( + key: ValueKey(value), + buttonType: ButtonType.secondary, + buttonSize: ButtonSize.small, + labelText: S.of(context).save, + isDisabled: value, + onTap: () async { + _focusNode.unfocus(); + await _editLocation(); + }, + ), + ); + }, + ), + ], + ), + const SizedBox(height: 20), + const EditCenterPointTileWidget(), + const SizedBox(height: 20), + RadiusPickerWidget( + _selectedRadiusIndexNotifier, + ), + const SizedBox(height: 20), + ], + ), + ), + const DividerWidget( + dividerType: DividerType.solid, + padding: EdgeInsets.only(top: 24, bottom: 20), + ), + SizedBox( + width: double.infinity, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: ValueListenableBuilder( + valueListenable: _memoriesCountNotifier, + builder: (context, int? value, _) { + Widget widget; + if (value == null) { + widget = RepaintBoundary( + child: EnteLoadingWidget( + size: 14, + color: colorScheme.strokeMuted, + alignment: Alignment.centerLeft, + padding: 3, + ), + ); + } else { + widget = Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + S.of(context).memoryCount(value), + style: textTheme.body, + ), + if (value > 1000) + Padding( + padding: const EdgeInsets.only(top: 2), + child: Text( + S.of(context).galleryMemoryLimitInfo, + style: textTheme.miniMuted, + ), + ), + ], + ); + } + return Align( + alignment: Alignment.centerLeft, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + switchInCurve: Curves.easeInOutExpo, + switchOutCurve: Curves.easeInOutExpo, + child: widget, + ), + ); + }, + ), + ), + ), + const SizedBox(height: 24), + DynamicLocationGalleryWidget( + _memoriesCountNotifier, + "Edit_location", + ), + ], + ), + ), + ), + ], + ), + ); + } + + Future _editLocation() async { + final locationTagState = InheritedLocationTagData.of(context); + await LocationService.instance.updateLocationTag( + locationTagEntity: locationTagState.locationTagEntity!, + newRadius: radiusValues[locationTagState.selectedRadiusIndex], + newName: _textEditingController.text.trim(), + newCenterPoint: InheritedLocationTagData.of(context).centerPoint, + ); + Navigator.of(context).pop(); + } + + void _focusNodeListener() { + final bool hasFocus = _focusNode.hasFocus; + _keyboardTopButtons ??= KeyboardTopButton( + onDoneTap: () { + _submitNotifer.value = !_submitNotifer.value; + }, + onCancelTap: () { + _cancelNotifier.value = !_cancelNotifier.value; + }, + ); + if (hasFocus) { + KeyboardOverlay.showOverlay(context, _keyboardTopButtons!); + } else { + KeyboardOverlay.removeOverlay(); + } + } + + void _selectedRadiusIndexListener() { + InheritedLocationTagData.of( + context, + ).updateSelectedIndex( + _selectedRadiusIndexNotifier.value, + ); + _memoriesCountNotifier.value = null; + } +} diff --git a/lib/ui/viewer/location/location_screen.dart b/lib/ui/viewer/location/location_screen.dart new file mode 100644 index 000000000..c577cfe0f --- /dev/null +++ b/lib/ui/viewer/location/location_screen.dart @@ -0,0 +1,302 @@ +import 'dart:developer' as dev; +import "package:flutter/material.dart"; +import "package:photos/core/constants.dart"; +import "package:photos/core/event_bus.dart"; +import "package:photos/db/files_db.dart"; +import "package:photos/events/files_updated_event.dart"; +import "package:photos/events/local_photos_updated_event.dart"; +import "package:photos/generated/l10n.dart"; +import "package:photos/models/file.dart"; +import "package:photos/models/file_load_result.dart"; +import "package:photos/models/gallery_type.dart"; +import "package:photos/models/selected_files.dart"; +import "package:photos/services/collections_service.dart"; +import "package:photos/services/files_service.dart"; +import "package:photos/services/location_service.dart"; +import "package:photos/states/location_screen_state.dart"; +import "package:photos/theme/colors.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/common/loading_widget.dart"; +import "package:photos/ui/components/buttons/icon_button_widget.dart"; +import "package:photos/ui/components/title_bar_title_widget.dart"; +import "package:photos/ui/components/title_bar_widget.dart"; +import "package:photos/ui/viewer/actions/file_selection_overlay_bar.dart"; +import "package:photos/ui/viewer/gallery/gallery.dart"; +import "package:photos/ui/viewer/location/edit_location_sheet.dart"; +import "package:photos/utils/dialog_util.dart"; + +class LocationScreen extends StatelessWidget { + const LocationScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const PreferredSize( + preferredSize: Size(double.infinity, 48), + child: TitleBarWidget( + isSliver: false, + isFlexibleSpaceDisabled: true, + actionIcons: [LocationScreenPopUpMenu()], + ), + ), + body: Column( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height - 102, + width: double.infinity, + child: const LocationGalleryWidget(), + ), + ], + ), + ); + } +} + +class LocationScreenPopUpMenu extends StatelessWidget { + const LocationScreenPopUpMenu({super.key}); + + @override + Widget build(BuildContext context) { + final textTheme = getEnteTextTheme(context); + final colorScheme = getEnteColorScheme(context); + return Padding( + padding: const EdgeInsets.only(right: 4), + child: Theme( + data: Theme.of(context).copyWith( + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + ), + child: PopupMenuButton( + elevation: 2, + offset: const Offset(10, 50), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + color: colorScheme.backgroundElevated2, + child: const IconButtonWidget( + icon: Icons.more_horiz, + iconButtonType: IconButtonType.primary, + disableGestureDetector: true, + ), + itemBuilder: (context) { + return [ + PopupMenuItem( + value: "edit", + child: Text( + S.of(context).edit, + style: textTheme.bodyBold, + ), + ), + PopupMenuItem( + onTap: () {}, + value: "delete", + child: Text( + S.of(context).deleteLocation, + style: textTheme.bodyBold.copyWith(color: warning500), + ), + ), + ]; + }, + onSelected: (value) async { + if (value == "edit") { + showEditLocationSheet( + context, + InheritedLocationScreenState.of(context).locationTagEntity, + ); + } else if (value == "delete") { + try { + await LocationService.instance.deleteLocationTag( + InheritedLocationScreenState.of(context).locationTagEntity.id, + ); + Navigator.of(context).pop(); + } catch (e) { + showGenericErrorDialog(context: context); + } + } + }, + ), + ), + ); + } +} + +class LocationGalleryWidget extends StatefulWidget { + const LocationGalleryWidget({super.key}); + + @override + State createState() => _LocationGalleryWidgetState(); +} + +class _LocationGalleryWidgetState extends State { + late final Future fileLoadResult; + late Future removeIgnoredFiles; + late Widget galleryHeaderWidget; + final _selectedFiles = SelectedFiles(); + @override + void initState() { + final collectionsToHide = + CollectionsService.instance.collectionsHiddenFromTimeline(); + fileLoadResult = + FilesDB.instance.fetchAllUploadedAndSharedFilesWithLocation( + galleryLoadStartTime, + galleryLoadEndTime, + limit: null, + asc: false, + ignoredCollectionIDs: collectionsToHide, + ); + removeIgnoredFiles = + FilesService.instance.removeIgnoredFiles(fileLoadResult); + galleryHeaderWidget = const GalleryHeaderWidget(); + super.initState(); + } + + @override + void dispose() { + InheritedLocationScreenState.memoryCountNotifier.value = null; + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final selectedRadius = + InheritedLocationScreenState.of(context).locationTagEntity.item.radius; + final centerPoint = InheritedLocationScreenState.of(context) + .locationTagEntity + .item + .centerPoint; + Future filterFiles() async { + final FileLoadResult result = await fileLoadResult; + //wait for ignored files to be removed after init + await removeIgnoredFiles; + final stopWatch = Stopwatch()..start(); + final copyOfFiles = List.from(result.files); + copyOfFiles.removeWhere((f) { + return !LocationService.instance.isFileInsideLocationTag( + centerPoint, + f.location!, + selectedRadius, + ); + }); + dev.log( + "Time taken to get all files in a location tag: ${stopWatch.elapsedMilliseconds} ms", + ); + stopWatch.stop(); + InheritedLocationScreenState.memoryCountNotifier.value = + copyOfFiles.length; + + return Future.value( + FileLoadResult( + copyOfFiles, + result.hasMore, + ), + ); + } + + return FutureBuilder( + //rebuild gallery only when there is change in radius or center point + key: ValueKey("$centerPoint$selectedRadius"), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Stack( + children: [ + Gallery( + loadingWidget: Column( + children: [ + galleryHeaderWidget, + EnteLoadingWidget( + color: getEnteColorScheme(context).strokeMuted, + ), + ], + ), + header: galleryHeaderWidget, + asyncLoader: ( + creationStartTime, + creationEndTime, { + limit, + asc, + }) async { + return snapshot.data as FileLoadResult; + }, + reloadEvent: Bus.instance.on(), + removalEventTypes: const { + EventType.deletedFromRemote, + EventType.deletedFromEverywhere, + }, + selectedFiles: _selectedFiles, + tagPrefix: "location_gallery", + ), + FileSelectionOverlayBar( + GalleryType.locationTag, + _selectedFiles, + ) + ], + ); + } else { + return Column( + children: [ + galleryHeaderWidget, + const Expanded( + child: EnteLoadingWidget(), + ), + ], + ); + } + }, + future: filterFiles(), + ); + } +} + +class GalleryHeaderWidget extends StatefulWidget { + const GalleryHeaderWidget({super.key}); + + @override + State createState() => _GalleryHeaderWidgetState(); +} + +class _GalleryHeaderWidgetState extends State { + @override + Widget build(BuildContext context) { + final locationName = + InheritedLocationScreenState.of(context).locationTagEntity.item.name; + return Padding( + padding: const EdgeInsets.only(bottom: 20), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + key: ValueKey(locationName), + width: double.infinity, + child: TitleBarTitleWidget( + title: locationName, + ), + ), + ValueListenableBuilder( + valueListenable: InheritedLocationScreenState.memoryCountNotifier, + builder: (context, int? value, _) { + if (value == null) { + return RepaintBoundary( + child: EnteLoadingWidget( + size: 12, + color: getEnteColorScheme(context).strokeMuted, + alignment: Alignment.centerLeft, + padding: 2.5, + ), + ); + } else { + return Text( + S.of(context).memoryCount(value), + style: getEnteTextTheme(context).smallMuted, + ); + } + }, + ) + ], + ), + ), + ); + } +} diff --git a/lib/ui/viewer/location/pick_center_point_widget.dart b/lib/ui/viewer/location/pick_center_point_widget.dart new file mode 100644 index 000000000..2e3762cb2 --- /dev/null +++ b/lib/ui/viewer/location/pick_center_point_widget.dart @@ -0,0 +1,183 @@ +import "dart:math"; + +import "package:flutter/material.dart"; +import "package:modal_bottom_sheet/modal_bottom_sheet.dart"; +import "package:photos/core/constants.dart"; +import "package:photos/core/event_bus.dart"; +import "package:photos/db/files_db.dart"; +import "package:photos/events/local_photos_updated_event.dart"; +import "package:photos/generated/l10n.dart"; +import "package:photos/models/file.dart"; +import "package:photos/models/file_load_result.dart"; +import "package:photos/models/local_entity_data.dart"; +import "package:photos/models/location_tag/location_tag.dart"; +import "package:photos/models/selected_files.dart"; +import "package:photos/services/collections_service.dart"; +import "package:photos/services/ignored_files_service.dart"; +import "package:photos/theme/colors.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/components/bottom_of_title_bar_widget.dart"; +import "package:photos/ui/components/buttons/button_widget.dart"; +import "package:photos/ui/components/models/button_type.dart"; +import "package:photos/ui/components/title_bar_title_widget.dart"; +import "package:photos/ui/viewer/gallery/gallery.dart"; + +Future showPickCenterPointSheet( + BuildContext context, + LocalEntity locationTagEntity, +) async { + return await showBarModalBottomSheet( + context: context, + builder: (context) { + return PickCenterPointWidget(locationTagEntity); + }, + shape: const RoundedRectangleBorder( + side: BorderSide(width: 0), + borderRadius: BorderRadius.vertical( + top: Radius.circular(5), + ), + ), + topControl: const SizedBox.shrink(), + backgroundColor: getEnteColorScheme(context).backgroundElevated, + barrierColor: backdropFaintDark, + enableDrag: false, + ); +} + +class PickCenterPointWidget extends StatelessWidget { + final LocalEntity locationTagEntity; + + const PickCenterPointWidget( + this.locationTagEntity, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final ValueNotifier isFileSelected = ValueNotifier(false); + final selectedFiles = SelectedFiles(); + selectedFiles.addListener(() { + isFileSelected.value = selectedFiles.files.isNotEmpty; + }); + + return Padding( + padding: const EdgeInsets.all(0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: min(428, MediaQuery.of(context).size.width), + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(0, 32, 0, 8), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Column( + children: [ + BottomOfTitleBarWidget( + title: TitleBarTitleWidget( + title: S.of(context).pickCenterPoint, + ), + caption: locationTagEntity.item.name, + ), + Expanded( + child: Gallery( + asyncLoader: ( + creationStartTime, + creationEndTime, { + limit, + asc, + }) async { + final collectionsToHide = CollectionsService + .instance + .collectionsHiddenFromTimeline(); + FileLoadResult result; + result = await FilesDB.instance + .fetchAllUploadedAndSharedFilesWithLocation( + galleryLoadStartTime, + galleryLoadEndTime, + limit: null, + asc: false, + ignoredCollectionIDs: collectionsToHide, + ); + + // hide ignored files from UI + final ignoredIDs = + await IgnoredFilesService.instance.ignoredIDs; + result.files.removeWhere( + (f) => + f.uploadedFileID == null && + IgnoredFilesService.instance + .shouldSkipUpload(ignoredIDs, f), + ); + return result; + }, + reloadEvent: + Bus.instance.on(), + tagPrefix: "pick_center_point_gallery", + selectedFiles: selectedFiles, + limitSelectionToOne: true, + ), + ), + ], + ), + ), + SafeArea( + child: Container( + //inner stroke of 1pt + 15 pts of top padding = 16 pts + padding: const EdgeInsets.fromLTRB(16, 15, 16, 8), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: getEnteColorScheme(context).strokeFaint, + ), + ), + ), + child: Column( + children: [ + ValueListenableBuilder( + valueListenable: isFileSelected, + builder: (context, bool value, _) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + switchInCurve: Curves.easeInOutExpo, + switchOutCurve: Curves.easeInOutExpo, + child: ButtonWidget( + key: ValueKey(value), + isDisabled: !value, + buttonType: ButtonType.neutral, + labelText: S.of(context).useSelectedPhoto, + onTap: () async { + final selectedFile = + selectedFiles.files.first; + Navigator.pop(context, selectedFile); + }, + ), + ); + }, + ), + const SizedBox(height: 8), + ButtonWidget( + buttonType: ButtonType.secondary, + buttonAction: ButtonAction.cancel, + labelText: S.of(context).cancel, + onTap: () async { + Navigator.of(context).pop(); + }, + ), + ], + ), + ), + ) + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/viewer/location/radius_picker_widget.dart b/lib/ui/viewer/location/radius_picker_widget.dart new file mode 100644 index 000000000..ecbf7ce82 --- /dev/null +++ b/lib/ui/viewer/location/radius_picker_widget.dart @@ -0,0 +1,144 @@ +import "package:flutter/material.dart"; +import "package:photos/core/constants.dart"; +import "package:photos/generated/l10n.dart"; +import "package:photos/states/location_state.dart"; +import "package:photos/theme/colors.dart"; +import "package:photos/theme/ente_theme.dart"; + +class CustomTrackShape extends RoundedRectSliderTrackShape { + @override + Rect getPreferredRect({ + required RenderBox parentBox, + Offset offset = Offset.zero, + required SliderThemeData sliderTheme, + bool isEnabled = false, + bool isDiscrete = false, + }) { + const trackHeight = 2.0; + final trackWidth = parentBox.size.width; + return Rect.fromLTWH(0, 0, trackWidth, trackHeight); + } +} + +class RadiusPickerWidget extends StatefulWidget { + ///This notifier can be listened to get the selected radius index from + ///a parent widget. + final ValueNotifier selectedRadiusIndexNotifier; + const RadiusPickerWidget( + this.selectedRadiusIndexNotifier, { + super.key, + }); + + @override + State createState() => _RadiusPickerWidgetState(); +} + +class _RadiusPickerWidgetState extends State { + @override + void initState() { + super.initState(); + } + + @override + void didChangeDependencies() { + widget.selectedRadiusIndexNotifier.value = + InheritedLocationTagData.of(context).selectedRadiusIndex; + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + final selectedRadiusIndex = widget.selectedRadiusIndexNotifier.value; + final radiusValue = radiusValues[selectedRadiusIndex]; + final textTheme = getEnteTextTheme(context); + final colorScheme = getEnteColorScheme(context); + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 48, + width: 48, + decoration: BoxDecoration( + color: colorScheme.fillFaint, + borderRadius: const BorderRadius.all(Radius.circular(2)), + ), + padding: const EdgeInsets.all(4), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 6, + child: Text( + radiusValue.toString(), + style: radiusValue != 1200 + ? textTheme.largeBold + : textTheme.bodyBold, + textAlign: TextAlign.center, + ), + ), + Expanded( + flex: 5, + child: Text( + S.of(context).kiloMeterUnit, + style: textTheme.miniMuted, + ), + ), + ], + ), + ), + const SizedBox(width: 4), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(S.of(context).radius, style: textTheme.body), + const SizedBox(height: 16), + SizedBox( + height: 16, + child: SliderTheme( + data: SliderThemeData( + overlayColor: Colors.transparent, + thumbColor: strokeSolidMutedLight, + activeTrackColor: strokeSolidMutedLight, + inactiveTrackColor: colorScheme.strokeFaint, + activeTickMarkColor: colorScheme.strokeMuted, + inactiveTickMarkColor: strokeSolidMutedLight, + trackShape: CustomTrackShape(), + thumbShape: const RoundSliderThumbShape( + enabledThumbRadius: 6, + pressedElevation: 0, + elevation: 0, + ), + tickMarkShape: const RoundSliderTickMarkShape( + tickMarkRadius: 1, + ), + ), + child: RepaintBoundary( + child: Slider( + value: selectedRadiusIndex.toDouble(), + onChanged: (value) { + setState(() { + widget.selectedRadiusIndexNotifier.value = + value.toInt(); + }); + }, + min: 0, + max: radiusValues.length - 1, + divisions: radiusValues.length - 1, + ), + ), + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index caea58bd0..e4b5ba64f 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -74,7 +74,12 @@ class SearchSuggestionsWidget extends StatelessWidget { } else if (result is FileSearchResult) { return FileSearchResultWidget(result); } else if (result is GenericSearchResult) { - return SearchResultWidget(result); + return SearchResultWidget( + result, + onResultTap: result.onResultTap != null + ? () => result.onResultTap!(context) + : null, + ); } else { Logger('SearchSuggestionsWidget') .info("Invalid/Unsupported value"); diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index c9b37e052..ed206f486 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -5,7 +5,6 @@ import 'package:logging/logging.dart'; import 'package:photos/ente_theme_data.dart'; import "package:photos/generated/l10n.dart"; import 'package:photos/models/search/search_result.dart'; -import 'package:photos/services/feature_flag_service.dart'; import 'package:photos/services/search_service.dart'; import 'package:photos/ui/components/buttons/icon_button_widget.dart'; import 'package:photos/ui/viewer/search/result/no_result_widget.dart'; @@ -215,16 +214,19 @@ class _SearchWidgetState extends State { await _searchService.getFileExtensionResults(query); allResults.addAll(fileExtnResult); + final locationResult = await _searchService.getLocationResults(query); + allResults.addAll(locationResult); + final collectionResults = await _searchService.getCollectionSearchResults(query); allResults.addAll(collectionResults); - if (FeatureFlagService.instance.isInternalUserOrDebugBuild() && - query.startsWith("l:")) { - final locationResults = await _searchService - .getLocationSearchResults(query.replaceAll("l:", "")); - allResults.addAll(locationResults); - } + // if (FeatureFlagService.instance.isInternalUserOrDebugBuild() && + // query.startsWith("l:")) { + // final locationResults = await _searchService + // .getLocationSearchResults(query.replaceAll("l:", "")); + // allResults.addAll(locationResults); + // } final monthResults = await _searchService.getMonthSearchResults(query); allResults.addAll(monthResults); diff --git a/lib/utils/file_uploader_util.dart b/lib/utils/file_uploader_util.dart index 64b3f77d3..e7c0a87ea 100644 --- a/lib/utils/file_uploader_util.dart +++ b/lib/utils/file_uploader_util.dart @@ -13,7 +13,7 @@ import 'package:photos/core/constants.dart'; import 'package:photos/core/errors.dart'; import 'package:photos/models/file.dart' as ente; import 'package:photos/models/file_type.dart'; -import 'package:photos/models/location.dart'; +import 'package:photos/models/location/location.dart'; import 'package:photos/utils/crypto_util.dart'; import 'package:photos/utils/file_util.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; @@ -155,7 +155,8 @@ Future _decorateEnteFileData(ente.File file, AssetEntity asset) async { if (file.location == null || (file.location!.latitude == 0 && file.location!.longitude == 0)) { final latLong = await asset.latlngAsync(); - file.location = Location(latLong.latitude, latLong.longitude); + file.location = + Location(latitude: latLong.latitude, longitude: latLong.longitude); } if (file.title == null || file.title!.isEmpty) { diff --git a/lib/utils/lat_lon_util.dart b/lib/utils/lat_lon_util.dart new file mode 100644 index 000000000..a270e3902 --- /dev/null +++ b/lib/utils/lat_lon_util.dart @@ -0,0 +1,14 @@ +String convertLatLng(double decimal, bool isLat) { + final degree = "${decimal.toString().split(".")[0]}°"; + final minutesBeforeConversion = + double.parse("0.${decimal.toString().split(".")[1]}"); + final minutes = "${(minutesBeforeConversion * 60).toString().split('.')[0]}'"; + final secondsBeforeConversion = double.parse( + "0.${(minutesBeforeConversion * 60).toString().split('.')[1]}", + ); + final seconds = + '${double.parse((secondsBeforeConversion * 60).toString()).toStringAsFixed(0)}" '; + final dmsOutput = + "$degree$minutes$seconds${isLat ? decimal > 0 ? 'N' : 'S' : decimal > 0 ? 'E' : 'W'}"; + return dmsOutput; +} diff --git a/pubspec.lock b/pubspec.lock index 083bfd1a7..71935b381 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -97,6 +97,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + url: "https://pub.dev" + source: hosted + version: "2.3.3" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + url: "https://pub.dev" + source: hosted + version: "7.2.7" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" + url: "https://pub.dev" + source: hosted + version: "8.4.4" cached_network_image: dependency: "direct main" description: @@ -169,6 +233,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + url: "https://pub.dev" + source: hosted + version: "2.0.2" chewie: dependency: "direct main" description: @@ -184,6 +256,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + url: "https://pub.dev" + source: hosted + version: "4.4.0" collection: dependency: "direct main" description: @@ -272,6 +352,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "5be16bf1707658e4c03078d4a9b90208ded217fb02c163e207d334082412f2fb" + url: "https://pub.dev" + source: hosted + version: "2.2.5" dbus: dependency: transitive description: @@ -472,6 +560,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.12" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" fk_user_agent: dependency: "direct main" description: @@ -756,6 +852,22 @@ packages: url: "https://pub.dev" source: hosted version: "8.1.3" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: e819441678f1679b719008ff2ff0ef045d66eed9f9ec81166ca0d9b02a187454 + url: "https://pub.dev" + source: hosted + version: "2.3.2" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338 + url: "https://pub.dev" + source: hosted + version: "2.2.0" frontend_server_client: dependency: transitive description: @@ -780,6 +892,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.6" + graphs: + dependency: transitive + description: + name: graphs + sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + url: "https://pub.dev" + source: hosted + version: "2.2.0" hex: dependency: transitive description: @@ -917,13 +1037,21 @@ packages: source: hosted version: "0.6.5" json_annotation: - dependency: transitive + dependency: "direct main" description: name: json_annotation sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 url: "https://pub.dev" source: hosted version: "4.8.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: dadc08bd61f72559f938dd08ec20dbfec6c709bba83515085ea943d2078d187a + url: "https://pub.dev" + source: hosted + version: "6.6.1" like_button: dependency: "direct main" description: @@ -1349,6 +1477,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c + url: "https://pub.dev" + source: hosted + version: "1.2.2" quiver: dependency: "direct main" description: @@ -1538,6 +1674,22 @@ packages: description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: c2bea18c95cfa0276a366270afaa2850b09b4a76db95d546f3d003dcc7011298 + url: "https://pub.dev" + source: hosted + version: "1.2.7" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" + url: "https://pub.dev" + source: hosted + version: "1.3.3" source_map_stack_trace: dependency: transitive description: @@ -1723,6 +1875,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" tuple: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 5b823747b..24da2fd1b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.7.39+439 +version: 0.7.40+440 environment: sdk: '>=2.17.0 <3.0.0' @@ -68,12 +68,14 @@ dependencies: flutter_sodium: ^0.2.0 flutter_typeahead: ^4.0.0 fluttertoast: ^8.0.6 + freezed_annotation: ^2.2.0 google_nav_bar: ^5.0.5 http: ^0.13.4 image: ^3.0.2 image_editor: ^1.3.0 in_app_purchase: ^3.0.7 intl: ^0.17.0 + json_annotation: ^4.8.0 like_button: ^2.0.2 loading_animations: ^2.1.0 local_auth: ^2.1.5 @@ -134,9 +136,12 @@ dependency_overrides: wakelock: ^0.6.1+2 dev_dependencies: + build_runner: ^2.3.3 flutter_lints: ^2.0.1 flutter_test: sdk: flutter + freezed: ^2.3.2 + json_serializable: ^6.6.1 test: flutter_icons: